blob: 0d5f5160f4d0dc97e1e8634b691a0f0861d83f64 [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;
31use std::sync::atomic::{AtomicUsize, Ordering};
Michael Layzell53fc31a2017-06-07 09:21:53 -040032
33use common::{respan, parse};
34
35#[allow(dead_code)]
36#[macro_use]
37mod common;
38
39/// Test some pre-set expressions chosen by us.
40#[test]
41fn test_simple_precedence() {
42 const EXPRS: &[&str] = &[
43 "1 + 2 * 3 + 4",
44 "1 + 2 * ( 3 + 4 )",
45 "{ for i in r { } *some_ptr += 1; }",
46 "{ loop { break 5; } }",
47 "{ if true { () }.mthd() }",
Nika Layzell3aa0dc72017-12-04 13:41:28 -050048 "{ for i in unsafe { 20 } { } }",
Michael Layzell53fc31a2017-06-07 09:21:53 -040049 ];
50
51 let mut failed = 0;
52
53 for input in EXPRS {
54 let expr = if let Some(expr) = parse::syn_expr(input) {
55 expr
56 } else {
57 failed += 1;
58 continue;
59 };
60
61 let pf = match test_expressions(vec![expr]) {
62 (1, 0) => "passed",
63 (0, 1) => {
64 failed += 1;
65 "failed"
66 }
67 _ => unreachable!(),
68 };
69 errorf!("=== {}: {}\n", input, pf);
70 }
71
72 if failed > 0 {
73 panic!("Failed {} tests", failed);
74 }
75}
76
77/// Test expressions from rustc, like in `test_round_trip`.
78#[test]
79fn test_rustc_precedence() {
Michael Layzell53fc31a2017-06-07 09:21:53 -040080 common::check_min_stack();
Alex Crichton86374772017-07-07 20:39:28 -070081 common::clone_rust();
Michael Layzell53fc31a2017-06-07 09:21:53 -040082 let abort_after = common::abort_after();
83 if abort_after == 0 {
84 panic!("Skipping all precedence tests");
85 }
86
David Tolnayee97dbf2017-11-19 14:24:38 -080087 let passed = AtomicUsize::new(0);
88 let failed = AtomicUsize::new(0);
Michael Layzell53fc31a2017-06-07 09:21:53 -040089
David Tolnayee97dbf2017-11-19 14:24:38 -080090 WalkDir::new("tests/rust")
91 .sort_by(|a, b| a.cmp(b))
92 .into_iter()
93 .filter_entry(common::base_dir_filter)
94 .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
95 .unwrap()
96 .into_par_iter()
97 .for_each(|entry|
98 {
Michael Layzell53fc31a2017-06-07 09:21:53 -040099 let path = entry.path();
100 if path.is_dir() {
David Tolnayee97dbf2017-11-19 14:24:38 -0800101 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400102 }
103
David Tolnaycfa5cc02017-11-13 01:05:11 -0800104 // Our version of `libsyntax` can't parse this tests
Alex Crichton10143d02017-08-28 10:02:00 -0700105 if path.to_str().unwrap().ends_with("optional_comma_in_match_arm.rs") {
David Tolnayee97dbf2017-11-19 14:24:38 -0800106 return;
Alex Crichton10143d02017-08-28 10:02:00 -0700107 }
108
Michael Layzell53fc31a2017-06-07 09:21:53 -0400109 let mut file = File::open(path).unwrap();
110 let mut content = String::new();
111 file.read_to_string(&mut content).unwrap();
112
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700113 let (l_passed, l_failed) = match syn::parse_file(&content) {
114 Ok(file) => {
115 let exprs = collect_exprs(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400116 test_expressions(exprs)
117 }
118 Err(msg) => {
119 errorf!("syn failed to parse\n{:?}\n", msg);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400120 (0, 1)
121 }
122 };
123
Michael Layzell53fc31a2017-06-07 09:21:53 -0400124 errorf!("=== {}: {} passed | {} failed\n", path.display(), l_passed, l_failed);
125
David Tolnayee97dbf2017-11-19 14:24:38 -0800126 passed.fetch_add(l_passed, Ordering::SeqCst);
127 let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);
128
129 if prev_failed + l_failed >= abort_after {
130 panic!("Aborting Immediately due to ABORT_AFTER_FAILURE");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400131 }
David Tolnayee97dbf2017-11-19 14:24:38 -0800132 });
133
134 let passed = passed.load(Ordering::SeqCst);
135 let failed = failed.load(Ordering::SeqCst);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400136
137 errorf!("\n===== Precedence Test Results =====\n");
138 errorf!("{} passed | {} failed\n", passed, failed);
139
Michael Layzell53fc31a2017-06-07 09:21:53 -0400140 if failed > 0 {
141 panic!("{} failures", failed);
142 }
143}
144
David Tolnayee97dbf2017-11-19 14:24:38 -0800145fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400146 let mut passed = 0;
147 let mut failed = 0;
148
149 for expr in exprs {
150 let raw = quote!(#expr).to_string();
151
David Tolnaycfa5cc02017-11-13 01:05:11 -0800152 let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400153 e
154 } else {
155 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800156 errorf!("\nFAIL - libsyntax failed to parse raw\n");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400157 continue;
158 };
159
160 let syn_expr = syn_brackets(expr);
David Tolnaycfa5cc02017-11-13 01:05:11 -0800161 let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400162 e
163 } else {
164 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800165 errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400166 continue;
167 };
168
169 let syn_ast = respan::respan_expr(syn_ast);
David Tolnaycfa5cc02017-11-13 01:05:11 -0800170 let libsyntax_ast = respan::respan_expr(libsyntax_ast);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400171
David Tolnaycfa5cc02017-11-13 01:05:11 -0800172 if syn_ast == libsyntax_ast {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400173 passed += 1;
174 } else {
175 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800176 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400177 }
178 }
179
180 (passed, failed)
181}
182
David Tolnaycfa5cc02017-11-13 01:05:11 -0800183fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
184 parse::libsyntax_expr(input).and_then(|e| libsyntax_brackets(e))
Michael Layzell53fc31a2017-06-07 09:21:53 -0400185}
186
187/// Wrap every expression which is not already wrapped in parens with parens, to
188/// reveal the precidence of the parsed expressions, and produce a stringified form
189/// of the resulting expression.
190///
David Tolnaycfa5cc02017-11-13 01:05:11 -0800191/// This method operates on libsyntax objects.
192fn libsyntax_brackets(libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
193 use syntax::ast::{Expr, ExprKind, Mac, Stmt, StmtKind, Pat, Ty, Field};
194 use syntax::fold::{self, Folder};
195 use syntax::util::ThinVec;
196 use syntax::util::small_vector::SmallVector;
197 use syntax::ext::quote::rt::DUMMY_SP;
198 use syntax::codemap;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400199
200 fn expr(node: ExprKind) -> P<Expr> {
201 P(Expr {
202 id: ast::DUMMY_NODE_ID,
203 node: node,
204 span: DUMMY_SP,
205 attrs: ThinVec::new(),
206 })
207 }
208
209 struct BracketsFolder {
210 failed: bool,
211 };
212 impl Folder for BracketsFolder {
213 fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
214 e.map(|e| {
215 Expr {
216 node: match e.node {
217 ExprKind::Paren(inner) => {
218 ExprKind::Paren(inner.map(|e| {
219 fold::noop_fold_expr(e, self)
220 }))
221 }
222 ExprKind::If(..) |
223 ExprKind::Block(..) |
224 ExprKind::IfLet(..) => {
225 return fold::noop_fold_expr(e, self);
226 }
227 node => {
228 ExprKind::Paren(expr(node).map(|e| {
229 fold::noop_fold_expr(e, self)
230 }))
231 }
232 },
233 ..e
234 }
235 })
236 }
237
238 fn fold_field(&mut self, f: Field) -> Field {
239 Field {
240 ident: codemap::respan(f.ident.span, self.fold_ident(f.ident.node)),
241 expr: if f.is_shorthand {
242 f.expr.map(|e| fold::noop_fold_expr(e, self))
243 } else {
244 self.fold_expr(f.expr)
245 },
246 span: self.new_span(f.span),
247 is_shorthand: f.is_shorthand,
248 attrs: fold::fold_thin_attrs(f.attrs, self),
249 }
250 }
251
252 // We don't want to look at expressions that might appear in patterns or
253 // types yet. We'll look into comparing those in the future. For now
254 // focus on expressions appearing in other places.
255 fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
256 pat
257 }
258
259 fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
260 ty
261 }
262
263 fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
264 let node = match stmt.node {
265 // Don't wrap toplevel expressions in statements.
266 StmtKind::Expr(e) => {
267 StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self)))
268 }
269 StmtKind::Semi(e) => {
270 StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self)))
271 }
272 s => s,
273 };
274
275 SmallVector::one(Stmt {
276 node: node,
277 ..stmt
278 })
279 }
280
281 fn fold_mac(&mut self, mac: Mac) -> Mac {
David Tolnaycfa5cc02017-11-13 01:05:11 -0800282 // By default when folding over macros, libsyntax panics. This is
Michael Layzell53fc31a2017-06-07 09:21:53 -0400283 // because it's usually not what you want, you want to run after
284 // macro expansion. We do want to do that (syn doesn't do macro
285 // expansion), so we implement fold_mac to just return the macro
286 // unchanged.
287 mac
288 }
289 }
290
291 let mut folder = BracketsFolder {
292 failed: false,
293 };
David Tolnaycfa5cc02017-11-13 01:05:11 -0800294 let e = folder.fold_expr(libsyntax_expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400295 if folder.failed {
296 None
297 } else {
298 Some(e)
299 }
300}
301
302/// Wrap every expression which is not already wrapped in parens with parens, to
303/// reveal the precidence of the parsed expressions, and produce a stringified form
304/// of the resulting expression.
305fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
306 use syn::*;
307 use syn::fold::*;
308
309 fn paren(folder: &mut BracketsFolder, node: ExprKind) -> ExprKind {
310 ExprKind::Paren(ExprParen {
Nika Layzella6f46c42017-10-26 15:26:16 -0400311 expr: Box::new(fold_expr(folder, Expr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400312 node: node,
313 attrs: vec![],
314 })),
315 paren_token: tokens::Paren::default(),
316 })
317 }
318
319 struct BracketsFolder;
320 impl Folder for BracketsFolder {
321 fn fold_expr(&mut self, expr: Expr) -> Expr {
322 let kind = match expr.node {
323 ExprKind::Group(_) => unreachable!(),
324 ExprKind::Paren(p) => paren(self, p.expr.node),
325 ExprKind::If(..) |
Nika Layzell640832a2017-12-04 13:37:09 -0500326 ExprKind::Unsafe(..) |
Michael Layzell53fc31a2017-06-07 09:21:53 -0400327 ExprKind::Block(..) |
328 ExprKind::IfLet(..) => {
Nika Layzella6f46c42017-10-26 15:26:16 -0400329 return fold_expr(self, expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400330 }
331 node => paren(self, node),
332 };
333
334 Expr {
335 node: kind,
336 ..expr
337 }
338 }
339
340 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
341 match stmt {
342 // Don't wrap toplevel expressions in statements.
343 Stmt::Expr(e) => {
Nika Layzella6f46c42017-10-26 15:26:16 -0400344 Stmt::Expr(Box::new(fold_expr(self, *e)))
Michael Layzell53fc31a2017-06-07 09:21:53 -0400345 }
346 Stmt::Semi(e, semi) => {
Nika Layzella6f46c42017-10-26 15:26:16 -0400347 Stmt::Semi(Box::new(fold_expr(self, *e)), semi)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400348 }
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
David Tolnayfd6bf5c2017-11-12 09:41:14 -0800360 fn fold_type(&mut self, ty: Type) -> Type {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400361 ty
362 }
363 }
364
365 let mut folder = BracketsFolder;
366 folder.fold_expr(syn_expr)
367}
368
369/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700370fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400371 use synom::delimited::Delimited;
372 use syn::*;
373 use syn::fold::*;
374
375 struct CollectExprsFolder(Vec<Expr>);
376 impl Folder for CollectExprsFolder {
377 fn fold_expr(&mut self, expr: Expr) -> Expr {
378 self.0.push(expr);
379
380 Expr {
381 node: ExprKind::Tup(ExprTup {
382 args: Delimited::new(),
383 paren_token: tokens::Paren::default(),
384 lone_comma: None
385 }),
386 attrs: vec![],
387 }
388 }
389 }
390
391 let mut folder = CollectExprsFolder(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700392 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400393 folder.0
394}