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