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_data_structures;
18 extern crate rustc_span;
19 extern crate syntax;
20
21 mod features;
22
23 use quote::quote;
24 use rayon::iter::{IntoParallelIterator, ParallelIterator};
25 use regex::Regex;
26 use rustc_span::edition::Edition;
27 use syntax::ast;
28 use syntax::ptr::P;
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 syntax::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("e!(#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_data_structures::thin_vec::ThinVec;
212 use rustc_span::DUMMY_SP;
213 use std::mem;
214 use syntax::ast::{Block, Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
215 use syntax::mut_visit::MutVisitor;
216 use syntax::util::map_in_place::MapInPlace;
217
218 struct BracketsVisitor {
219 failed: bool,
220 };
221
222 fn flat_map_field<T: MutVisitor>(mut f: Field, vis: &mut T) -> Vec<Field> {
223 if f.is_shorthand {
224 noop_visit_expr(&mut f.expr, vis);
225 } else {
226 vis.visit_expr(&mut f.expr);
227 }
228 vec![f]
229 }
230
231 fn flat_map_stmt<T: MutVisitor>(stmt: Stmt, vis: &mut T) -> Vec<Stmt> {
232 let kind = match stmt.kind {
233 // Don't wrap toplevel expressions in statements.
234 StmtKind::Expr(mut e) => {
235 noop_visit_expr(&mut e, vis);
236 StmtKind::Expr(e)
237 }
238 StmtKind::Semi(mut e) => {
239 noop_visit_expr(&mut e, vis);
240 StmtKind::Semi(e)
241 }
242 s => s,
243 };
244
245 vec![Stmt { kind, ..stmt }]
246 }
247
248 fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) {
249 use syntax::mut_visit::{noop_visit_expr, visit_opt, visit_thin_attrs};
250 match &mut e.kind {
251 ExprKind::Struct(path, fields, expr) => {
252 vis.visit_path(path);
253 fields.flat_map_in_place(|field| flat_map_field(field, vis));
254 visit_opt(expr, |expr| vis.visit_expr(expr));
255 vis.visit_id(&mut e.id);
256 vis.visit_span(&mut e.span);
257 visit_thin_attrs(&mut e.attrs, vis);
258 }
259 _ => noop_visit_expr(e, vis),
260 }
261 }
262
263 impl MutVisitor for BracketsVisitor {
264 fn visit_expr(&mut self, e: &mut P<Expr>) {
265 noop_visit_expr(e, self);
266 match e.kind {
267 ExprKind::If(..) | ExprKind::Block(..) | ExprKind::Let(..) => {}
268 _ => {
269 let inner = mem::replace(
270 e,
271 P(Expr {
272 id: ast::DUMMY_NODE_ID,
273 kind: ExprKind::Err,
274 span: DUMMY_SP,
275 attrs: ThinVec::new(),
276 }),
277 );
278 e.kind = ExprKind::Paren(inner);
279 }
280 }
281 }
282
283 fn visit_block(&mut self, block: &mut P<Block>) {
284 self.visit_id(&mut block.id);
285 block
286 .stmts
287 .flat_map_in_place(|stmt| flat_map_stmt(stmt, self));
288 self.visit_span(&mut block.span);
289 }
290
291 // We don't want to look at expressions that might appear in patterns or
292 // types yet. We'll look into comparing those in the future. For now
293 // focus on expressions appearing in other places.
294 fn visit_pat(&mut self, pat: &mut P<Pat>) {
295 let _ = pat;
296 }
297
298 fn visit_ty(&mut self, ty: &mut P<Ty>) {
299 let _ = ty;
300 }
301
302 fn visit_mac(&mut self, mac: &mut Mac) {
303 // By default when folding over macros, libsyntax panics. This is
304 // because it's usually not what you want, you want to run after
305 // macro expansion. We do want to do that (syn doesn't do macro
306 // expansion), so we implement visit_mac to just return the macro
307 // unchanged.
308 let _ = mac;
309 }
310 }
311
312 let mut folder = BracketsVisitor { failed: false };
313 folder.visit_expr(&mut libsyntax_expr);
314 if folder.failed {
315 None
316 } else {
317 Some(libsyntax_expr)
318 }
319 }
320
321 /// Wrap every expression which is not already wrapped in parens with parens, to
322 /// reveal the precedence of the parsed expressions, and produce a stringified
323 /// form of the resulting expression.
syn_brackets(syn_expr: syn::Expr) -> syn::Expr324 fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
325 use syn::fold::*;
326 use syn::*;
327
328 struct ParenthesizeEveryExpr;
329 impl Fold for ParenthesizeEveryExpr {
330 fn fold_expr(&mut self, expr: Expr) -> Expr {
331 match expr {
332 Expr::Group(_) => unreachable!(),
333 Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => {
334 fold_expr(self, expr)
335 }
336 _ => Expr::Paren(ExprParen {
337 attrs: Vec::new(),
338 expr: Box::new(fold_expr(self, expr)),
339 paren_token: token::Paren::default(),
340 }),
341 }
342 }
343
344 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
345 match stmt {
346 // Don't wrap toplevel expressions in statements.
347 Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
348 Stmt::Semi(e, semi) => Stmt::Semi(fold_expr(self, e), semi),
349 s => s,
350 }
351 }
352
353 // We don't want to look at expressions that might appear in patterns or
354 // types yet. We'll look into comparing those in the future. For now
355 // focus on expressions appearing in other places.
356 fn fold_pat(&mut self, pat: Pat) -> Pat {
357 pat
358 }
359
360 fn fold_type(&mut self, ty: Type) -> Type {
361 ty
362 }
363 }
364
365 let mut folder = ParenthesizeEveryExpr;
366 folder.fold_expr(syn_expr)
367 }
368
369 /// Walk through a crate collecting all expressions we can find in it.
collect_exprs(file: syn::File) -> Vec<syn::Expr>370 fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
371 use syn::fold::*;
372 use syn::punctuated::Punctuated;
373 use syn::*;
374
375 struct CollectExprs(Vec<Expr>);
376 impl Fold for CollectExprs {
377 fn fold_expr(&mut self, expr: Expr) -> Expr {
378 match expr {
379 Expr::Verbatim(tokens) if tokens.is_empty() => {}
380 _ => self.0.push(expr),
381 }
382
383 Expr::Tuple(ExprTuple {
384 attrs: vec![],
385 elems: Punctuated::new(),
386 paren_token: token::Paren::default(),
387 })
388 }
389 }
390
391 let mut folder = CollectExprs(vec![]);
392 folder.fold_file(file);
393 folder.0
394 }
395