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