blob: 1066b92cc4d16bbe561554c0c9aa09866158235d [file] [log] [blame]
David Tolnay55535012018-01-05 16:39:23 -08001// Copyright 2018 Syn Developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
Michael Layzell53fc31a2017-06-07 09:21:53 -04009#![cfg(all(feature = "full", feature = "fold"))]
Nika Layzella2a1a4a2017-11-19 11:33:17 -050010#![feature(rustc_private)]
11
Michael Layzell53fc31a2017-06-07 09:21:53 -040012//! The tests in this module do the following:
13//!
David Tolnaycfa5cc02017-11-13 01:05:11 -080014//! 1. Parse a given expression in both `syn` and `libsyntax`.
Michael Layzell53fc31a2017-06-07 09:21:53 -040015//! 2. Fold over the expression adding brackets around each subexpression (with
David Tolnaycfa5cc02017-11-13 01:05:11 -080016//! some complications - see the `syn_brackets` and `libsyntax_brackets`
Michael Layzell53fc31a2017-06-07 09:21:53 -040017//! methods).
18//! 3. Serialize the `syn` expression back into a string, and re-parse it with
David Tolnaycfa5cc02017-11-13 01:05:11 -080019//! `libsyntax`.
Michael Layzell53fc31a2017-06-07 09:21:53 -040020//! 4. Respan all of the expressions, replacing the spans with the default spans.
21//! 5. Compare the expressions with one another, if they are not equal fail.
22
23#[macro_use]
24extern crate quote;
David Tolnayee97dbf2017-11-19 14:24:38 -080025extern crate rayon;
Michael Layzell53fc31a2017-06-07 09:21:53 -040026extern crate syn;
David Tolnaycfa5cc02017-11-13 01:05:11 -080027extern crate syntax;
Michael Layzell53fc31a2017-06-07 09:21:53 -040028extern crate walkdir;
Michael Layzell53fc31a2017-06-07 09:21:53 -040029
David Tolnay51382052017-12-27 13:46:21 -050030use rayon::iter::{IntoParallelIterator, ParallelIterator};
David Tolnaycfa5cc02017-11-13 01:05:11 -080031use syntax::ast;
32use syntax::ptr::P;
Igor Gnatenko951a52b2018-03-12 10:33:33 +010033use walkdir::{DirEntry, WalkDir};
David Tolnayee97dbf2017-11-19 14:24:38 -080034
35use std::fs::File;
36use std::io::Read;
David Tolnay3eaf7d82017-12-17 23:14:52 -080037use std::process;
David Tolnayee97dbf2017-11-19 14:24:38 -080038use std::sync::atomic::{AtomicUsize, Ordering};
Michael Layzell53fc31a2017-06-07 09:21:53 -040039
David Tolnay51382052017-12-27 13:46:21 -050040use common::{parse, respan};
Michael Layzell53fc31a2017-06-07 09:21:53 -040041
Michael Layzell53fc31a2017-06-07 09:21:53 -040042#[macro_use]
David Tolnaydd125562017-12-31 02:16:22 -050043mod macros;
44
45#[allow(dead_code)]
Michael Layzell53fc31a2017-06-07 09:21:53 -040046mod common;
47
48/// Test some pre-set expressions chosen by us.
49#[test]
50fn test_simple_precedence() {
51 const EXPRS: &[&str] = &[
52 "1 + 2 * 3 + 4",
53 "1 + 2 * ( 3 + 4 )",
54 "{ for i in r { } *some_ptr += 1; }",
55 "{ loop { break 5; } }",
56 "{ if true { () }.mthd() }",
Nika Layzell3aa0dc72017-12-04 13:41:28 -050057 "{ for i in unsafe { 20 } { } }",
Michael Layzell53fc31a2017-06-07 09:21:53 -040058 ];
59
60 let mut failed = 0;
61
62 for input in EXPRS {
63 let expr = if let Some(expr) = parse::syn_expr(input) {
64 expr
65 } else {
66 failed += 1;
67 continue;
68 };
69
70 let pf = match test_expressions(vec![expr]) {
71 (1, 0) => "passed",
72 (0, 1) => {
73 failed += 1;
74 "failed"
75 }
76 _ => unreachable!(),
77 };
78 errorf!("=== {}: {}\n", input, pf);
79 }
80
81 if failed > 0 {
82 panic!("Failed {} tests", failed);
83 }
84}
85
86/// Test expressions from rustc, like in `test_round_trip`.
87#[test]
88fn test_rustc_precedence() {
Michael Layzell53fc31a2017-06-07 09:21:53 -040089 common::check_min_stack();
Alex Crichton86374772017-07-07 20:39:28 -070090 common::clone_rust();
Michael Layzell53fc31a2017-06-07 09:21:53 -040091 let abort_after = common::abort_after();
92 if abort_after == 0 {
93 panic!("Skipping all precedence tests");
94 }
95
David Tolnayee97dbf2017-11-19 14:24:38 -080096 let passed = AtomicUsize::new(0);
97 let failed = AtomicUsize::new(0);
Michael Layzell53fc31a2017-06-07 09:21:53 -040098
David Tolnayee97dbf2017-11-19 14:24:38 -080099 WalkDir::new("tests/rust")
Igor Gnatenko951a52b2018-03-12 10:33:33 +0100100 .sort_by(|a, b| a.file_name().cmp(b.file_name()))
David Tolnayee97dbf2017-11-19 14:24:38 -0800101 .into_iter()
102 .filter_entry(common::base_dir_filter)
103 .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
104 .unwrap()
105 .into_par_iter()
David Tolnay51382052017-12-27 13:46:21 -0500106 .for_each(|entry| {
107 let path = entry.path();
108 if path.is_dir() {
109 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400110 }
David Tolnay51382052017-12-27 13:46:21 -0500111
112 // Our version of `libsyntax` can't parse this tests
David Tolnay65fb5662018-05-20 20:02:28 -0700113 if path
114 .to_str()
David Tolnay51382052017-12-27 13:46:21 -0500115 .unwrap()
116 .ends_with("optional_comma_in_match_arm.rs")
117 {
118 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400119 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400120
David Tolnay51382052017-12-27 13:46:21 -0500121 let mut file = File::open(path).unwrap();
122 let mut content = String::new();
123 file.read_to_string(&mut content).unwrap();
Michael Layzell53fc31a2017-06-07 09:21:53 -0400124
David Tolnay51382052017-12-27 13:46:21 -0500125 let (l_passed, l_failed) = match syn::parse_file(&content) {
126 Ok(file) => {
127 let exprs = collect_exprs(file);
128 test_expressions(exprs)
129 }
130 Err(msg) => {
131 errorf!("syn failed to parse\n{:?}\n", msg);
132 (0, 1)
133 }
134 };
David Tolnayee97dbf2017-11-19 14:24:38 -0800135
David Tolnay51382052017-12-27 13:46:21 -0500136 errorf!(
137 "=== {}: {} passed | {} failed\n",
138 path.display(),
139 l_passed,
140 l_failed
141 );
142
143 passed.fetch_add(l_passed, Ordering::SeqCst);
144 let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);
145
146 if prev_failed + l_failed >= abort_after {
147 process::exit(1);
148 }
149 });
David Tolnayee97dbf2017-11-19 14:24:38 -0800150
151 let passed = passed.load(Ordering::SeqCst);
152 let failed = failed.load(Ordering::SeqCst);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400153
154 errorf!("\n===== Precedence Test Results =====\n");
155 errorf!("{} passed | {} failed\n", passed, failed);
156
Michael Layzell53fc31a2017-06-07 09:21:53 -0400157 if failed > 0 {
158 panic!("{} failures", failed);
159 }
160}
161
David Tolnayee97dbf2017-11-19 14:24:38 -0800162fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400163 let mut passed = 0;
164 let mut failed = 0;
165
David Tolnayeb7d79b2018-03-31 22:52:17 +0200166 syntax::with_globals(|| {
167 for expr in exprs {
168 let raw = quote!(#expr).to_string();
Michael Layzell53fc31a2017-06-07 09:21:53 -0400169
David Tolnayeb7d79b2018-03-31 22:52:17 +0200170 let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
171 e
172 } else {
173 failed += 1;
174 errorf!("\nFAIL - libsyntax failed to parse raw\n");
175 continue;
176 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400177
David Tolnayeb7d79b2018-03-31 22:52:17 +0200178 let syn_expr = syn_brackets(expr);
179 let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
180 e
181 } else {
182 failed += 1;
183 errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
184 continue;
185 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400186
David Tolnayeb7d79b2018-03-31 22:52:17 +0200187 let syn_ast = respan::respan_expr(syn_ast);
188 let libsyntax_ast = respan::respan_expr(libsyntax_ast);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400189
David Tolnayeb7d79b2018-03-31 22:52:17 +0200190 if syn_ast == libsyntax_ast {
191 passed += 1;
192 } else {
193 failed += 1;
194 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
195 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400196 }
David Tolnay5d1a3ee2018-03-17 19:41:36 -0700197 });
Michael Layzell53fc31a2017-06-07 09:21:53 -0400198
199 (passed, failed)
200}
201
David Tolnaycfa5cc02017-11-13 01:05:11 -0800202fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
David Tolnay3cede942017-12-26 12:29:24 -0500203 parse::libsyntax_expr(input).and_then(libsyntax_brackets)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400204}
205
206/// Wrap every expression which is not already wrapped in parens with parens, to
207/// reveal the precidence of the parsed expressions, and produce a stringified form
208/// of the resulting expression.
209///
David Tolnaycfa5cc02017-11-13 01:05:11 -0800210/// This method operates on libsyntax objects.
211fn libsyntax_brackets(libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
David Tolnay51382052017-12-27 13:46:21 -0500212 use syntax::ast::{Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
David Tolnayeb7d79b2018-03-31 22:52:17 +0200213 use syntax::ext::quote::rt::DUMMY_SP;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800214 use syntax::fold::{self, Folder};
David Tolnaycfa5cc02017-11-13 01:05:11 -0800215 use syntax::util::small_vector::SmallVector;
David Tolnay94d2b792018-04-29 12:26:10 -0700216 use syntax::util::ThinVec;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400217
218 fn expr(node: ExprKind) -> P<Expr> {
219 P(Expr {
220 id: ast::DUMMY_NODE_ID,
David Tolnay54bdb4f2018-03-17 13:00:42 -0700221 node,
Michael Layzell53fc31a2017-06-07 09:21:53 -0400222 span: DUMMY_SP,
223 attrs: ThinVec::new(),
224 })
225 }
226
227 struct BracketsFolder {
228 failed: bool,
229 };
230 impl Folder for BracketsFolder {
231 fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
David Tolnay51382052017-12-27 13:46:21 -0500232 e.map(|e| Expr {
233 node: match e.node {
234 ExprKind::Paren(inner) => {
235 ExprKind::Paren(inner.map(|e| fold::noop_fold_expr(e, self)))
236 }
237 ExprKind::If(..) | ExprKind::Block(..) | ExprKind::IfLet(..) => {
238 return fold::noop_fold_expr(e, self);
239 }
240 node => ExprKind::Paren(expr(node).map(|e| fold::noop_fold_expr(e, self))),
241 },
242 ..e
Michael Layzell53fc31a2017-06-07 09:21:53 -0400243 })
244 }
245
246 fn fold_field(&mut self, f: Field) -> Field {
247 Field {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400248 expr: if f.is_shorthand {
249 f.expr.map(|e| fold::noop_fold_expr(e, self))
250 } else {
251 self.fold_expr(f.expr)
252 },
David Tolnaya3174992018-04-06 22:41:59 -0700253 ..f
Michael Layzell53fc31a2017-06-07 09:21:53 -0400254 }
255 }
256
257 // We don't want to look at expressions that might appear in patterns or
258 // types yet. We'll look into comparing those in the future. For now
259 // focus on expressions appearing in other places.
260 fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
261 pat
262 }
263
264 fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
265 ty
266 }
267
268 fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
269 let node = match stmt.node {
270 // Don't wrap toplevel expressions in statements.
David Tolnay51382052017-12-27 13:46:21 -0500271 StmtKind::Expr(e) => StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self))),
272 StmtKind::Semi(e) => StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self))),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400273 s => s,
274 };
275
David Tolnay54bdb4f2018-03-17 13:00:42 -0700276 SmallVector::one(Stmt { node, ..stmt })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400277 }
278
279 fn fold_mac(&mut self, mac: Mac) -> Mac {
David Tolnaycfa5cc02017-11-13 01:05:11 -0800280 // By default when folding over macros, libsyntax panics. This is
Michael Layzell53fc31a2017-06-07 09:21:53 -0400281 // because it's usually not what you want, you want to run after
282 // macro expansion. We do want to do that (syn doesn't do macro
283 // expansion), so we implement fold_mac to just return the macro
284 // unchanged.
285 mac
286 }
287 }
288
David Tolnay51382052017-12-27 13:46:21 -0500289 let mut folder = BracketsFolder { failed: false };
David Tolnaycfa5cc02017-11-13 01:05:11 -0800290 let e = folder.fold_expr(libsyntax_expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400291 if folder.failed {
292 None
293 } else {
294 Some(e)
295 }
296}
297
298/// Wrap every expression which is not already wrapped in parens with parens, to
David Tolnayeb752062018-01-06 13:51:42 -0800299/// reveal the precedence of the parsed expressions, and produce a stringified form
Michael Layzell53fc31a2017-06-07 09:21:53 -0400300/// of the resulting expression.
301fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400302 use syn::fold::*;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200303 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400304
David Tolnayeb752062018-01-06 13:51:42 -0800305 fn paren(folder: &mut ParenthesizeEveryExpr, mut node: Expr) -> Expr {
David Tolnay2ae520a2017-12-29 11:19:50 -0500306 let attrs = node.replace_attrs(Vec::new());
David Tolnay8c91b882017-12-28 23:04:32 -0500307 Expr::Paren(ExprParen {
David Tolnay54bdb4f2018-03-17 13:00:42 -0700308 attrs,
David Tolnay61037c62018-01-05 16:21:03 -0800309 expr: Box::new(fold_expr(folder, node)),
David Tolnay42eaae12017-12-26 23:05:18 -0500310 paren_token: token::Paren::default(),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400311 })
312 }
313
David Tolnayeb752062018-01-06 13:51:42 -0800314 struct ParenthesizeEveryExpr;
315 impl Fold for ParenthesizeEveryExpr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400316 fn fold_expr(&mut self, expr: Expr) -> Expr {
David Tolnay8c91b882017-12-28 23:04:32 -0500317 match expr {
318 Expr::Group(_) => unreachable!(),
319 Expr::Paren(p) => paren(self, *p.expr),
David Tolnay61037c62018-01-05 16:21:03 -0800320 Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::IfLet(..) => {
David Tolnay3bc597f2017-12-31 02:31:11 -0500321 fold_expr(self, expr)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400322 }
323 node => paren(self, node),
David Tolnay8c91b882017-12-28 23:04:32 -0500324 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400325 }
326
327 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
328 match stmt {
329 // Don't wrap toplevel expressions in statements.
David Tolnay1f0b7b82018-01-06 16:07:14 -0800330 Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
331 Stmt::Semi(e, semi) => Stmt::Semi(fold_expr(self, e), semi),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400332 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
David Tolnayfd6bf5c2017-11-12 09:41:14 -0800343 fn fold_type(&mut self, ty: Type) -> Type {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400344 ty
345 }
346 }
347
David Tolnayeb752062018-01-06 13:51:42 -0800348 let mut folder = ParenthesizeEveryExpr;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400349 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 syn::fold::*;
David Tolnayf2cfd722017-12-31 18:02:51 -0500355 use syn::punctuated::Punctuated;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200356 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400357
David Tolnayeb752062018-01-06 13:51:42 -0800358 struct CollectExprs(Vec<Expr>);
359 impl Fold for CollectExprs {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400360 fn fold_expr(&mut self, expr: Expr) -> Expr {
361 self.0.push(expr);
362
David Tolnay8c91b882017-12-28 23:04:32 -0500363 Expr::Tuple(ExprTuple {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400364 attrs: vec![],
David Tolnayf2cfd722017-12-31 18:02:51 -0500365 elems: Punctuated::new(),
David Tolnay8c91b882017-12-28 23:04:32 -0500366 paren_token: token::Paren::default(),
367 })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400368 }
369 }
370
David Tolnayeb752062018-01-06 13:51:42 -0800371 let mut folder = CollectExprs(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700372 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400373 folder.0
374}