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