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