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