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("e!(#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