blob: 8ef0a7958894a839dfd5592a7a0ed9a2e9274b43 [file] [log] [blame]
Michael Layzell53fc31a2017-06-07 09:21:53 -04001#![cfg(all(feature = "full", feature = "fold"))]
2
Nika Layzella2a1a4a2017-11-19 11:33:17 -05003#![feature(rustc_private)]
4
Michael Layzell53fc31a2017-06-07 09:21:53 -04005//! The tests in this module do the following:
6//!
David Tolnaycfa5cc02017-11-13 01:05:11 -08007//! 1. Parse a given expression in both `syn` and `libsyntax`.
Michael Layzell53fc31a2017-06-07 09:21:53 -04008//! 2. Fold over the expression adding brackets around each subexpression (with
David Tolnaycfa5cc02017-11-13 01:05:11 -08009//! some complications - see the `syn_brackets` and `libsyntax_brackets`
Michael Layzell53fc31a2017-06-07 09:21:53 -040010//! methods).
11//! 3. Serialize the `syn` expression back into a string, and re-parse it with
David Tolnaycfa5cc02017-11-13 01:05:11 -080012//! `libsyntax`.
Michael Layzell53fc31a2017-06-07 09:21:53 -040013//! 4. Respan all of the expressions, replacing the spans with the default spans.
14//! 5. Compare the expressions with one another, if they are not equal fail.
15
16#[macro_use]
17extern crate quote;
David Tolnayee97dbf2017-11-19 14:24:38 -080018extern crate rayon;
Michael Layzell53fc31a2017-06-07 09:21:53 -040019extern crate syn;
20extern crate synom;
David Tolnaycfa5cc02017-11-13 01:05:11 -080021extern crate syntax;
Michael Layzell53fc31a2017-06-07 09:21:53 -040022extern crate walkdir;
Michael Layzell53fc31a2017-06-07 09:21:53 -040023
David Tolnayee97dbf2017-11-19 14:24:38 -080024use rayon::iter::{ParallelIterator, IntoParallelIterator};
David Tolnaycfa5cc02017-11-13 01:05:11 -080025use syntax::ast;
26use syntax::ptr::P;
David Tolnayee97dbf2017-11-19 14:24:38 -080027use walkdir::{WalkDir, WalkDirIterator, DirEntry};
28
29use std::fs::File;
30use std::io::Read;
David Tolnay3eaf7d82017-12-17 23:14:52 -080031use std::process;
David Tolnayee97dbf2017-11-19 14:24:38 -080032use std::sync::atomic::{AtomicUsize, Ordering};
Michael Layzell53fc31a2017-06-07 09:21:53 -040033
34use common::{respan, parse};
35
36#[allow(dead_code)]
37#[macro_use]
38mod 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()
98 .for_each(|entry|
99 {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400100 let path = entry.path();
101 if path.is_dir() {
David Tolnayee97dbf2017-11-19 14:24:38 -0800102 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400103 }
104
David Tolnaycfa5cc02017-11-13 01:05:11 -0800105 // Our version of `libsyntax` can't parse this tests
Alex Crichton10143d02017-08-28 10:02:00 -0700106 if path.to_str().unwrap().ends_with("optional_comma_in_match_arm.rs") {
David Tolnayee97dbf2017-11-19 14:24:38 -0800107 return;
Alex Crichton10143d02017-08-28 10:02:00 -0700108 }
109
Michael Layzell53fc31a2017-06-07 09:21:53 -0400110 let mut file = File::open(path).unwrap();
111 let mut content = String::new();
112 file.read_to_string(&mut content).unwrap();
113
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700114 let (l_passed, l_failed) = match syn::parse_file(&content) {
115 Ok(file) => {
116 let exprs = collect_exprs(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400117 test_expressions(exprs)
118 }
119 Err(msg) => {
120 errorf!("syn failed to parse\n{:?}\n", msg);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400121 (0, 1)
122 }
123 };
124
Michael Layzell53fc31a2017-06-07 09:21:53 -0400125 errorf!("=== {}: {} passed | {} failed\n", path.display(), l_passed, l_failed);
126
David Tolnayee97dbf2017-11-19 14:24:38 -0800127 passed.fetch_add(l_passed, Ordering::SeqCst);
128 let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);
129
130 if prev_failed + l_failed >= abort_after {
David Tolnay3eaf7d82017-12-17 23:14:52 -0800131 process::exit(1);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400132 }
David Tolnayee97dbf2017-11-19 14:24:38 -0800133 });
134
135 let passed = passed.load(Ordering::SeqCst);
136 let failed = failed.load(Ordering::SeqCst);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400137
138 errorf!("\n===== Precedence Test Results =====\n");
139 errorf!("{} passed | {} failed\n", passed, failed);
140
Michael Layzell53fc31a2017-06-07 09:21:53 -0400141 if failed > 0 {
142 panic!("{} failures", failed);
143 }
144}
145
David Tolnayee97dbf2017-11-19 14:24:38 -0800146fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400147 let mut passed = 0;
148 let mut failed = 0;
149
150 for expr in exprs {
151 let raw = quote!(#expr).to_string();
152
David Tolnaycfa5cc02017-11-13 01:05:11 -0800153 let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400154 e
155 } else {
156 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800157 errorf!("\nFAIL - libsyntax failed to parse raw\n");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400158 continue;
159 };
160
161 let syn_expr = syn_brackets(expr);
David Tolnaycfa5cc02017-11-13 01:05:11 -0800162 let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400163 e
164 } else {
165 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800166 errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400167 continue;
168 };
169
170 let syn_ast = respan::respan_expr(syn_ast);
David Tolnaycfa5cc02017-11-13 01:05:11 -0800171 let libsyntax_ast = respan::respan_expr(libsyntax_ast);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400172
David Tolnaycfa5cc02017-11-13 01:05:11 -0800173 if syn_ast == libsyntax_ast {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400174 passed += 1;
175 } else {
176 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800177 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400178 }
179 }
180
181 (passed, failed)
182}
183
David Tolnaycfa5cc02017-11-13 01:05:11 -0800184fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
185 parse::libsyntax_expr(input).and_then(|e| libsyntax_brackets(e))
Michael Layzell53fc31a2017-06-07 09:21:53 -0400186}
187
188/// Wrap every expression which is not already wrapped in parens with parens, to
189/// reveal the precidence of the parsed expressions, and produce a stringified form
190/// of the resulting expression.
191///
David Tolnaycfa5cc02017-11-13 01:05:11 -0800192/// This method operates on libsyntax objects.
193fn libsyntax_brackets(libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
194 use syntax::ast::{Expr, ExprKind, Mac, Stmt, StmtKind, Pat, Ty, Field};
195 use syntax::fold::{self, Folder};
196 use syntax::util::ThinVec;
197 use syntax::util::small_vector::SmallVector;
198 use syntax::ext::quote::rt::DUMMY_SP;
199 use syntax::codemap;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400200
201 fn expr(node: ExprKind) -> P<Expr> {
202 P(Expr {
203 id: ast::DUMMY_NODE_ID,
204 node: node,
205 span: DUMMY_SP,
206 attrs: ThinVec::new(),
207 })
208 }
209
210 struct BracketsFolder {
211 failed: bool,
212 };
213 impl Folder for BracketsFolder {
214 fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
215 e.map(|e| {
216 Expr {
217 node: match e.node {
218 ExprKind::Paren(inner) => {
219 ExprKind::Paren(inner.map(|e| {
220 fold::noop_fold_expr(e, self)
221 }))
222 }
223 ExprKind::If(..) |
224 ExprKind::Block(..) |
225 ExprKind::IfLet(..) => {
226 return fold::noop_fold_expr(e, self);
227 }
228 node => {
229 ExprKind::Paren(expr(node).map(|e| {
230 fold::noop_fold_expr(e, self)
231 }))
232 }
233 },
234 ..e
235 }
236 })
237 }
238
239 fn fold_field(&mut self, f: Field) -> Field {
240 Field {
241 ident: codemap::respan(f.ident.span, self.fold_ident(f.ident.node)),
242 expr: if f.is_shorthand {
243 f.expr.map(|e| fold::noop_fold_expr(e, self))
244 } else {
245 self.fold_expr(f.expr)
246 },
247 span: self.new_span(f.span),
248 is_shorthand: f.is_shorthand,
249 attrs: fold::fold_thin_attrs(f.attrs, self),
250 }
251 }
252
253 // We don't want to look at expressions that might appear in patterns or
254 // types yet. We'll look into comparing those in the future. For now
255 // focus on expressions appearing in other places.
256 fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
257 pat
258 }
259
260 fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
261 ty
262 }
263
264 fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
265 let node = match stmt.node {
266 // Don't wrap toplevel expressions in statements.
267 StmtKind::Expr(e) => {
268 StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self)))
269 }
270 StmtKind::Semi(e) => {
271 StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self)))
272 }
273 s => s,
274 };
275
276 SmallVector::one(Stmt {
277 node: node,
278 ..stmt
279 })
280 }
281
282 fn fold_mac(&mut self, mac: Mac) -> Mac {
David Tolnaycfa5cc02017-11-13 01:05:11 -0800283 // By default when folding over macros, libsyntax panics. This is
Michael Layzell53fc31a2017-06-07 09:21:53 -0400284 // because it's usually not what you want, you want to run after
285 // macro expansion. We do want to do that (syn doesn't do macro
286 // expansion), so we implement fold_mac to just return the macro
287 // unchanged.
288 mac
289 }
290 }
291
292 let mut folder = BracketsFolder {
293 failed: false,
294 };
David Tolnaycfa5cc02017-11-13 01:05:11 -0800295 let e = folder.fold_expr(libsyntax_expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400296 if folder.failed {
297 None
298 } else {
299 Some(e)
300 }
301}
302
303/// Wrap every expression which is not already wrapped in parens with parens, to
304/// reveal the precidence of the parsed expressions, and produce a stringified form
305/// of the resulting expression.
306fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
307 use syn::*;
308 use syn::fold::*;
309
310 fn paren(folder: &mut BracketsFolder, node: ExprKind) -> ExprKind {
311 ExprKind::Paren(ExprParen {
Nika Layzella6f46c42017-10-26 15:26:16 -0400312 expr: Box::new(fold_expr(folder, Expr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400313 node: node,
314 attrs: vec![],
315 })),
316 paren_token: tokens::Paren::default(),
317 })
318 }
319
320 struct BracketsFolder;
321 impl Folder for BracketsFolder {
322 fn fold_expr(&mut self, expr: Expr) -> Expr {
323 let kind = match expr.node {
324 ExprKind::Group(_) => unreachable!(),
325 ExprKind::Paren(p) => paren(self, p.expr.node),
326 ExprKind::If(..) |
Nika Layzell640832a2017-12-04 13:37:09 -0500327 ExprKind::Unsafe(..) |
Michael Layzell53fc31a2017-06-07 09:21:53 -0400328 ExprKind::Block(..) |
329 ExprKind::IfLet(..) => {
Nika Layzella6f46c42017-10-26 15:26:16 -0400330 return fold_expr(self, expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400331 }
332 node => paren(self, node),
333 };
334
335 Expr {
336 node: kind,
337 ..expr
338 }
339 }
340
341 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
342 match stmt {
343 // Don't wrap toplevel expressions in statements.
344 Stmt::Expr(e) => {
Nika Layzella6f46c42017-10-26 15:26:16 -0400345 Stmt::Expr(Box::new(fold_expr(self, *e)))
Michael Layzell53fc31a2017-06-07 09:21:53 -0400346 }
347 Stmt::Semi(e, semi) => {
Nika Layzella6f46c42017-10-26 15:26:16 -0400348 Stmt::Semi(Box::new(fold_expr(self, *e)), semi)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400349 }
350 s => s,
351 }
352 }
353
354 // We don't want to look at expressions that might appear in patterns or
355 // types yet. We'll look into comparing those in the future. For now
356 // focus on expressions appearing in other places.
357 fn fold_pat(&mut self, pat: Pat) -> Pat {
358 pat
359 }
360
David Tolnayfd6bf5c2017-11-12 09:41:14 -0800361 fn fold_type(&mut self, ty: Type) -> Type {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400362 ty
363 }
364 }
365
366 let mut folder = BracketsFolder;
367 folder.fold_expr(syn_expr)
368}
369
370/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700371fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400372 use synom::delimited::Delimited;
373 use syn::*;
374 use syn::fold::*;
375
376 struct CollectExprsFolder(Vec<Expr>);
377 impl Folder for CollectExprsFolder {
378 fn fold_expr(&mut self, expr: Expr) -> Expr {
379 self.0.push(expr);
380
381 Expr {
David Tolnayd0797732017-12-26 01:46:12 -0500382 node: ExprKind::Tuple(ExprTuple {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400383 args: Delimited::new(),
384 paren_token: tokens::Paren::default(),
385 lone_comma: None
386 }),
387 attrs: vec![],
388 }
389 }
390 }
391
392 let mut folder = CollectExprsFolder(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700393 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400394 folder.0
395}