blob: de4172102f8e1cf51d1a91f3695dd40685632f03 [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
Michael Layzell53fc31a2017-06-07 09:21:53 -040034#[macro_use]
David Tolnaydd125562017-12-31 02:16:22 -050035mod macros;
36
37#[allow(dead_code)]
Michael Layzell53fc31a2017-06-07 09:21:53 -040038mod common;
39
40/// Test some pre-set expressions chosen by us.
41#[test]
42fn test_simple_precedence() {
43 const EXPRS: &[&str] = &[
44 "1 + 2 * 3 + 4",
45 "1 + 2 * ( 3 + 4 )",
46 "{ for i in r { } *some_ptr += 1; }",
47 "{ loop { break 5; } }",
48 "{ if true { () }.mthd() }",
Nika Layzell3aa0dc72017-12-04 13:41:28 -050049 "{ for i in unsafe { 20 } { } }",
Michael Layzell53fc31a2017-06-07 09:21:53 -040050 ];
51
52 let mut failed = 0;
53
54 for input in EXPRS {
55 let expr = if let Some(expr) = parse::syn_expr(input) {
56 expr
57 } else {
58 failed += 1;
59 continue;
60 };
61
62 let pf = match test_expressions(vec![expr]) {
63 (1, 0) => "passed",
64 (0, 1) => {
65 failed += 1;
66 "failed"
67 }
68 _ => unreachable!(),
69 };
70 errorf!("=== {}: {}\n", input, pf);
71 }
72
73 if failed > 0 {
74 panic!("Failed {} tests", failed);
75 }
76}
77
78/// Test expressions from rustc, like in `test_round_trip`.
79#[test]
80fn test_rustc_precedence() {
Michael Layzell53fc31a2017-06-07 09:21:53 -040081 common::check_min_stack();
Alex Crichton86374772017-07-07 20:39:28 -070082 common::clone_rust();
Michael Layzell53fc31a2017-06-07 09:21:53 -040083 let abort_after = common::abort_after();
84 if abort_after == 0 {
85 panic!("Skipping all precedence tests");
86 }
87
David Tolnayee97dbf2017-11-19 14:24:38 -080088 let passed = AtomicUsize::new(0);
89 let failed = AtomicUsize::new(0);
Michael Layzell53fc31a2017-06-07 09:21:53 -040090
David Tolnayee97dbf2017-11-19 14:24:38 -080091 WalkDir::new("tests/rust")
92 .sort_by(|a, b| a.cmp(b))
93 .into_iter()
94 .filter_entry(common::base_dir_filter)
95 .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
96 .unwrap()
97 .into_par_iter()
David Tolnay51382052017-12-27 13:46:21 -050098 .for_each(|entry| {
99 let path = entry.path();
100 if path.is_dir() {
101 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400102 }
David Tolnay51382052017-12-27 13:46:21 -0500103
104 // Our version of `libsyntax` can't parse this tests
105 if path.to_str()
106 .unwrap()
107 .ends_with("optional_comma_in_match_arm.rs")
108 {
109 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400110 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400111
David Tolnay51382052017-12-27 13:46:21 -0500112 let mut file = File::open(path).unwrap();
113 let mut content = String::new();
114 file.read_to_string(&mut content).unwrap();
Michael Layzell53fc31a2017-06-07 09:21:53 -0400115
David Tolnay51382052017-12-27 13:46:21 -0500116 let (l_passed, l_failed) = match syn::parse_file(&content) {
117 Ok(file) => {
118 let exprs = collect_exprs(file);
119 test_expressions(exprs)
120 }
121 Err(msg) => {
122 errorf!("syn failed to parse\n{:?}\n", msg);
123 (0, 1)
124 }
125 };
David Tolnayee97dbf2017-11-19 14:24:38 -0800126
David Tolnay51382052017-12-27 13:46:21 -0500127 errorf!(
128 "=== {}: {} passed | {} failed\n",
129 path.display(),
130 l_passed,
131 l_failed
132 );
133
134 passed.fetch_add(l_passed, Ordering::SeqCst);
135 let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);
136
137 if prev_failed + l_failed >= abort_after {
138 process::exit(1);
139 }
140 });
David Tolnayee97dbf2017-11-19 14:24:38 -0800141
142 let passed = passed.load(Ordering::SeqCst);
143 let failed = failed.load(Ordering::SeqCst);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400144
145 errorf!("\n===== Precedence Test Results =====\n");
146 errorf!("{} passed | {} failed\n", passed, failed);
147
Michael Layzell53fc31a2017-06-07 09:21:53 -0400148 if failed > 0 {
149 panic!("{} failures", failed);
150 }
151}
152
David Tolnayee97dbf2017-11-19 14:24:38 -0800153fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400154 let mut passed = 0;
155 let mut failed = 0;
156
157 for expr in exprs {
158 let raw = quote!(#expr).to_string();
159
David Tolnaycfa5cc02017-11-13 01:05:11 -0800160 let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400161 e
162 } else {
163 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800164 errorf!("\nFAIL - libsyntax failed to parse raw\n");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400165 continue;
166 };
167
168 let syn_expr = syn_brackets(expr);
David Tolnaycfa5cc02017-11-13 01:05:11 -0800169 let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400170 e
171 } else {
172 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800173 errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400174 continue;
175 };
176
177 let syn_ast = respan::respan_expr(syn_ast);
David Tolnaycfa5cc02017-11-13 01:05:11 -0800178 let libsyntax_ast = respan::respan_expr(libsyntax_ast);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400179
David Tolnaycfa5cc02017-11-13 01:05:11 -0800180 if syn_ast == libsyntax_ast {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400181 passed += 1;
182 } else {
183 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800184 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400185 }
186 }
187
188 (passed, failed)
189}
190
David Tolnaycfa5cc02017-11-13 01:05:11 -0800191fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
David Tolnay3cede942017-12-26 12:29:24 -0500192 parse::libsyntax_expr(input).and_then(libsyntax_brackets)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400193}
194
195/// Wrap every expression which is not already wrapped in parens with parens, to
196/// reveal the precidence of the parsed expressions, and produce a stringified form
197/// of the resulting expression.
198///
David Tolnaycfa5cc02017-11-13 01:05:11 -0800199/// This method operates on libsyntax objects.
200fn libsyntax_brackets(libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
David Tolnay51382052017-12-27 13:46:21 -0500201 use syntax::ast::{Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
David Tolnaycfa5cc02017-11-13 01:05:11 -0800202 use syntax::fold::{self, Folder};
203 use syntax::util::ThinVec;
204 use syntax::util::small_vector::SmallVector;
205 use syntax::ext::quote::rt::DUMMY_SP;
206 use syntax::codemap;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400207
208 fn expr(node: ExprKind) -> P<Expr> {
209 P(Expr {
210 id: ast::DUMMY_NODE_ID,
211 node: node,
212 span: DUMMY_SP,
213 attrs: ThinVec::new(),
214 })
215 }
216
217 struct BracketsFolder {
218 failed: bool,
219 };
220 impl Folder for BracketsFolder {
221 fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
David Tolnay51382052017-12-27 13:46:21 -0500222 e.map(|e| Expr {
223 node: match e.node {
224 ExprKind::Paren(inner) => {
225 ExprKind::Paren(inner.map(|e| fold::noop_fold_expr(e, self)))
226 }
227 ExprKind::If(..) | ExprKind::Block(..) | ExprKind::IfLet(..) => {
228 return fold::noop_fold_expr(e, self);
229 }
230 node => ExprKind::Paren(expr(node).map(|e| fold::noop_fold_expr(e, self))),
231 },
232 ..e
Michael Layzell53fc31a2017-06-07 09:21:53 -0400233 })
234 }
235
236 fn fold_field(&mut self, f: Field) -> Field {
237 Field {
238 ident: codemap::respan(f.ident.span, self.fold_ident(f.ident.node)),
239 expr: if f.is_shorthand {
240 f.expr.map(|e| fold::noop_fold_expr(e, self))
241 } else {
242 self.fold_expr(f.expr)
243 },
244 span: self.new_span(f.span),
245 is_shorthand: f.is_shorthand,
246 attrs: fold::fold_thin_attrs(f.attrs, self),
247 }
248 }
249
250 // We don't want to look at expressions that might appear in patterns or
251 // types yet. We'll look into comparing those in the future. For now
252 // focus on expressions appearing in other places.
253 fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
254 pat
255 }
256
257 fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
258 ty
259 }
260
261 fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
262 let node = match stmt.node {
263 // Don't wrap toplevel expressions in statements.
David Tolnay51382052017-12-27 13:46:21 -0500264 StmtKind::Expr(e) => StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self))),
265 StmtKind::Semi(e) => StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self))),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400266 s => s,
267 };
268
David Tolnay51382052017-12-27 13:46:21 -0500269 SmallVector::one(Stmt { node: node, ..stmt })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400270 }
271
272 fn fold_mac(&mut self, mac: Mac) -> Mac {
David Tolnaycfa5cc02017-11-13 01:05:11 -0800273 // By default when folding over macros, libsyntax panics. This is
Michael Layzell53fc31a2017-06-07 09:21:53 -0400274 // because it's usually not what you want, you want to run after
275 // macro expansion. We do want to do that (syn doesn't do macro
276 // expansion), so we implement fold_mac to just return the macro
277 // unchanged.
278 mac
279 }
280 }
281
David Tolnay51382052017-12-27 13:46:21 -0500282 let mut folder = BracketsFolder { failed: false };
David Tolnaycfa5cc02017-11-13 01:05:11 -0800283 let e = folder.fold_expr(libsyntax_expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400284 if folder.failed {
285 None
286 } else {
287 Some(e)
288 }
289}
290
291/// Wrap every expression which is not already wrapped in parens with parens, to
292/// reveal the precidence of the parsed expressions, and produce a stringified form
293/// of the resulting expression.
294fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
295 use syn::*;
296 use syn::fold::*;
297
David Tolnay8c91b882017-12-28 23:04:32 -0500298 fn paren(folder: &mut BracketsFolder, mut node: Expr) -> Expr {
David Tolnay2ae520a2017-12-29 11:19:50 -0500299 let attrs = node.replace_attrs(Vec::new());
David Tolnay8c91b882017-12-28 23:04:32 -0500300 Expr::Paren(ExprParen {
301 attrs: attrs,
David Tolnay51382052017-12-27 13:46:21 -0500302 expr: Box::new(fold_expr(
303 folder,
David Tolnay8c91b882017-12-28 23:04:32 -0500304 node,
David Tolnay51382052017-12-27 13:46:21 -0500305 )),
David Tolnay42eaae12017-12-26 23:05:18 -0500306 paren_token: token::Paren::default(),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400307 })
308 }
309
310 struct BracketsFolder;
311 impl Folder for BracketsFolder {
312 fn fold_expr(&mut self, expr: Expr) -> Expr {
David Tolnay8c91b882017-12-28 23:04:32 -0500313 match expr {
314 Expr::Group(_) => unreachable!(),
315 Expr::Paren(p) => paren(self, *p.expr),
316 Expr::If(..)
317 | Expr::Unsafe(..)
318 | Expr::Block(..)
319 | Expr::IfLet(..) => {
David Tolnay3bc597f2017-12-31 02:31:11 -0500320 fold_expr(self, expr)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400321 }
322 node => paren(self, node),
David Tolnay8c91b882017-12-28 23:04:32 -0500323 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400324 }
325
326 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
327 match stmt {
328 // Don't wrap toplevel expressions in statements.
David Tolnay51382052017-12-27 13:46:21 -0500329 Stmt::Expr(e) => Stmt::Expr(Box::new(fold_expr(self, *e))),
330 Stmt::Semi(e, semi) => Stmt::Semi(Box::new(fold_expr(self, *e)), semi),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400331 s => s,
332 }
333 }
334
335 // We don't want to look at expressions that might appear in patterns or
336 // types yet. We'll look into comparing those in the future. For now
337 // focus on expressions appearing in other places.
338 fn fold_pat(&mut self, pat: Pat) -> Pat {
339 pat
340 }
341
David Tolnayfd6bf5c2017-11-12 09:41:14 -0800342 fn fold_type(&mut self, ty: Type) -> Type {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400343 ty
344 }
345 }
346
347 let mut folder = BracketsFolder;
348 folder.fold_expr(syn_expr)
349}
350
351/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700352fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400353 use syn::*;
354 use syn::fold::*;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500355 use syn::delimited::Delimited;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400356
357 struct CollectExprsFolder(Vec<Expr>);
358 impl Folder for CollectExprsFolder {
359 fn fold_expr(&mut self, expr: Expr) -> Expr {
360 self.0.push(expr);
361
David Tolnay8c91b882017-12-28 23:04:32 -0500362 Expr::Tuple(ExprTuple {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400363 attrs: vec![],
David Tolnay2a86fdd2017-12-28 23:34:28 -0500364 elems: Delimited::new(),
David Tolnay8c91b882017-12-28 23:04:32 -0500365 paren_token: token::Paren::default(),
366 })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400367 }
368 }
369
370 let mut folder = CollectExprsFolder(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700371 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400372 folder.0
373}