1 | #![allow ( |
2 | clippy::assertions_on_result_states, |
3 | clippy::non_ascii_literal, |
4 | clippy::uninlined_format_args |
5 | )] |
6 | |
7 | #[macro_use ] |
8 | mod macros; |
9 | |
10 | use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; |
11 | use quote::{quote, ToTokens as _}; |
12 | use syn::parse::Parser as _; |
13 | use syn::{Block, Stmt}; |
14 | |
15 | #[test] |
16 | fn test_raw_operator() { |
17 | let stmt = syn::parse_str::<Stmt>("let _ = &raw const x;" ).unwrap(); |
18 | |
19 | snapshot!(stmt, @r###" |
20 | Stmt::Local { |
21 | pat: Pat::Wild, |
22 | init: Some(LocalInit { |
23 | expr: Expr::Verbatim(`& raw const x`), |
24 | }), |
25 | } |
26 | "### ); |
27 | } |
28 | |
29 | #[test] |
30 | fn test_raw_variable() { |
31 | let stmt = syn::parse_str::<Stmt>("let _ = &raw;" ).unwrap(); |
32 | |
33 | snapshot!(stmt, @r###" |
34 | Stmt::Local { |
35 | pat: Pat::Wild, |
36 | init: Some(LocalInit { |
37 | expr: Expr::Reference { |
38 | expr: Expr::Path { |
39 | path: Path { |
40 | segments: [ |
41 | PathSegment { |
42 | ident: "raw", |
43 | }, |
44 | ], |
45 | }, |
46 | }, |
47 | }, |
48 | }), |
49 | } |
50 | "### ); |
51 | } |
52 | |
53 | #[test] |
54 | fn test_raw_invalid() { |
55 | assert!(syn::parse_str::<Stmt>("let _ = &raw x;" ).is_err()); |
56 | } |
57 | |
58 | #[test] |
59 | fn test_none_group() { |
60 | // <Ø async fn f() {} Ø> |
61 | let tokens = TokenStream::from_iter(vec![TokenTree::Group(Group::new( |
62 | Delimiter::None, |
63 | TokenStream::from_iter(vec![ |
64 | TokenTree::Ident(Ident::new("async" , Span::call_site())), |
65 | TokenTree::Ident(Ident::new("fn" , Span::call_site())), |
66 | TokenTree::Ident(Ident::new("f" , Span::call_site())), |
67 | TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())), |
68 | TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())), |
69 | ]), |
70 | ))]); |
71 | snapshot!(tokens as Stmt, @r###" |
72 | Stmt::Item(Item::Fn { |
73 | vis: Visibility::Inherited, |
74 | sig: Signature { |
75 | asyncness: Some, |
76 | ident: "f", |
77 | generics: Generics, |
78 | output: ReturnType::Default, |
79 | }, |
80 | block: Block { |
81 | stmts: [], |
82 | }, |
83 | }) |
84 | "### ); |
85 | |
86 | let tokens = Group::new(Delimiter::None, quote!(let None = None)).to_token_stream(); |
87 | let stmts = Block::parse_within.parse2(tokens).unwrap(); |
88 | snapshot!(stmts, @r###" |
89 | [ |
90 | Stmt::Expr( |
91 | Expr::Group { |
92 | expr: Expr::Let { |
93 | pat: Pat::Ident { |
94 | ident: "None", |
95 | }, |
96 | expr: Expr::Path { |
97 | path: Path { |
98 | segments: [ |
99 | PathSegment { |
100 | ident: "None", |
101 | }, |
102 | ], |
103 | }, |
104 | }, |
105 | }, |
106 | }, |
107 | None, |
108 | ), |
109 | ] |
110 | "### ); |
111 | } |
112 | |
113 | #[test] |
114 | fn test_let_dot_dot() { |
115 | let tokens = quote! { |
116 | let .. = 10; |
117 | }; |
118 | |
119 | snapshot!(tokens as Stmt, @r###" |
120 | Stmt::Local { |
121 | pat: Pat::Rest, |
122 | init: Some(LocalInit { |
123 | expr: Expr::Lit { |
124 | lit: 10, |
125 | }, |
126 | }), |
127 | } |
128 | "### ); |
129 | } |
130 | |
131 | #[test] |
132 | fn test_let_else() { |
133 | let tokens = quote! { |
134 | let Some(x) = None else { return 0; }; |
135 | }; |
136 | |
137 | snapshot!(tokens as Stmt, @r###" |
138 | Stmt::Local { |
139 | pat: Pat::TupleStruct { |
140 | path: Path { |
141 | segments: [ |
142 | PathSegment { |
143 | ident: "Some", |
144 | }, |
145 | ], |
146 | }, |
147 | elems: [ |
148 | Pat::Ident { |
149 | ident: "x", |
150 | }, |
151 | ], |
152 | }, |
153 | init: Some(LocalInit { |
154 | expr: Expr::Path { |
155 | path: Path { |
156 | segments: [ |
157 | PathSegment { |
158 | ident: "None", |
159 | }, |
160 | ], |
161 | }, |
162 | }, |
163 | diverge: Some(Expr::Block { |
164 | block: Block { |
165 | stmts: [ |
166 | Stmt::Expr( |
167 | Expr::Return { |
168 | expr: Some(Expr::Lit { |
169 | lit: 0, |
170 | }), |
171 | }, |
172 | Some, |
173 | ), |
174 | ], |
175 | }, |
176 | }), |
177 | }), |
178 | } |
179 | "### ); |
180 | } |
181 | |
182 | #[test] |
183 | fn test_macros() { |
184 | let tokens = quote! { |
185 | fn main() { |
186 | macro_rules! mac {} |
187 | thread_local! { static FOO } |
188 | println!("" ); |
189 | vec![] |
190 | } |
191 | }; |
192 | |
193 | snapshot!(tokens as Stmt, @r###" |
194 | Stmt::Item(Item::Fn { |
195 | vis: Visibility::Inherited, |
196 | sig: Signature { |
197 | ident: "main", |
198 | generics: Generics, |
199 | output: ReturnType::Default, |
200 | }, |
201 | block: Block { |
202 | stmts: [ |
203 | Stmt::Item(Item::Macro { |
204 | ident: Some("mac"), |
205 | mac: Macro { |
206 | path: Path { |
207 | segments: [ |
208 | PathSegment { |
209 | ident: "macro_rules", |
210 | }, |
211 | ], |
212 | }, |
213 | delimiter: MacroDelimiter::Brace, |
214 | tokens: TokenStream(``), |
215 | }, |
216 | }), |
217 | Stmt::Macro { |
218 | mac: Macro { |
219 | path: Path { |
220 | segments: [ |
221 | PathSegment { |
222 | ident: "thread_local", |
223 | }, |
224 | ], |
225 | }, |
226 | delimiter: MacroDelimiter::Brace, |
227 | tokens: TokenStream(`static FOO`), |
228 | }, |
229 | }, |
230 | Stmt::Macro { |
231 | mac: Macro { |
232 | path: Path { |
233 | segments: [ |
234 | PathSegment { |
235 | ident: "println", |
236 | }, |
237 | ], |
238 | }, |
239 | delimiter: MacroDelimiter::Paren, |
240 | tokens: TokenStream(`""`), |
241 | }, |
242 | semi_token: Some, |
243 | }, |
244 | Stmt::Expr( |
245 | Expr::Macro { |
246 | mac: Macro { |
247 | path: Path { |
248 | segments: [ |
249 | PathSegment { |
250 | ident: "vec", |
251 | }, |
252 | ], |
253 | }, |
254 | delimiter: MacroDelimiter::Bracket, |
255 | tokens: TokenStream(``), |
256 | }, |
257 | }, |
258 | None, |
259 | ), |
260 | ], |
261 | }, |
262 | }) |
263 | "### ); |
264 | } |
265 | |
266 | #[test] |
267 | fn test_early_parse_loop() { |
268 | // The following is an Expr::Loop followed by Expr::Tuple. It is not an |
269 | // Expr::Call. |
270 | let tokens = quote! { |
271 | loop {} |
272 | () |
273 | }; |
274 | |
275 | let stmts = Block::parse_within.parse2(tokens).unwrap(); |
276 | |
277 | snapshot!(stmts, @r###" |
278 | [ |
279 | Stmt::Expr( |
280 | Expr::Loop { |
281 | body: Block { |
282 | stmts: [], |
283 | }, |
284 | }, |
285 | None, |
286 | ), |
287 | Stmt::Expr( |
288 | Expr::Tuple, |
289 | None, |
290 | ), |
291 | ] |
292 | "### ); |
293 | |
294 | let tokens = quote! { |
295 | 'a: loop {} |
296 | () |
297 | }; |
298 | |
299 | let stmts = Block::parse_within.parse2(tokens).unwrap(); |
300 | |
301 | snapshot!(stmts, @r###" |
302 | [ |
303 | Stmt::Expr( |
304 | Expr::Loop { |
305 | label: Some(Label { |
306 | name: Lifetime { |
307 | ident: "a", |
308 | }, |
309 | }), |
310 | body: Block { |
311 | stmts: [], |
312 | }, |
313 | }, |
314 | None, |
315 | ), |
316 | Stmt::Expr( |
317 | Expr::Tuple, |
318 | None, |
319 | ), |
320 | ] |
321 | "### ); |
322 | } |
323 | |