1 #[macro_use]
2 mod macros;
3 
4 use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
5 use std::iter::FromIterator;
6 use syn::parse::{Parse, ParseStream};
7 use syn::{DeriveInput, Result, Visibility};
8 
9 #[derive(Debug)]
10 struct VisRest {
11     vis: Visibility,
12     rest: TokenStream,
13 }
14 
15 impl Parse for VisRest {
parse(input: ParseStream) -> Result<Self>16     fn parse(input: ParseStream) -> Result<Self> {
17         Ok(VisRest {
18             vis: input.parse()?,
19             rest: input.parse()?,
20         })
21     }
22 }
23 
24 macro_rules! assert_vis_parse {
25     ($input:expr, Ok($p:pat)) => {
26         assert_vis_parse!($input, Ok($p) + "");
27     };
28 
29     ($input:expr, Ok($p:pat) + $rest:expr) => {
30         let expected = $rest.parse::<TokenStream>().unwrap();
31         let parse: VisRest = syn::parse_str($input).unwrap();
32 
33         match parse.vis {
34             $p => {}
35             _ => panic!("Expected {}, got {:?}", stringify!($p), parse.vis),
36         }
37 
38         // NOTE: Round-trips through `to_string` to avoid potential whitespace
39         // diffs.
40         assert_eq!(parse.rest.to_string(), expected.to_string());
41     };
42 
43     ($input:expr, Err) => {
44         syn::parse2::<VisRest>($input.parse().unwrap()).unwrap_err();
45     };
46 }
47 
48 #[test]
test_pub()49 fn test_pub() {
50     assert_vis_parse!("pub", Ok(Visibility::Public(_)));
51 }
52 
53 #[test]
test_crate()54 fn test_crate() {
55     assert_vis_parse!("crate", Ok(Visibility::Crate(_)));
56 }
57 
58 #[test]
test_inherited()59 fn test_inherited() {
60     assert_vis_parse!("", Ok(Visibility::Inherited));
61 }
62 
63 #[test]
test_in()64 fn test_in() {
65     assert_vis_parse!("pub(in foo::bar)", Ok(Visibility::Restricted(_)));
66 }
67 
68 #[test]
test_pub_crate()69 fn test_pub_crate() {
70     assert_vis_parse!("pub(crate)", Ok(Visibility::Restricted(_)));
71 }
72 
73 #[test]
test_pub_self()74 fn test_pub_self() {
75     assert_vis_parse!("pub(self)", Ok(Visibility::Restricted(_)));
76 }
77 
78 #[test]
test_pub_super()79 fn test_pub_super() {
80     assert_vis_parse!("pub(super)", Ok(Visibility::Restricted(_)));
81 }
82 
83 #[test]
test_missing_in()84 fn test_missing_in() {
85     assert_vis_parse!("pub(foo::bar)", Ok(Visibility::Public(_)) + "(foo::bar)");
86 }
87 
88 #[test]
test_missing_in_path()89 fn test_missing_in_path() {
90     assert_vis_parse!("pub(in)", Err);
91 }
92 
93 #[test]
test_crate_path()94 fn test_crate_path() {
95     assert_vis_parse!(
96         "pub(crate::A, crate::B)",
97         Ok(Visibility::Public(_)) + "(crate::A, crate::B)"
98     );
99 }
100 
101 #[test]
test_junk_after_in()102 fn test_junk_after_in() {
103     assert_vis_parse!("pub(in some::path @@garbage)", Err);
104 }
105 
106 #[test]
test_empty_group_vis()107 fn test_empty_group_vis() {
108     // mimics `struct S { $vis $field: () }` where $vis is empty
109     let tokens = TokenStream::from_iter(vec![
110         TokenTree::Ident(Ident::new("struct", Span::call_site())),
111         TokenTree::Ident(Ident::new("S", Span::call_site())),
112         TokenTree::Group(Group::new(
113             Delimiter::Brace,
114             TokenStream::from_iter(vec![
115                 TokenTree::Group(Group::new(Delimiter::None, TokenStream::new())),
116                 TokenTree::Group(Group::new(
117                     Delimiter::None,
118                     TokenStream::from_iter(vec![TokenTree::Ident(Ident::new(
119                         "f",
120                         Span::call_site(),
121                     ))]),
122                 )),
123                 TokenTree::Punct(Punct::new(':', Spacing::Alone)),
124                 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
125             ]),
126         )),
127     ]);
128 
129     snapshot!(tokens as DeriveInput, @r###"
130     DeriveInput {
131         vis: Inherited,
132         ident: "S",
133         generics: Generics,
134         data: Data::Struct {
135             fields: Fields::Named {
136                 named: [
137                     Field {
138                         vis: Inherited,
139                         ident: Some("f"),
140                         colon_token: Some,
141                         ty: Type::Tuple,
142                     },
143                 ],
144             },
145         },
146     }
147     "###);
148 }
149