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