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