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::{Expr, ExprRange};
9
10 #[test]
test_expr_parse()11 fn test_expr_parse() {
12 let tokens = quote!(..100u32);
13 snapshot!(tokens as Expr, @r###"
14 Expr::Range {
15 limits: RangeLimits::HalfOpen,
16 end: Some(Expr::Lit {
17 lit: 100u32,
18 }),
19 }
20 "###);
21
22 let tokens = quote!(..100u32);
23 snapshot!(tokens as ExprRange, @r###"
24 ExprRange {
25 limits: RangeLimits::HalfOpen,
26 end: Some(Expr::Lit {
27 lit: 100u32,
28 }),
29 }
30 "###);
31 }
32
33 #[test]
test_await()34 fn test_await() {
35 // Must not parse as Expr::Field.
36 let tokens = quote!(fut.await);
37
38 snapshot!(tokens as Expr, @r###"
39 Expr::Await {
40 base: Expr::Path {
41 path: Path {
42 segments: [
43 PathSegment {
44 ident: "fut",
45 },
46 ],
47 },
48 },
49 }
50 "###);
51 }
52
53 #[rustfmt::skip]
54 #[test]
test_tuple_multi_index()55 fn test_tuple_multi_index() {
56 let expected = snapshot!("tuple.0.0" as Expr, @r###"
57 Expr::Field {
58 base: Expr::Field {
59 base: Expr::Path {
60 path: Path {
61 segments: [
62 PathSegment {
63 ident: "tuple",
64 },
65 ],
66 },
67 },
68 member: Member::Unnamed(Index {
69 index: 0,
70 }),
71 },
72 member: Member::Unnamed(Index {
73 index: 0,
74 }),
75 }
76 "###);
77
78 for &input in &[
79 "tuple .0.0",
80 "tuple. 0.0",
81 "tuple.0 .0",
82 "tuple.0. 0",
83 "tuple . 0 . 0",
84 ] {
85 assert_eq!(expected, syn::parse_str(input).unwrap());
86 }
87
88 for tokens in [
89 quote!(tuple.0.0),
90 quote!(tuple .0.0),
91 quote!(tuple. 0.0),
92 quote!(tuple.0 .0),
93 quote!(tuple.0. 0),
94 quote!(tuple . 0 . 0),
95 ] {
96 assert_eq!(expected, syn::parse2(tokens).unwrap());
97 }
98 }
99
100 #[test]
test_macro_variable_func()101 fn test_macro_variable_func() {
102 // mimics the token stream corresponding to `$fn()`
103 let tokens = TokenStream::from_iter(vec![
104 TokenTree::Group(Group::new(Delimiter::None, quote! { f })),
105 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
106 ]);
107
108 snapshot!(tokens as Expr, @r###"
109 Expr::Call {
110 func: Expr::Group {
111 expr: Expr::Path {
112 path: Path {
113 segments: [
114 PathSegment {
115 ident: "f",
116 },
117 ],
118 },
119 },
120 },
121 }
122 "###);
123
124 let tokens = TokenStream::from_iter(vec![
125 TokenTree::Punct(Punct::new('#', Spacing::Alone)),
126 TokenTree::Group(Group::new(Delimiter::Bracket, quote! { outside })),
127 TokenTree::Group(Group::new(Delimiter::None, quote! { #[inside] f })),
128 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
129 ]);
130
131 snapshot!(tokens as Expr, @r###"
132 Expr::Call {
133 attrs: [
134 Attribute {
135 style: AttrStyle::Outer,
136 meta: Meta::Path {
137 segments: [
138 PathSegment {
139 ident: "outside",
140 },
141 ],
142 },
143 },
144 ],
145 func: Expr::Group {
146 expr: Expr::Path {
147 attrs: [
148 Attribute {
149 style: AttrStyle::Outer,
150 meta: Meta::Path {
151 segments: [
152 PathSegment {
153 ident: "inside",
154 },
155 ],
156 },
157 },
158 ],
159 path: Path {
160 segments: [
161 PathSegment {
162 ident: "f",
163 },
164 ],
165 },
166 },
167 },
168 }
169 "###);
170 }
171
172 #[test]
test_macro_variable_macro()173 fn test_macro_variable_macro() {
174 // mimics the token stream corresponding to `$macro!()`
175 let tokens = TokenStream::from_iter(vec![
176 TokenTree::Group(Group::new(Delimiter::None, quote! { m })),
177 TokenTree::Punct(Punct::new('!', Spacing::Alone)),
178 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
179 ]);
180
181 snapshot!(tokens as Expr, @r###"
182 Expr::Macro {
183 mac: Macro {
184 path: Path {
185 segments: [
186 PathSegment {
187 ident: "m",
188 },
189 ],
190 },
191 delimiter: MacroDelimiter::Paren,
192 tokens: TokenStream(``),
193 },
194 }
195 "###);
196 }
197
198 #[test]
test_macro_variable_struct()199 fn test_macro_variable_struct() {
200 // mimics the token stream corresponding to `$struct {}`
201 let tokens = TokenStream::from_iter(vec![
202 TokenTree::Group(Group::new(Delimiter::None, quote! { S })),
203 TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())),
204 ]);
205
206 snapshot!(tokens as Expr, @r###"
207 Expr::Struct {
208 path: Path {
209 segments: [
210 PathSegment {
211 ident: "S",
212 },
213 ],
214 },
215 }
216 "###);
217 }
218
219 #[test]
test_macro_variable_match_arm()220 fn test_macro_variable_match_arm() {
221 // mimics the token stream corresponding to `match v { _ => $expr }`
222 let tokens = TokenStream::from_iter(vec![
223 TokenTree::Ident(Ident::new("match", Span::call_site())),
224 TokenTree::Ident(Ident::new("v", Span::call_site())),
225 TokenTree::Group(Group::new(
226 Delimiter::Brace,
227 TokenStream::from_iter(vec![
228 TokenTree::Punct(Punct::new('_', Spacing::Alone)),
229 TokenTree::Punct(Punct::new('=', Spacing::Joint)),
230 TokenTree::Punct(Punct::new('>', Spacing::Alone)),
231 TokenTree::Group(Group::new(Delimiter::None, quote! { #[a] () })),
232 ]),
233 )),
234 ]);
235
236 snapshot!(tokens as Expr, @r###"
237 Expr::Match {
238 expr: Expr::Path {
239 path: Path {
240 segments: [
241 PathSegment {
242 ident: "v",
243 },
244 ],
245 },
246 },
247 arms: [
248 Arm {
249 pat: Pat::Wild,
250 body: Expr::Group {
251 expr: Expr::Tuple {
252 attrs: [
253 Attribute {
254 style: AttrStyle::Outer,
255 meta: Meta::Path {
256 segments: [
257 PathSegment {
258 ident: "a",
259 },
260 ],
261 },
262 },
263 ],
264 },
265 },
266 },
267 ],
268 }
269 "###);
270 }
271
272 // https://github.com/dtolnay/syn/issues/1019
273 #[test]
test_closure_vs_rangefull()274 fn test_closure_vs_rangefull() {
275 #[rustfmt::skip] // rustfmt bug: https://github.com/rust-lang/rustfmt/issues/4808
276 let tokens = quote!(|| .. .method());
277 snapshot!(tokens as Expr, @r###"
278 Expr::MethodCall {
279 receiver: Expr::Closure {
280 output: ReturnType::Default,
281 body: Expr::Range {
282 limits: RangeLimits::HalfOpen,
283 },
284 },
285 method: "method",
286 }
287 "###);
288 }
289
290 #[test]
test_postfix_operator_after_cast()291 fn test_postfix_operator_after_cast() {
292 syn::parse_str::<Expr>("|| &x as T[0]").unwrap_err();
293 syn::parse_str::<Expr>("|| () as ()()").unwrap_err();
294 }
295
296 #[test]
test_ranges()297 fn test_ranges() {
298 syn::parse_str::<Expr>("..").unwrap();
299 syn::parse_str::<Expr>("..hi").unwrap();
300 syn::parse_str::<Expr>("lo..").unwrap();
301 syn::parse_str::<Expr>("lo..hi").unwrap();
302
303 syn::parse_str::<Expr>("..=").unwrap_err();
304 syn::parse_str::<Expr>("..=hi").unwrap();
305 syn::parse_str::<Expr>("lo..=").unwrap_err();
306 syn::parse_str::<Expr>("lo..=hi").unwrap();
307
308 syn::parse_str::<Expr>("...").unwrap_err();
309 syn::parse_str::<Expr>("...hi").unwrap_err();
310 syn::parse_str::<Expr>("lo...").unwrap_err();
311 syn::parse_str::<Expr>("lo...hi").unwrap_err();
312 }
313