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