blob: fbe27e8134214a42e0e2de2b80853c5915cb0018 [file] [log] [blame]
Michael Layzell53fc31a2017-06-07 09:21:53 -04001#![cfg(all(feature = "full", feature = "fold"))]
Nika Layzella2a1a4a2017-11-19 11:33:17 -05002#![feature(rustc_private)]
3
Michael Layzell53fc31a2017-06-07 09:21:53 -04004//! The tests in this module do the following:
5//!
David Tolnaycfa5cc02017-11-13 01:05:11 -08006//! 1. Parse a given expression in both `syn` and `libsyntax`.
Michael Layzell53fc31a2017-06-07 09:21:53 -04007//! 2. Fold over the expression adding brackets around each subexpression (with
David Tolnaycfa5cc02017-11-13 01:05:11 -08008//! some complications - see the `syn_brackets` and `libsyntax_brackets`
Michael Layzell53fc31a2017-06-07 09:21:53 -04009//! methods).
10//! 3. Serialize the `syn` expression back into a string, and re-parse it with
David Tolnaycfa5cc02017-11-13 01:05:11 -080011//! `libsyntax`.
Michael Layzell53fc31a2017-06-07 09:21:53 -040012//! 4. Respan all of the expressions, replacing the spans with the default spans.
13//! 5. Compare the expressions with one another, if they are not equal fail.
14
15#[macro_use]
16extern crate quote;
David Tolnayee97dbf2017-11-19 14:24:38 -080017extern crate rayon;
Michael Layzell53fc31a2017-06-07 09:21:53 -040018extern crate syn;
David Tolnaycfa5cc02017-11-13 01:05:11 -080019extern crate syntax;
Michael Layzell53fc31a2017-06-07 09:21:53 -040020extern crate walkdir;
Michael Layzell53fc31a2017-06-07 09:21:53 -040021
David Tolnay51382052017-12-27 13:46:21 -050022use rayon::iter::{IntoParallelIterator, ParallelIterator};
David Tolnaycfa5cc02017-11-13 01:05:11 -080023use syntax::ast;
24use syntax::ptr::P;
David Tolnay51382052017-12-27 13:46:21 -050025use walkdir::{DirEntry, WalkDir, WalkDirIterator};
David Tolnayee97dbf2017-11-19 14:24:38 -080026
27use std::fs::File;
28use std::io::Read;
David Tolnay3eaf7d82017-12-17 23:14:52 -080029use std::process;
David Tolnayee97dbf2017-11-19 14:24:38 -080030use std::sync::atomic::{AtomicUsize, Ordering};
Michael Layzell53fc31a2017-06-07 09:21:53 -040031
David Tolnay51382052017-12-27 13:46:21 -050032use common::{parse, respan};
Michael Layzell53fc31a2017-06-07 09:21:53 -040033
34#[allow(dead_code)]
35#[macro_use]
36mod common;
37
38/// Test some pre-set expressions chosen by us.
39#[test]
40fn test_simple_precedence() {
41 const EXPRS: &[&str] = &[
42 "1 + 2 * 3 + 4",
43 "1 + 2 * ( 3 + 4 )",
44 "{ for i in r { } *some_ptr += 1; }",
45 "{ loop { break 5; } }",
46 "{ if true { () }.mthd() }",
Nika Layzell3aa0dc72017-12-04 13:41:28 -050047 "{ for i in unsafe { 20 } { } }",
Michael Layzell53fc31a2017-06-07 09:21:53 -040048 ];
49
50 let mut failed = 0;
51
52 for input in EXPRS {
53 let expr = if let Some(expr) = parse::syn_expr(input) {
54 expr
55 } else {
56 failed += 1;
57 continue;
58 };
59
60 let pf = match test_expressions(vec![expr]) {
61 (1, 0) => "passed",
62 (0, 1) => {
63 failed += 1;
64 "failed"
65 }
66 _ => unreachable!(),
67 };
68 errorf!("=== {}: {}\n", input, pf);
69 }
70
71 if failed > 0 {
72 panic!("Failed {} tests", failed);
73 }
74}
75
76/// Test expressions from rustc, like in `test_round_trip`.
77#[test]
78fn test_rustc_precedence() {
Michael Layzell53fc31a2017-06-07 09:21:53 -040079 common::check_min_stack();
Alex Crichton86374772017-07-07 20:39:28 -070080 common::clone_rust();
Michael Layzell53fc31a2017-06-07 09:21:53 -040081 let abort_after = common::abort_after();
82 if abort_after == 0 {
83 panic!("Skipping all precedence tests");
84 }
85
David Tolnayee97dbf2017-11-19 14:24:38 -080086 let passed = AtomicUsize::new(0);
87 let failed = AtomicUsize::new(0);
Michael Layzell53fc31a2017-06-07 09:21:53 -040088
David Tolnayee97dbf2017-11-19 14:24:38 -080089 WalkDir::new("tests/rust")
90 .sort_by(|a, b| a.cmp(b))
91 .into_iter()
92 .filter_entry(common::base_dir_filter)
93 .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
94 .unwrap()
95 .into_par_iter()
David Tolnay51382052017-12-27 13:46:21 -050096 .for_each(|entry| {
97 let path = entry.path();
98 if path.is_dir() {
99 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400100 }
David Tolnay51382052017-12-27 13:46:21 -0500101
102 // Our version of `libsyntax` can't parse this tests
103 if path.to_str()
104 .unwrap()
105 .ends_with("optional_comma_in_match_arm.rs")
106 {
107 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400108 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400109
David Tolnay51382052017-12-27 13:46:21 -0500110 let mut file = File::open(path).unwrap();
111 let mut content = String::new();
112 file.read_to_string(&mut content).unwrap();
Michael Layzell53fc31a2017-06-07 09:21:53 -0400113
David Tolnay51382052017-12-27 13:46:21 -0500114 let (l_passed, l_failed) = match syn::parse_file(&content) {
115 Ok(file) => {
116 let exprs = collect_exprs(file);
117 test_expressions(exprs)
118 }
119 Err(msg) => {
120 errorf!("syn failed to parse\n{:?}\n", msg);
121 (0, 1)
122 }
123 };
David Tolnayee97dbf2017-11-19 14:24:38 -0800124
David Tolnay51382052017-12-27 13:46:21 -0500125 errorf!(
126 "=== {}: {} passed | {} failed\n",
127 path.display(),
128 l_passed,
129 l_failed
130 );
131
132 passed.fetch_add(l_passed, Ordering::SeqCst);
133 let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);
134
135 if prev_failed + l_failed >= abort_after {
136 process::exit(1);
137 }
138 });
David Tolnayee97dbf2017-11-19 14:24:38 -0800139
140 let passed = passed.load(Ordering::SeqCst);
141 let failed = failed.load(Ordering::SeqCst);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400142
143 errorf!("\n===== Precedence Test Results =====\n");
144 errorf!("{} passed | {} failed\n", passed, failed);
145
Michael Layzell53fc31a2017-06-07 09:21:53 -0400146 if failed > 0 {
147 panic!("{} failures", failed);
148 }
149}
150
David Tolnayee97dbf2017-11-19 14:24:38 -0800151fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400152 let mut passed = 0;
153 let mut failed = 0;
154
155 for expr in exprs {
156 let raw = quote!(#expr).to_string();
157
David Tolnaycfa5cc02017-11-13 01:05:11 -0800158 let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400159 e
160 } else {
161 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800162 errorf!("\nFAIL - libsyntax failed to parse raw\n");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400163 continue;
164 };
165
166 let syn_expr = syn_brackets(expr);
David Tolnaycfa5cc02017-11-13 01:05:11 -0800167 let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400168 e
169 } else {
170 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800171 errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400172 continue;
173 };
174
175 let syn_ast = respan::respan_expr(syn_ast);
David Tolnaycfa5cc02017-11-13 01:05:11 -0800176 let libsyntax_ast = respan::respan_expr(libsyntax_ast);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400177
David Tolnaycfa5cc02017-11-13 01:05:11 -0800178 if syn_ast == libsyntax_ast {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400179 passed += 1;
180 } else {
181 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800182 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400183 }
184 }
185
186 (passed, failed)
187}
188
David Tolnaycfa5cc02017-11-13 01:05:11 -0800189fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
David Tolnay3cede942017-12-26 12:29:24 -0500190 parse::libsyntax_expr(input).and_then(libsyntax_brackets)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400191}
192
193/// Wrap every expression which is not already wrapped in parens with parens, to
194/// reveal the precidence of the parsed expressions, and produce a stringified form
195/// of the resulting expression.
196///
David Tolnaycfa5cc02017-11-13 01:05:11 -0800197/// This method operates on libsyntax objects.
198fn libsyntax_brackets(libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
David Tolnay51382052017-12-27 13:46:21 -0500199 use syntax::ast::{Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
David Tolnaycfa5cc02017-11-13 01:05:11 -0800200 use syntax::fold::{self, Folder};
201 use syntax::util::ThinVec;
202 use syntax::util::small_vector::SmallVector;
203 use syntax::ext::quote::rt::DUMMY_SP;
204 use syntax::codemap;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400205
206 fn expr(node: ExprKind) -> P<Expr> {
207 P(Expr {
208 id: ast::DUMMY_NODE_ID,
209 node: node,
210 span: DUMMY_SP,
211 attrs: ThinVec::new(),
212 })
213 }
214
215 struct BracketsFolder {
216 failed: bool,
217 };
218 impl Folder for BracketsFolder {
219 fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
David Tolnay51382052017-12-27 13:46:21 -0500220 e.map(|e| Expr {
221 node: match e.node {
222 ExprKind::Paren(inner) => {
223 ExprKind::Paren(inner.map(|e| fold::noop_fold_expr(e, self)))
224 }
225 ExprKind::If(..) | ExprKind::Block(..) | ExprKind::IfLet(..) => {
226 return fold::noop_fold_expr(e, self);
227 }
228 node => ExprKind::Paren(expr(node).map(|e| fold::noop_fold_expr(e, self))),
229 },
230 ..e
Michael Layzell53fc31a2017-06-07 09:21:53 -0400231 })
232 }
233
234 fn fold_field(&mut self, f: Field) -> Field {
235 Field {
236 ident: codemap::respan(f.ident.span, self.fold_ident(f.ident.node)),
237 expr: if f.is_shorthand {
238 f.expr.map(|e| fold::noop_fold_expr(e, self))
239 } else {
240 self.fold_expr(f.expr)
241 },
242 span: self.new_span(f.span),
243 is_shorthand: f.is_shorthand,
244 attrs: fold::fold_thin_attrs(f.attrs, self),
245 }
246 }
247
248 // We don't want to look at expressions that might appear in patterns or
249 // types yet. We'll look into comparing those in the future. For now
250 // focus on expressions appearing in other places.
251 fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
252 pat
253 }
254
255 fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
256 ty
257 }
258
259 fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
260 let node = match stmt.node {
261 // Don't wrap toplevel expressions in statements.
David Tolnay51382052017-12-27 13:46:21 -0500262 StmtKind::Expr(e) => StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self))),
263 StmtKind::Semi(e) => StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self))),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400264 s => s,
265 };
266
David Tolnay51382052017-12-27 13:46:21 -0500267 SmallVector::one(Stmt { node: node, ..stmt })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400268 }
269
270 fn fold_mac(&mut self, mac: Mac) -> Mac {
David Tolnaycfa5cc02017-11-13 01:05:11 -0800271 // By default when folding over macros, libsyntax panics. This is
Michael Layzell53fc31a2017-06-07 09:21:53 -0400272 // because it's usually not what you want, you want to run after
273 // macro expansion. We do want to do that (syn doesn't do macro
274 // expansion), so we implement fold_mac to just return the macro
275 // unchanged.
276 mac
277 }
278 }
279
David Tolnay51382052017-12-27 13:46:21 -0500280 let mut folder = BracketsFolder { failed: false };
David Tolnaycfa5cc02017-11-13 01:05:11 -0800281 let e = folder.fold_expr(libsyntax_expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400282 if folder.failed {
283 None
284 } else {
285 Some(e)
286 }
287}
288
289/// Wrap every expression which is not already wrapped in parens with parens, to
290/// reveal the precidence of the parsed expressions, and produce a stringified form
291/// of the resulting expression.
292fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
293 use syn::*;
294 use syn::fold::*;
295
David Tolnay8c91b882017-12-28 23:04:32 -0500296 fn paren(folder: &mut BracketsFolder, mut node: Expr) -> Expr {
David Tolnay2ae520a2017-12-29 11:19:50 -0500297 let attrs = node.replace_attrs(Vec::new());
David Tolnay8c91b882017-12-28 23:04:32 -0500298 Expr::Paren(ExprParen {
299 attrs: attrs,
David Tolnay51382052017-12-27 13:46:21 -0500300 expr: Box::new(fold_expr(
301 folder,
David Tolnay8c91b882017-12-28 23:04:32 -0500302 node,
David Tolnay51382052017-12-27 13:46:21 -0500303 )),
David Tolnay42eaae12017-12-26 23:05:18 -0500304 paren_token: token::Paren::default(),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400305 })
306 }
307
308 struct BracketsFolder;
309 impl Folder for BracketsFolder {
310 fn fold_expr(&mut self, expr: Expr) -> Expr {
David Tolnay8c91b882017-12-28 23:04:32 -0500311 match expr {
312 Expr::Group(_) => unreachable!(),
313 Expr::Paren(p) => paren(self, *p.expr),
314 Expr::If(..)
315 | Expr::Unsafe(..)
316 | Expr::Block(..)
317 | Expr::IfLet(..) => {
Nika Layzella6f46c42017-10-26 15:26:16 -0400318 return fold_expr(self, expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400319 }
320 node => paren(self, node),
David Tolnay8c91b882017-12-28 23:04:32 -0500321 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400322 }
323
324 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
325 match stmt {
326 // Don't wrap toplevel expressions in statements.
David Tolnay51382052017-12-27 13:46:21 -0500327 Stmt::Expr(e) => Stmt::Expr(Box::new(fold_expr(self, *e))),
328 Stmt::Semi(e, semi) => Stmt::Semi(Box::new(fold_expr(self, *e)), semi),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400329 s => s,
330 }
331 }
332
333 // We don't want to look at expressions that might appear in patterns or
334 // types yet. We'll look into comparing those in the future. For now
335 // focus on expressions appearing in other places.
336 fn fold_pat(&mut self, pat: Pat) -> Pat {
337 pat
338 }
339
David Tolnayfd6bf5c2017-11-12 09:41:14 -0800340 fn fold_type(&mut self, ty: Type) -> Type {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400341 ty
342 }
343 }
344
345 let mut folder = BracketsFolder;
346 folder.fold_expr(syn_expr)
347}
348
349/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700350fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400351 use syn::*;
352 use syn::fold::*;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500353 use syn::delimited::Delimited;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400354
355 struct CollectExprsFolder(Vec<Expr>);
356 impl Folder for CollectExprsFolder {
357 fn fold_expr(&mut self, expr: Expr) -> Expr {
358 self.0.push(expr);
359
David Tolnay8c91b882017-12-28 23:04:32 -0500360 Expr::Tuple(ExprTuple {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400361 attrs: vec![],
David Tolnay2a86fdd2017-12-28 23:34:28 -0500362 elems: Delimited::new(),
David Tolnay8c91b882017-12-28 23:04:32 -0500363 paren_token: token::Paren::default(),
364 })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400365 }
366 }
367
368 let mut folder = CollectExprsFolder(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700369 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400370 folder.0
371}