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