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]
test_simple_precedence()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]
test_rustc_precedence()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 
test_expressions(edition: Edition, exprs: Vec<syn::Expr>) -> (usize, usize)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 
librustc_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>>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 precidence of the parsed expressions, and produce a stringified
200 /// form of the resulting expression.
201 ///
202 /// This method operates on librustc objects.
librustc_brackets(mut librustc_expr: P<ast::Expr>) -> Option<P<ast::Expr>>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.
syn_brackets(syn_expr: syn::Expr) -> syn::Expr349 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.
collect_exprs(file: syn::File) -> Vec<syn::Expr>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};
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 
447     let mut folder = CollectExprs(vec![]);
448     folder.fold_file(file);
449     folder.0
450 }
451