blob: b058c392dfa9e20820a80c76e091797c76154105 [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;
Michael Layzell53fc31a2017-06-07 09:21:53 -040018extern crate syntex_syntax;
19extern crate walkdir;
Michael Layzell53fc31a2017-06-07 09:21:53 -040020
21use syntex_syntax::ast;
22use syntex_syntax::ptr::P;
23
24use common::{respan, parse};
25
26#[allow(dead_code)]
27#[macro_use]
28mod common;
29
30/// Test some pre-set expressions chosen by us.
31#[test]
32fn test_simple_precedence() {
33 const EXPRS: &[&str] = &[
34 "1 + 2 * 3 + 4",
35 "1 + 2 * ( 3 + 4 )",
36 "{ for i in r { } *some_ptr += 1; }",
37 "{ loop { break 5; } }",
38 "{ if true { () }.mthd() }",
39 ];
40
41 let mut failed = 0;
42
43 for input in EXPRS {
44 let expr = if let Some(expr) = parse::syn_expr(input) {
45 expr
46 } else {
47 failed += 1;
48 continue;
49 };
50
51 let pf = match test_expressions(vec![expr]) {
52 (1, 0) => "passed",
53 (0, 1) => {
54 failed += 1;
55 "failed"
56 }
57 _ => unreachable!(),
58 };
59 errorf!("=== {}: {}\n", input, pf);
60 }
61
62 if failed > 0 {
63 panic!("Failed {} tests", failed);
64 }
65}
66
67/// Test expressions from rustc, like in `test_round_trip`.
68#[test]
69fn test_rustc_precedence() {
70 use walkdir::{WalkDir, WalkDirIterator};
71 use std::fs::File;
72 use std::io::Read;
73
74 common::check_min_stack();
Alex Crichton86374772017-07-07 20:39:28 -070075 common::clone_rust();
Michael Layzell53fc31a2017-06-07 09:21:53 -040076 let abort_after = common::abort_after();
77 if abort_after == 0 {
78 panic!("Skipping all precedence tests");
79 }
80
81 let mut passed = 0;
82 let mut failed = 0;
83
84 let walk = WalkDir::new("tests/rust").sort_by(|a, b| a.cmp(b));
85 for entry in walk.into_iter().filter_entry(common::base_dir_filter) {
86 let entry = entry.unwrap();
87
88 let path = entry.path();
89 if path.is_dir() {
90 continue;
91 }
92
93 let mut file = File::open(path).unwrap();
94 let mut content = String::new();
95 file.read_to_string(&mut content).unwrap();
96
David Tolnayc7a5d3d2017-06-04 12:11:05 -070097 let (l_passed, l_failed) = match syn::parse_file(&content) {
98 Ok(file) => {
99 let exprs = collect_exprs(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400100 test_expressions(exprs)
101 }
102 Err(msg) => {
103 errorf!("syn failed to parse\n{:?}\n", msg);
104 failed += 1;
105 (0, 1)
106 }
107 };
108
109 passed += l_passed;
110 failed += l_failed;
111
112 errorf!("=== {}: {} passed | {} failed\n", path.display(), l_passed, l_failed);
113
114 if failed >= abort_after {
115 errorf!("Aborting Immediately due to ABORT_AFTER_FAILURE\n");
116 break;
117 }
118 }
119
120 errorf!("\n===== Precedence Test Results =====\n");
121 errorf!("{} passed | {} failed\n", passed, failed);
122
123
124 if failed > 0 {
125 panic!("{} failures", failed);
126 }
127}
128
129fn test_expressions(exprs: Vec<syn::Expr>) -> (u32, u32) {
130 let mut passed = 0;
131 let mut failed = 0;
132
133 for expr in exprs {
134 let raw = quote!(#expr).to_string();
135
136 let syntex_ast = if let Some(e) = syntex_parse_and_rewrite(&raw) {
137 e
138 } else {
139 failed += 1;
140 errorf!("\nFAIL - syntex failed to parse raw\n");
141 continue;
142 };
143
144 let syn_expr = syn_brackets(expr);
145 let syn_ast = if let Some(e) = parse::syntex_expr(&quote!(#syn_expr).to_string()) {
146 e
147 } else {
148 failed += 1;
149 errorf!("\nFAIL - syntex failed to parse bracketed\n");
150 continue;
151 };
152
153 let syn_ast = respan::respan_expr(syn_ast);
154 let syntex_ast = respan::respan_expr(syntex_ast);
155
156 if syn_ast == syntex_ast {
157 passed += 1;
158 } else {
159 failed += 1;
160 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, syntex_ast);
161 }
162 }
163
164 (passed, failed)
165}
166
167fn syntex_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
168 parse::syntex_expr(input).and_then(|e| syntex_brackets(e))
169}
170
171/// Wrap every expression which is not already wrapped in parens with parens, to
172/// reveal the precidence of the parsed expressions, and produce a stringified form
173/// of the resulting expression.
174///
175/// This method operates on syntex objects.
176fn syntex_brackets(syntex_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
177 use syntex_syntax::ast::{Expr, ExprKind, Mac, Stmt, StmtKind, Pat, Ty, Field};
178 use syntex_syntax::fold::{self, Folder};
179 use syntex_syntax::util::ThinVec;
180 use syntex_syntax::util::small_vector::SmallVector;
181 use syntex_syntax::ext::quote::rt::DUMMY_SP;
182 use syntex_syntax::codemap;
183
184 fn expr(node: ExprKind) -> P<Expr> {
185 P(Expr {
186 id: ast::DUMMY_NODE_ID,
187 node: node,
188 span: DUMMY_SP,
189 attrs: ThinVec::new(),
190 })
191 }
192
193 struct BracketsFolder {
194 failed: bool,
195 };
196 impl Folder for BracketsFolder {
197 fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
198 e.map(|e| {
199 Expr {
200 node: match e.node {
201 ExprKind::Paren(inner) => {
202 ExprKind::Paren(inner.map(|e| {
203 fold::noop_fold_expr(e, self)
204 }))
205 }
206 ExprKind::If(..) |
207 ExprKind::Block(..) |
208 ExprKind::IfLet(..) => {
209 return fold::noop_fold_expr(e, self);
210 }
211 node => {
212 ExprKind::Paren(expr(node).map(|e| {
213 fold::noop_fold_expr(e, self)
214 }))
215 }
216 },
217 ..e
218 }
219 })
220 }
221
222 fn fold_field(&mut self, f: Field) -> Field {
223 Field {
224 ident: codemap::respan(f.ident.span, self.fold_ident(f.ident.node)),
225 expr: if f.is_shorthand {
226 f.expr.map(|e| fold::noop_fold_expr(e, self))
227 } else {
228 self.fold_expr(f.expr)
229 },
230 span: self.new_span(f.span),
231 is_shorthand: f.is_shorthand,
232 attrs: fold::fold_thin_attrs(f.attrs, self),
233 }
234 }
235
236 // We don't want to look at expressions that might appear in patterns or
237 // types yet. We'll look into comparing those in the future. For now
238 // focus on expressions appearing in other places.
239 fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
240 pat
241 }
242
243 fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
244 ty
245 }
246
247 fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
248 let node = match stmt.node {
249 // Don't wrap toplevel expressions in statements.
250 StmtKind::Expr(e) => {
251 StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self)))
252 }
253 StmtKind::Semi(e) => {
254 StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self)))
255 }
256 s => s,
257 };
258
259 SmallVector::one(Stmt {
260 node: node,
261 ..stmt
262 })
263 }
264
265 fn fold_mac(&mut self, mac: Mac) -> Mac {
266 // By default when folding over macros, syntex panics. This is
267 // because it's usually not what you want, you want to run after
268 // macro expansion. We do want to do that (syn doesn't do macro
269 // expansion), so we implement fold_mac to just return the macro
270 // unchanged.
271 mac
272 }
273 }
274
275 let mut folder = BracketsFolder {
276 failed: false,
277 };
278 let e = folder.fold_expr(syntex_expr);
279 if folder.failed {
280 None
281 } else {
282 Some(e)
283 }
284}
285
286/// Wrap every expression which is not already wrapped in parens with parens, to
287/// reveal the precidence of the parsed expressions, and produce a stringified form
288/// of the resulting expression.
289fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
290 use syn::*;
291 use syn::fold::*;
292
293 fn paren(folder: &mut BracketsFolder, node: ExprKind) -> ExprKind {
294 ExprKind::Paren(ExprParen {
295 expr: Box::new(noop_fold_expr(folder, Expr {
296 node: node,
297 attrs: vec![],
298 })),
299 paren_token: tokens::Paren::default(),
300 })
301 }
302
303 struct BracketsFolder;
304 impl Folder for BracketsFolder {
305 fn fold_expr(&mut self, expr: Expr) -> Expr {
306 let kind = match expr.node {
307 ExprKind::Group(_) => unreachable!(),
308 ExprKind::Paren(p) => paren(self, p.expr.node),
309 ExprKind::If(..) |
310 ExprKind::Block(..) |
311 ExprKind::IfLet(..) => {
312 return noop_fold_expr(self, expr);
313 }
314 node => paren(self, node),
315 };
316
317 Expr {
318 node: kind,
319 ..expr
320 }
321 }
322
323 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
324 match stmt {
325 // Don't wrap toplevel expressions in statements.
326 Stmt::Expr(e) => {
327 Stmt::Expr(Box::new(noop_fold_expr(self, *e)))
328 }
329 Stmt::Semi(e, semi) => {
330 Stmt::Semi(Box::new(noop_fold_expr(self, *e)), semi)
331 }
332 s => s,
333 }
334 }
335
336 // We don't want to look at expressions that might appear in patterns or
337 // types yet. We'll look into comparing those in the future. For now
338 // focus on expressions appearing in other places.
339 fn fold_pat(&mut self, pat: Pat) -> Pat {
340 pat
341 }
342
343 fn fold_ty(&mut self, ty: Ty) -> Ty {
344 ty
345 }
346 }
347
348 let mut folder = BracketsFolder;
349 folder.fold_expr(syn_expr)
350}
351
352/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700353fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400354 use synom::delimited::Delimited;
355 use syn::*;
356 use syn::fold::*;
357
358 struct CollectExprsFolder(Vec<Expr>);
359 impl Folder for CollectExprsFolder {
360 fn fold_expr(&mut self, expr: Expr) -> Expr {
361 self.0.push(expr);
362
363 Expr {
364 node: ExprKind::Tup(ExprTup {
365 args: Delimited::new(),
366 paren_token: tokens::Paren::default(),
367 lone_comma: None
368 }),
369 attrs: vec![],
370 }
371 }
372 }
373
374 let mut folder = CollectExprsFolder(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700375 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400376 folder.0
377}