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