blob: a5128fc9c349c18d3d29d4c8eaa005e12428410f [file] [log] [blame]
Michael Layzell53fc31a2017-06-07 09:21:53 -04001#![cfg(all(feature = "full", feature = "fold"))]
2
3//! The tests in this module do the following:
4//!
5//! 1. Parse a given expression in both `syn` and `syntex`.
6//! 2. Fold over the expression adding brackets around each subexpression (with
7//! some complications - see the `syn_brackets` and `syntex_brackets`
8//! methods).
9//! 3. Serialize the `syn` expression back into a string, and re-parse it with
10//! `syntex`.
11//! 4. Respan all of the expressions, replacing the spans with the default spans.
12//! 5. Compare the expressions with one another, if they are not equal fail.
13
14#[macro_use]
15extern crate quote;
16extern crate syn;
17extern crate synom;
18extern crate syntex_pos;
19extern crate syntex_syntax;
20extern crate walkdir;
21extern crate proc_macro2;
22
23use syntex_syntax::ast;
24use syntex_syntax::ptr::P;
25
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();
77 let abort_after = common::abort_after();
78 if abort_after == 0 {
79 panic!("Skipping all precedence tests");
80 }
81
82 let mut passed = 0;
83 let mut failed = 0;
84
85 let walk = WalkDir::new("tests/rust").sort_by(|a, b| a.cmp(b));
86 for entry in walk.into_iter().filter_entry(common::base_dir_filter) {
87 let entry = entry.unwrap();
88
89 let path = entry.path();
90 if path.is_dir() {
91 continue;
92 }
93
94 let mut file = File::open(path).unwrap();
95 let mut content = String::new();
96 file.read_to_string(&mut content).unwrap();
97
David Tolnayc7a5d3d2017-06-04 12:11:05 -070098 let (l_passed, l_failed) = match syn::parse_file(&content) {
99 Ok(file) => {
100 let exprs = collect_exprs(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400101 test_expressions(exprs)
102 }
103 Err(msg) => {
104 errorf!("syn failed to parse\n{:?}\n", msg);
105 failed += 1;
106 (0, 1)
107 }
108 };
109
110 passed += l_passed;
111 failed += l_failed;
112
113 errorf!("=== {}: {} passed | {} failed\n", path.display(), l_passed, l_failed);
114
115 if failed >= abort_after {
116 errorf!("Aborting Immediately due to ABORT_AFTER_FAILURE\n");
117 break;
118 }
119 }
120
121 errorf!("\n===== Precedence Test Results =====\n");
122 errorf!("{} passed | {} failed\n", passed, failed);
123
124
125 if failed > 0 {
126 panic!("{} failures", failed);
127 }
128}
129
130fn test_expressions(exprs: Vec<syn::Expr>) -> (u32, u32) {
131 let mut passed = 0;
132 let mut failed = 0;
133
134 for expr in exprs {
135 let raw = quote!(#expr).to_string();
136
137 let syntex_ast = if let Some(e) = syntex_parse_and_rewrite(&raw) {
138 e
139 } else {
140 failed += 1;
141 errorf!("\nFAIL - syntex failed to parse raw\n");
142 continue;
143 };
144
145 let syn_expr = syn_brackets(expr);
146 let syn_ast = if let Some(e) = parse::syntex_expr(&quote!(#syn_expr).to_string()) {
147 e
148 } else {
149 failed += 1;
150 errorf!("\nFAIL - syntex failed to parse bracketed\n");
151 continue;
152 };
153
154 let syn_ast = respan::respan_expr(syn_ast);
155 let syntex_ast = respan::respan_expr(syntex_ast);
156
157 if syn_ast == syntex_ast {
158 passed += 1;
159 } else {
160 failed += 1;
161 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, syntex_ast);
162 }
163 }
164
165 (passed, failed)
166}
167
168fn syntex_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
169 parse::syntex_expr(input).and_then(|e| syntex_brackets(e))
170}
171
172/// Wrap every expression which is not already wrapped in parens with parens, to
173/// reveal the precidence of the parsed expressions, and produce a stringified form
174/// of the resulting expression.
175///
176/// This method operates on syntex objects.
177fn syntex_brackets(syntex_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
178 use syntex_syntax::ast::{Expr, ExprKind, Mac, Stmt, StmtKind, Pat, Ty, Field};
179 use syntex_syntax::fold::{self, Folder};
180 use syntex_syntax::util::ThinVec;
181 use syntex_syntax::util::small_vector::SmallVector;
182 use syntex_syntax::ext::quote::rt::DUMMY_SP;
183 use syntex_syntax::codemap;
184
185 fn expr(node: ExprKind) -> P<Expr> {
186 P(Expr {
187 id: ast::DUMMY_NODE_ID,
188 node: node,
189 span: DUMMY_SP,
190 attrs: ThinVec::new(),
191 })
192 }
193
194 struct BracketsFolder {
195 failed: bool,
196 };
197 impl Folder for BracketsFolder {
198 fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
199 e.map(|e| {
200 Expr {
201 node: match e.node {
202 ExprKind::Paren(inner) => {
203 ExprKind::Paren(inner.map(|e| {
204 fold::noop_fold_expr(e, self)
205 }))
206 }
207 ExprKind::If(..) |
208 ExprKind::Block(..) |
209 ExprKind::IfLet(..) => {
210 return fold::noop_fold_expr(e, self);
211 }
212 node => {
213 ExprKind::Paren(expr(node).map(|e| {
214 fold::noop_fold_expr(e, self)
215 }))
216 }
217 },
218 ..e
219 }
220 })
221 }
222
223 fn fold_field(&mut self, f: Field) -> Field {
224 Field {
225 ident: codemap::respan(f.ident.span, self.fold_ident(f.ident.node)),
226 expr: if f.is_shorthand {
227 f.expr.map(|e| fold::noop_fold_expr(e, self))
228 } else {
229 self.fold_expr(f.expr)
230 },
231 span: self.new_span(f.span),
232 is_shorthand: f.is_shorthand,
233 attrs: fold::fold_thin_attrs(f.attrs, self),
234 }
235 }
236
237 // We don't want to look at expressions that might appear in patterns or
238 // types yet. We'll look into comparing those in the future. For now
239 // focus on expressions appearing in other places.
240 fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
241 pat
242 }
243
244 fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
245 ty
246 }
247
248 fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
249 let node = match stmt.node {
250 // Don't wrap toplevel expressions in statements.
251 StmtKind::Expr(e) => {
252 StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self)))
253 }
254 StmtKind::Semi(e) => {
255 StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self)))
256 }
257 s => s,
258 };
259
260 SmallVector::one(Stmt {
261 node: node,
262 ..stmt
263 })
264 }
265
266 fn fold_mac(&mut self, mac: Mac) -> Mac {
267 // By default when folding over macros, syntex panics. This is
268 // because it's usually not what you want, you want to run after
269 // macro expansion. We do want to do that (syn doesn't do macro
270 // expansion), so we implement fold_mac to just return the macro
271 // unchanged.
272 mac
273 }
274 }
275
276 let mut folder = BracketsFolder {
277 failed: false,
278 };
279 let e = folder.fold_expr(syntex_expr);
280 if folder.failed {
281 None
282 } else {
283 Some(e)
284 }
285}
286
287/// Wrap every expression which is not already wrapped in parens with parens, to
288/// reveal the precidence of the parsed expressions, and produce a stringified form
289/// of the resulting expression.
290fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
291 use syn::*;
292 use syn::fold::*;
293
294 fn paren(folder: &mut BracketsFolder, node: ExprKind) -> ExprKind {
295 ExprKind::Paren(ExprParen {
296 expr: Box::new(noop_fold_expr(folder, Expr {
297 node: node,
298 attrs: vec![],
299 })),
300 paren_token: tokens::Paren::default(),
301 })
302 }
303
304 struct BracketsFolder;
305 impl Folder for BracketsFolder {
306 fn fold_expr(&mut self, expr: Expr) -> Expr {
307 let kind = match expr.node {
308 ExprKind::Group(_) => unreachable!(),
309 ExprKind::Paren(p) => paren(self, p.expr.node),
310 ExprKind::If(..) |
311 ExprKind::Block(..) |
312 ExprKind::IfLet(..) => {
313 return noop_fold_expr(self, expr);
314 }
315 node => paren(self, node),
316 };
317
318 Expr {
319 node: kind,
320 ..expr
321 }
322 }
323
324 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
325 match stmt {
326 // Don't wrap toplevel expressions in statements.
327 Stmt::Expr(e) => {
328 Stmt::Expr(Box::new(noop_fold_expr(self, *e)))
329 }
330 Stmt::Semi(e, semi) => {
331 Stmt::Semi(Box::new(noop_fold_expr(self, *e)), semi)
332 }
333 s => s,
334 }
335 }
336
337 // We don't want to look at expressions that might appear in patterns or
338 // types yet. We'll look into comparing those in the future. For now
339 // focus on expressions appearing in other places.
340 fn fold_pat(&mut self, pat: Pat) -> Pat {
341 pat
342 }
343
344 fn fold_ty(&mut self, ty: Ty) -> Ty {
345 ty
346 }
347 }
348
349 let mut folder = BracketsFolder;
350 folder.fold_expr(syn_expr)
351}
352
353/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700354fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400355 use synom::delimited::Delimited;
356 use syn::*;
357 use syn::fold::*;
358
359 struct CollectExprsFolder(Vec<Expr>);
360 impl Folder for CollectExprsFolder {
361 fn fold_expr(&mut self, expr: Expr) -> Expr {
362 self.0.push(expr);
363
364 Expr {
365 node: ExprKind::Tup(ExprTup {
366 args: Delimited::new(),
367 paren_token: tokens::Paren::default(),
368 lone_comma: None
369 }),
370 attrs: vec![],
371 }
372 }
373 }
374
375 let mut folder = CollectExprsFolder(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700376 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400377 folder.0
378}