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