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