]> git.proxmox.com Git - rustc.git/blob - vendor/syn/tests/test_precedence.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / vendor / syn / tests / test_precedence.rs
1 #![cfg(not(syn_disable_nightly_tests))]
2 #![cfg(not(miri))]
3 #![recursion_limit = "1024"]
4 #![feature(rustc_private)]
5 #![allow(
6 clippy::explicit_deref_methods,
7 clippy::manual_assert,
8 clippy::match_wildcard_for_single_variants,
9 clippy::too_many_lines
10 )]
11
12 //! The tests in this module do the following:
13 //!
14 //! 1. Parse a given expression in both `syn` and `librustc`.
15 //! 2. Fold over the expression adding brackets around each subexpression (with
16 //! some complications - see the `syn_brackets` and `librustc_brackets`
17 //! methods).
18 //! 3. Serialize the `syn` expression back into a string, and re-parse it with
19 //! `librustc`.
20 //! 4. Respan all of the expressions, replacing the spans with the default
21 //! spans.
22 //! 5. Compare the expressions with one another, if they are not equal fail.
23
24 extern crate rustc_ast;
25 extern crate rustc_data_structures;
26 extern crate rustc_span;
27
28 use crate::common::eq::SpanlessEq;
29 use crate::common::parse;
30 use quote::quote;
31 use rayon::iter::{IntoParallelIterator, ParallelIterator};
32 use regex::Regex;
33 use rustc_ast::ast;
34 use rustc_ast::ptr::P;
35 use rustc_span::edition::Edition;
36 use std::fs;
37 use std::process;
38 use std::sync::atomic::{AtomicUsize, Ordering};
39 use walkdir::{DirEntry, WalkDir};
40
41 #[macro_use]
42 mod macros;
43
44 #[allow(dead_code)]
45 mod common;
46
47 mod repo;
48
49 /// Test some pre-set expressions chosen by us.
50 #[test]
51 fn test_simple_precedence() {
52 const EXPRS: &[&str] = &[
53 "1 + 2 * 3 + 4",
54 "1 + 2 * ( 3 + 4 )",
55 "{ for i in r { } *some_ptr += 1; }",
56 "{ loop { break 5; } }",
57 "{ if true { () }.mthd() }",
58 "{ for i in unsafe { 20 } { } }",
59 ];
60
61 let mut failed = 0;
62
63 for input in EXPRS {
64 let expr = if let Some(expr) = parse::syn_expr(input) {
65 expr
66 } else {
67 failed += 1;
68 continue;
69 };
70
71 let pf = match test_expressions(Edition::Edition2018, vec![expr]) {
72 (1, 0) => "passed",
73 (0, 1) => {
74 failed += 1;
75 "failed"
76 }
77 _ => unreachable!(),
78 };
79 errorf!("=== {}: {}\n", input, pf);
80 }
81
82 if failed > 0 {
83 panic!("Failed {} tests", failed);
84 }
85 }
86
87 /// Test expressions from rustc, like in `test_round_trip`.
88 #[test]
89 fn test_rustc_precedence() {
90 common::rayon_init();
91 repo::clone_rust();
92 let abort_after = common::abort_after();
93 if abort_after == 0 {
94 panic!("Skipping all precedence tests");
95 }
96
97 let passed = AtomicUsize::new(0);
98 let failed = AtomicUsize::new(0);
99
100 // 2018 edition is hard
101 let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap();
102
103 WalkDir::new("tests/rust")
104 .sort_by(|a, b| a.file_name().cmp(b.file_name()))
105 .into_iter()
106 .filter_entry(repo::base_dir_filter)
107 .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
108 .unwrap()
109 .into_par_iter()
110 .for_each(|entry| {
111 let path = entry.path();
112 if path.is_dir() {
113 return;
114 }
115
116 let content = fs::read_to_string(path).unwrap();
117 let content = edition_regex.replace_all(&content, "_$0");
118
119 let (l_passed, l_failed) = match syn::parse_file(&content) {
120 Ok(file) => {
121 let edition = repo::edition(path).parse().unwrap();
122 let exprs = collect_exprs(file);
123 test_expressions(edition, exprs)
124 }
125 Err(msg) => {
126 errorf!("syn failed to parse\n{:?}\n", msg);
127 (0, 1)
128 }
129 };
130
131 errorf!(
132 "=== {}: {} passed | {} failed\n",
133 path.display(),
134 l_passed,
135 l_failed
136 );
137
138 passed.fetch_add(l_passed, Ordering::Relaxed);
139 let prev_failed = failed.fetch_add(l_failed, Ordering::Relaxed);
140
141 if prev_failed + l_failed >= abort_after {
142 process::exit(1);
143 }
144 });
145
146 let passed = passed.load(Ordering::Relaxed);
147 let failed = failed.load(Ordering::Relaxed);
148
149 errorf!("\n===== Precedence Test Results =====\n");
150 errorf!("{} passed | {} failed\n", passed, failed);
151
152 if failed > 0 {
153 panic!("{} failures", failed);
154 }
155 }
156
157 fn test_expressions(edition: Edition, exprs: Vec<syn::Expr>) -> (usize, usize) {
158 let mut passed = 0;
159 let mut failed = 0;
160
161 rustc_span::create_session_if_not_set_then(edition, |_| {
162 for expr in exprs {
163 let raw = quote!(#expr).to_string();
164
165 let librustc_ast = if let Some(e) = librustc_parse_and_rewrite(&raw) {
166 e
167 } else {
168 failed += 1;
169 errorf!("\nFAIL - librustc failed to parse raw\n");
170 continue;
171 };
172
173 let syn_expr = syn_brackets(expr);
174 let syn_ast = if let Some(e) = parse::librustc_expr(&quote!(#syn_expr).to_string()) {
175 e
176 } else {
177 failed += 1;
178 errorf!("\nFAIL - librustc failed to parse bracketed\n");
179 continue;
180 };
181
182 if SpanlessEq::eq(&syn_ast, &librustc_ast) {
183 passed += 1;
184 } else {
185 failed += 1;
186 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, librustc_ast);
187 }
188 }
189 });
190
191 (passed, failed)
192 }
193
194 fn librustc_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
195 parse::librustc_expr(input).and_then(librustc_brackets)
196 }
197
198 /// Wrap every expression which is not already wrapped in parens with parens, to
199 /// reveal the precedence of the parsed expressions, and produce a stringified
200 /// form of the resulting expression.
201 ///
202 /// This method operates on librustc objects.
203 fn librustc_brackets(mut librustc_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
204 use rustc_ast::ast::{
205 Attribute, Block, BorrowKind, Expr, ExprField, ExprKind, GenericArg, Local, LocalKind, Pat,
206 Stmt, StmtKind, StructExpr, StructRest, Ty,
207 };
208 use rustc_ast::mut_visit::{noop_visit_generic_arg, noop_visit_local, MutVisitor};
209 use rustc_data_structures::map_in_place::MapInPlace;
210 use rustc_data_structures::thin_vec::ThinVec;
211 use rustc_span::DUMMY_SP;
212 use std::mem;
213 use std::ops::DerefMut;
214
215 struct BracketsVisitor {
216 failed: bool,
217 }
218
219 fn flat_map_field<T: MutVisitor>(mut f: ExprField, vis: &mut T) -> Vec<ExprField> {
220 if f.is_shorthand {
221 noop_visit_expr(&mut f.expr, vis);
222 } else {
223 vis.visit_expr(&mut f.expr);
224 }
225 vec![f]
226 }
227
228 fn flat_map_stmt<T: MutVisitor>(stmt: Stmt, vis: &mut T) -> Vec<Stmt> {
229 let kind = match stmt.kind {
230 // Don't wrap toplevel expressions in statements.
231 StmtKind::Expr(mut e) => {
232 noop_visit_expr(&mut e, vis);
233 StmtKind::Expr(e)
234 }
235 StmtKind::Semi(mut e) => {
236 noop_visit_expr(&mut e, vis);
237 StmtKind::Semi(e)
238 }
239 s => s,
240 };
241
242 vec![Stmt { kind, ..stmt }]
243 }
244
245 fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) {
246 use rustc_ast::mut_visit::{noop_visit_expr, visit_thin_attrs};
247 match &mut e.kind {
248 ExprKind::AddrOf(BorrowKind::Raw, ..) => {}
249 ExprKind::Struct(expr) => {
250 let StructExpr {
251 qself,
252 path,
253 fields,
254 rest,
255 } = expr.deref_mut();
256 vis.visit_qself(qself);
257 vis.visit_path(path);
258 fields.flat_map_in_place(|field| flat_map_field(field, vis));
259 if let StructRest::Base(rest) = rest {
260 vis.visit_expr(rest);
261 }
262 vis.visit_id(&mut e.id);
263 vis.visit_span(&mut e.span);
264 visit_thin_attrs(&mut e.attrs, vis);
265 }
266 _ => noop_visit_expr(e, vis),
267 }
268 }
269
270 impl MutVisitor for BracketsVisitor {
271 fn visit_expr(&mut self, e: &mut P<Expr>) {
272 match e.kind {
273 ExprKind::ConstBlock(..) => {}
274 _ => noop_visit_expr(e, self),
275 }
276 match e.kind {
277 ExprKind::If(..) | ExprKind::Block(..) | ExprKind::Let(..) => {}
278 _ => {
279 let inner = mem::replace(
280 e,
281 P(Expr {
282 id: ast::DUMMY_NODE_ID,
283 kind: ExprKind::Err,
284 span: DUMMY_SP,
285 attrs: ThinVec::new(),
286 tokens: None,
287 }),
288 );
289 e.kind = ExprKind::Paren(inner);
290 }
291 }
292 }
293
294 fn visit_generic_arg(&mut self, arg: &mut GenericArg) {
295 match arg {
296 // Don't wrap unbraced const generic arg as that's invalid syntax.
297 GenericArg::Const(anon_const) => {
298 if let ExprKind::Block(..) = &mut anon_const.value.kind {
299 noop_visit_expr(&mut anon_const.value, self);
300 }
301 }
302 _ => noop_visit_generic_arg(arg, self),
303 }
304 }
305
306 fn visit_block(&mut self, block: &mut P<Block>) {
307 self.visit_id(&mut block.id);
308 block
309 .stmts
310 .flat_map_in_place(|stmt| flat_map_stmt(stmt, self));
311 self.visit_span(&mut block.span);
312 }
313
314 fn visit_local(&mut self, local: &mut P<Local>) {
315 match local.kind {
316 LocalKind::InitElse(..) => {}
317 _ => noop_visit_local(local, self),
318 }
319 }
320
321 // We don't want to look at expressions that might appear in patterns or
322 // types yet. We'll look into comparing those in the future. For now
323 // focus on expressions appearing in other places.
324 fn visit_pat(&mut self, pat: &mut P<Pat>) {
325 let _ = pat;
326 }
327
328 fn visit_ty(&mut self, ty: &mut P<Ty>) {
329 let _ = ty;
330 }
331
332 fn visit_attribute(&mut self, attr: &mut Attribute) {
333 let _ = attr;
334 }
335 }
336
337 let mut folder = BracketsVisitor { failed: false };
338 folder.visit_expr(&mut librustc_expr);
339 if folder.failed {
340 None
341 } else {
342 Some(librustc_expr)
343 }
344 }
345
346 /// Wrap every expression which is not already wrapped in parens with parens, to
347 /// reveal the precedence of the parsed expressions, and produce a stringified
348 /// form of the resulting expression.
349 fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
350 use syn::fold::{fold_expr, fold_generic_argument, fold_generic_method_argument, Fold};
351 use syn::{token, Expr, ExprParen, GenericArgument, GenericMethodArgument, Pat, Stmt, Type};
352
353 struct ParenthesizeEveryExpr;
354 impl Fold for ParenthesizeEveryExpr {
355 fn fold_expr(&mut self, expr: Expr) -> Expr {
356 match expr {
357 Expr::Group(_) => unreachable!(),
358 Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => {
359 fold_expr(self, expr)
360 }
361 _ => Expr::Paren(ExprParen {
362 attrs: Vec::new(),
363 expr: Box::new(fold_expr(self, expr)),
364 paren_token: token::Paren::default(),
365 }),
366 }
367 }
368
369 fn fold_generic_argument(&mut self, arg: GenericArgument) -> GenericArgument {
370 match arg {
371 GenericArgument::Const(arg) => GenericArgument::Const(match arg {
372 Expr::Block(_) => fold_expr(self, arg),
373 // Don't wrap unbraced const generic arg as that's invalid syntax.
374 _ => arg,
375 }),
376 _ => fold_generic_argument(self, arg),
377 }
378 }
379
380 fn fold_generic_method_argument(
381 &mut self,
382 arg: GenericMethodArgument,
383 ) -> GenericMethodArgument {
384 match arg {
385 GenericMethodArgument::Const(arg) => GenericMethodArgument::Const(match arg {
386 Expr::Block(_) => fold_expr(self, arg),
387 // Don't wrap unbraced const generic arg as that's invalid syntax.
388 _ => arg,
389 }),
390 _ => fold_generic_method_argument(self, arg),
391 }
392 }
393
394 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
395 match stmt {
396 // Don't wrap toplevel expressions in statements.
397 Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
398 Stmt::Semi(e, semi) => {
399 if let Expr::Verbatim(_) = e {
400 Stmt::Semi(e, semi)
401 } else {
402 Stmt::Semi(fold_expr(self, e), semi)
403 }
404 }
405 s => s,
406 }
407 }
408
409 // We don't want to look at expressions that might appear in patterns or
410 // types yet. We'll look into comparing those in the future. For now
411 // focus on expressions appearing in other places.
412 fn fold_pat(&mut self, pat: Pat) -> Pat {
413 pat
414 }
415
416 fn fold_type(&mut self, ty: Type) -> Type {
417 ty
418 }
419 }
420
421 let mut folder = ParenthesizeEveryExpr;
422 folder.fold_expr(syn_expr)
423 }
424
425 /// Walk through a crate collecting all expressions we can find in it.
426 fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
427 use syn::fold::Fold;
428 use syn::punctuated::Punctuated;
429 use syn::{token, Expr, ExprTuple, Path};
430
431 struct CollectExprs(Vec<Expr>);
432 impl Fold for CollectExprs {
433 fn fold_expr(&mut self, expr: Expr) -> Expr {
434 match expr {
435 Expr::Verbatim(_) => {}
436 _ => self.0.push(expr),
437 }
438
439 Expr::Tuple(ExprTuple {
440 attrs: vec![],
441 elems: Punctuated::new(),
442 paren_token: token::Paren::default(),
443 })
444 }
445
446 fn fold_path(&mut self, path: Path) -> Path {
447 // Skip traversing into const generic path arguments
448 path
449 }
450 }
451
452 let mut folder = CollectExprs(vec![]);
453 folder.fold_file(file);
454 folder.0
455 }