blob: 9f432715a734762733fc3aad640d49b8ae2a4984 [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;
18extern crate syn;
19extern crate synom;
David Tolnaycfa5cc02017-11-13 01:05:11 -080020extern crate syntax;
Michael Layzell53fc31a2017-06-07 09:21:53 -040021extern crate walkdir;
Michael Layzell53fc31a2017-06-07 09:21:53 -040022
David Tolnaycfa5cc02017-11-13 01:05:11 -080023use syntax::ast;
24use syntax::ptr::P;
Michael Layzell53fc31a2017-06-07 09:21:53 -040025
26use common::{respan, parse};
27
28#[allow(dead_code)]
29#[macro_use]
30mod common;
31
32/// Test some pre-set expressions chosen by us.
33#[test]
34fn test_simple_precedence() {
35 const EXPRS: &[&str] = &[
36 "1 + 2 * 3 + 4",
37 "1 + 2 * ( 3 + 4 )",
38 "{ for i in r { } *some_ptr += 1; }",
39 "{ loop { break 5; } }",
40 "{ if true { () }.mthd() }",
41 ];
42
43 let mut failed = 0;
44
45 for input in EXPRS {
46 let expr = if let Some(expr) = parse::syn_expr(input) {
47 expr
48 } else {
49 failed += 1;
50 continue;
51 };
52
53 let pf = match test_expressions(vec![expr]) {
54 (1, 0) => "passed",
55 (0, 1) => {
56 failed += 1;
57 "failed"
58 }
59 _ => unreachable!(),
60 };
61 errorf!("=== {}: {}\n", input, pf);
62 }
63
64 if failed > 0 {
65 panic!("Failed {} tests", failed);
66 }
67}
68
69/// Test expressions from rustc, like in `test_round_trip`.
70#[test]
71fn test_rustc_precedence() {
72 use walkdir::{WalkDir, WalkDirIterator};
73 use std::fs::File;
74 use std::io::Read;
75
76 common::check_min_stack();
Alex Crichton86374772017-07-07 20:39:28 -070077 common::clone_rust();
Michael Layzell53fc31a2017-06-07 09:21:53 -040078 let abort_after = common::abort_after();
79 if abort_after == 0 {
80 panic!("Skipping all precedence tests");
81 }
82
83 let mut passed = 0;
84 let mut failed = 0;
85
86 let walk = WalkDir::new("tests/rust").sort_by(|a, b| a.cmp(b));
87 for entry in walk.into_iter().filter_entry(common::base_dir_filter) {
88 let entry = entry.unwrap();
89
90 let path = entry.path();
91 if path.is_dir() {
92 continue;
93 }
94
David Tolnaycfa5cc02017-11-13 01:05:11 -080095 // Our version of `libsyntax` can't parse this tests
Alex Crichton10143d02017-08-28 10:02:00 -070096 if path.to_str().unwrap().ends_with("optional_comma_in_match_arm.rs") {
97 continue
98 }
99
Michael Layzell53fc31a2017-06-07 09:21:53 -0400100 let mut file = File::open(path).unwrap();
101 let mut content = String::new();
102 file.read_to_string(&mut content).unwrap();
103
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700104 let (l_passed, l_failed) = match syn::parse_file(&content) {
105 Ok(file) => {
106 let exprs = collect_exprs(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400107 test_expressions(exprs)
108 }
109 Err(msg) => {
110 errorf!("syn failed to parse\n{:?}\n", msg);
111 failed += 1;
112 (0, 1)
113 }
114 };
115
116 passed += l_passed;
117 failed += l_failed;
118
119 errorf!("=== {}: {} passed | {} failed\n", path.display(), l_passed, l_failed);
120
121 if failed >= abort_after {
122 errorf!("Aborting Immediately due to ABORT_AFTER_FAILURE\n");
123 break;
124 }
125 }
126
127 errorf!("\n===== Precedence Test Results =====\n");
128 errorf!("{} passed | {} failed\n", passed, failed);
129
130
131 if failed > 0 {
132 panic!("{} failures", failed);
133 }
134}
135
136fn test_expressions(exprs: Vec<syn::Expr>) -> (u32, u32) {
137 let mut passed = 0;
138 let mut failed = 0;
139
140 for expr in exprs {
141 let raw = quote!(#expr).to_string();
142
David Tolnaycfa5cc02017-11-13 01:05:11 -0800143 let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400144 e
145 } else {
146 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800147 errorf!("\nFAIL - libsyntax failed to parse raw\n");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400148 continue;
149 };
150
151 let syn_expr = syn_brackets(expr);
David Tolnaycfa5cc02017-11-13 01:05:11 -0800152 let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
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 bracketed\n");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400157 continue;
158 };
159
160 let syn_ast = respan::respan_expr(syn_ast);
David Tolnaycfa5cc02017-11-13 01:05:11 -0800161 let libsyntax_ast = respan::respan_expr(libsyntax_ast);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400162
David Tolnaycfa5cc02017-11-13 01:05:11 -0800163 if syn_ast == libsyntax_ast {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400164 passed += 1;
165 } else {
166 failed += 1;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800167 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400168 }
169 }
170
171 (passed, failed)
172}
173
David Tolnaycfa5cc02017-11-13 01:05:11 -0800174fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
175 parse::libsyntax_expr(input).and_then(|e| libsyntax_brackets(e))
Michael Layzell53fc31a2017-06-07 09:21:53 -0400176}
177
178/// Wrap every expression which is not already wrapped in parens with parens, to
179/// reveal the precidence of the parsed expressions, and produce a stringified form
180/// of the resulting expression.
181///
David Tolnaycfa5cc02017-11-13 01:05:11 -0800182/// This method operates on libsyntax objects.
183fn libsyntax_brackets(libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
184 use syntax::ast::{Expr, ExprKind, Mac, Stmt, StmtKind, Pat, Ty, Field};
185 use syntax::fold::{self, Folder};
186 use syntax::util::ThinVec;
187 use syntax::util::small_vector::SmallVector;
188 use syntax::ext::quote::rt::DUMMY_SP;
189 use syntax::codemap;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400190
191 fn expr(node: ExprKind) -> P<Expr> {
192 P(Expr {
193 id: ast::DUMMY_NODE_ID,
194 node: node,
195 span: DUMMY_SP,
196 attrs: ThinVec::new(),
197 })
198 }
199
200 struct BracketsFolder {
201 failed: bool,
202 };
203 impl Folder for BracketsFolder {
204 fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
205 e.map(|e| {
206 Expr {
207 node: match e.node {
208 ExprKind::Paren(inner) => {
209 ExprKind::Paren(inner.map(|e| {
210 fold::noop_fold_expr(e, self)
211 }))
212 }
213 ExprKind::If(..) |
214 ExprKind::Block(..) |
215 ExprKind::IfLet(..) => {
216 return fold::noop_fold_expr(e, self);
217 }
218 node => {
219 ExprKind::Paren(expr(node).map(|e| {
220 fold::noop_fold_expr(e, self)
221 }))
222 }
223 },
224 ..e
225 }
226 })
227 }
228
229 fn fold_field(&mut self, f: Field) -> Field {
230 Field {
231 ident: codemap::respan(f.ident.span, self.fold_ident(f.ident.node)),
232 expr: if f.is_shorthand {
233 f.expr.map(|e| fold::noop_fold_expr(e, self))
234 } else {
235 self.fold_expr(f.expr)
236 },
237 span: self.new_span(f.span),
238 is_shorthand: f.is_shorthand,
239 attrs: fold::fold_thin_attrs(f.attrs, self),
240 }
241 }
242
243 // We don't want to look at expressions that might appear in patterns or
244 // types yet. We'll look into comparing those in the future. For now
245 // focus on expressions appearing in other places.
246 fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
247 pat
248 }
249
250 fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
251 ty
252 }
253
254 fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
255 let node = match stmt.node {
256 // Don't wrap toplevel expressions in statements.
257 StmtKind::Expr(e) => {
258 StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self)))
259 }
260 StmtKind::Semi(e) => {
261 StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self)))
262 }
263 s => s,
264 };
265
266 SmallVector::one(Stmt {
267 node: node,
268 ..stmt
269 })
270 }
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
282 let mut folder = BracketsFolder {
283 failed: false,
284 };
David Tolnaycfa5cc02017-11-13 01:05:11 -0800285 let e = folder.fold_expr(libsyntax_expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400286 if folder.failed {
287 None
288 } else {
289 Some(e)
290 }
291}
292
293/// Wrap every expression which is not already wrapped in parens with parens, to
294/// reveal the precidence of the parsed expressions, and produce a stringified form
295/// of the resulting expression.
296fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
297 use syn::*;
298 use syn::fold::*;
299
300 fn paren(folder: &mut BracketsFolder, node: ExprKind) -> ExprKind {
301 ExprKind::Paren(ExprParen {
Nika Layzella6f46c42017-10-26 15:26:16 -0400302 expr: Box::new(fold_expr(folder, Expr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400303 node: node,
304 attrs: vec![],
305 })),
306 paren_token: tokens::Paren::default(),
307 })
308 }
309
310 struct BracketsFolder;
311 impl Folder for BracketsFolder {
312 fn fold_expr(&mut self, expr: Expr) -> Expr {
313 let kind = match expr.node {
314 ExprKind::Group(_) => unreachable!(),
315 ExprKind::Paren(p) => paren(self, p.expr.node),
316 ExprKind::If(..) |
317 ExprKind::Block(..) |
318 ExprKind::IfLet(..) => {
Nika Layzella6f46c42017-10-26 15:26:16 -0400319 return fold_expr(self, expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400320 }
321 node => paren(self, node),
322 };
323
324 Expr {
325 node: kind,
326 ..expr
327 }
328 }
329
330 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
331 match stmt {
332 // Don't wrap toplevel expressions in statements.
333 Stmt::Expr(e) => {
Nika Layzella6f46c42017-10-26 15:26:16 -0400334 Stmt::Expr(Box::new(fold_expr(self, *e)))
Michael Layzell53fc31a2017-06-07 09:21:53 -0400335 }
336 Stmt::Semi(e, semi) => {
Nika Layzella6f46c42017-10-26 15:26:16 -0400337 Stmt::Semi(Box::new(fold_expr(self, *e)), semi)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400338 }
339 s => s,
340 }
341 }
342
343 // We don't want to look at expressions that might appear in patterns or
344 // types yet. We'll look into comparing those in the future. For now
345 // focus on expressions appearing in other places.
346 fn fold_pat(&mut self, pat: Pat) -> Pat {
347 pat
348 }
349
David Tolnayfd6bf5c2017-11-12 09:41:14 -0800350 fn fold_type(&mut self, ty: Type) -> Type {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400351 ty
352 }
353 }
354
355 let mut folder = BracketsFolder;
356 folder.fold_expr(syn_expr)
357}
358
359/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700360fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400361 use synom::delimited::Delimited;
362 use syn::*;
363 use syn::fold::*;
364
365 struct CollectExprsFolder(Vec<Expr>);
366 impl Folder for CollectExprsFolder {
367 fn fold_expr(&mut self, expr: Expr) -> Expr {
368 self.0.push(expr);
369
370 Expr {
371 node: ExprKind::Tup(ExprTup {
372 args: Delimited::new(),
373 paren_token: tokens::Paren::default(),
374 lone_comma: None
375 }),
376 attrs: vec![],
377 }
378 }
379 }
380
381 let mut folder = CollectExprsFolder(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700382 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400383 folder.0
384}