blob: d4d086d3f92d411f6568615f9fdaf93746678ffb [file] [log] [blame]
David Tolnayecd024d2018-07-21 09:07:56 -07001#![recursion_limit = "1024"]
Nika Layzella2a1a4a2017-11-19 11:33:17 -05002#![feature(rustc_private)]
3
Michael Layzell53fc31a2017-06-07 09:21:53 -04004//! The tests in this module do the following:
5//!
David Tolnaycfa5cc02017-11-13 01:05:11 -08006//! 1. Parse a given expression in both `syn` and `libsyntax`.
Michael Layzell53fc31a2017-06-07 09:21:53 -04007//! 2. Fold over the expression adding brackets around each subexpression (with
David Tolnaycfa5cc02017-11-13 01:05:11 -08008//! some complications - see the `syn_brackets` and `libsyntax_brackets`
Michael Layzell53fc31a2017-06-07 09:21:53 -04009//! methods).
10//! 3. Serialize the `syn` expression back into a string, and re-parse it with
David Tolnaycfa5cc02017-11-13 01:05:11 -080011//! `libsyntax`.
David Tolnay0ccb6d12018-08-14 22:43:00 -070012//! 4. Respan all of the expressions, replacing the spans with the default
13//! spans.
Michael Layzell53fc31a2017-06-07 09:21:53 -040014//! 5. Compare the expressions with one another, if they are not equal fail.
15
16#[macro_use]
17extern crate quote;
David Tolnayee97dbf2017-11-19 14:24:38 -080018extern crate rayon;
David Tolnayd1c31cc2018-08-24 14:47:15 -040019extern crate regex;
David Tolnayc8659922018-08-14 22:40:50 -070020extern crate rustc_data_structures;
David Tolnay0eab7d92018-09-26 22:10:40 -070021#[macro_use]
22extern crate smallvec;
Michael Layzell53fc31a2017-06-07 09:21:53 -040023extern crate syn;
David Tolnaycfa5cc02017-11-13 01:05:11 -080024extern crate syntax;
Michael Layzell53fc31a2017-06-07 09:21:53 -040025extern crate walkdir;
Michael Layzell53fc31a2017-06-07 09:21:53 -040026
David Tolnayc3f98562018-11-02 08:55:05 -070027mod features;
28
David Tolnay51382052017-12-27 13:46:21 -050029use rayon::iter::{IntoParallelIterator, ParallelIterator};
David Tolnayd1c31cc2018-08-24 14:47:15 -040030use regex::Regex;
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 Tolnayecd024d2018-07-21 09:07:56 -070040use common::eq::SpanlessEq;
41use common::parse;
Michael Layzell53fc31a2017-06-07 09:21:53 -040042
Michael Layzell53fc31a2017-06-07 09:21:53 -040043#[macro_use]
David Tolnaydd125562017-12-31 02:16:22 -050044mod macros;
45
46#[allow(dead_code)]
Michael Layzell53fc31a2017-06-07 09:21:53 -040047mod common;
48
49/// Test some pre-set expressions chosen by us.
50#[test]
51fn test_simple_precedence() {
52 const EXPRS: &[&str] = &[
53 "1 + 2 * 3 + 4",
54 "1 + 2 * ( 3 + 4 )",
55 "{ for i in r { } *some_ptr += 1; }",
56 "{ loop { break 5; } }",
57 "{ if true { () }.mthd() }",
Nika Layzell3aa0dc72017-12-04 13:41:28 -050058 "{ for i in unsafe { 20 } { } }",
Michael Layzell53fc31a2017-06-07 09:21:53 -040059 ];
60
61 let mut failed = 0;
62
63 for input in EXPRS {
64 let expr = if let Some(expr) = parse::syn_expr(input) {
65 expr
66 } else {
67 failed += 1;
68 continue;
69 };
70
71 let pf = match test_expressions(vec![expr]) {
72 (1, 0) => "passed",
73 (0, 1) => {
74 failed += 1;
75 "failed"
76 }
77 _ => unreachable!(),
78 };
79 errorf!("=== {}: {}\n", input, pf);
80 }
81
82 if failed > 0 {
83 panic!("Failed {} tests", failed);
84 }
85}
86
87/// Test expressions from rustc, like in `test_round_trip`.
88#[test]
cad97286b19f2019-01-23 23:08:25 -050089#[cfg_attr(target_os = "windows", ignore = "requires nix .sh")]
Michael Layzell53fc31a2017-06-07 09:21:53 -040090fn test_rustc_precedence() {
Alex Crichton86374772017-07-07 20:39:28 -070091 common::clone_rust();
Michael Layzell53fc31a2017-06-07 09:21:53 -040092 let abort_after = common::abort_after();
93 if abort_after == 0 {
94 panic!("Skipping all precedence tests");
95 }
96
David Tolnayee97dbf2017-11-19 14:24:38 -080097 let passed = AtomicUsize::new(0);
98 let failed = AtomicUsize::new(0);
Michael Layzell53fc31a2017-06-07 09:21:53 -040099
David Tolnayd1c31cc2018-08-24 14:47:15 -0400100 // 2018 edition is hard
101 let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap();
102
David Tolnayee97dbf2017-11-19 14:24:38 -0800103 WalkDir::new("tests/rust")
Igor Gnatenko951a52b2018-03-12 10:33:33 +0100104 .sort_by(|a, b| a.file_name().cmp(b.file_name()))
David Tolnayee97dbf2017-11-19 14:24:38 -0800105 .into_iter()
106 .filter_entry(common::base_dir_filter)
107 .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
108 .unwrap()
109 .into_par_iter()
David Tolnay51382052017-12-27 13:46:21 -0500110 .for_each(|entry| {
111 let path = entry.path();
112 if path.is_dir() {
113 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400114 }
David Tolnay51382052017-12-27 13:46:21 -0500115
116 // Our version of `libsyntax` can't parse this tests
David Tolnay65fb5662018-05-20 20:02:28 -0700117 if path
118 .to_str()
David Tolnay51382052017-12-27 13:46:21 -0500119 .unwrap()
120 .ends_with("optional_comma_in_match_arm.rs")
121 {
122 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400123 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400124
David Tolnay51382052017-12-27 13:46:21 -0500125 let mut file = File::open(path).unwrap();
126 let mut content = String::new();
127 file.read_to_string(&mut content).unwrap();
David Tolnayd1c31cc2018-08-24 14:47:15 -0400128 let content = edition_regex.replace_all(&content, "_$0");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400129
David Tolnay51382052017-12-27 13:46:21 -0500130 let (l_passed, l_failed) = match syn::parse_file(&content) {
131 Ok(file) => {
132 let exprs = collect_exprs(file);
133 test_expressions(exprs)
134 }
135 Err(msg) => {
136 errorf!("syn failed to parse\n{:?}\n", msg);
137 (0, 1)
138 }
139 };
David Tolnayee97dbf2017-11-19 14:24:38 -0800140
David Tolnay51382052017-12-27 13:46:21 -0500141 errorf!(
142 "=== {}: {} passed | {} failed\n",
143 path.display(),
144 l_passed,
145 l_failed
146 );
147
148 passed.fetch_add(l_passed, Ordering::SeqCst);
149 let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);
150
151 if prev_failed + l_failed >= abort_after {
152 process::exit(1);
153 }
154 });
David Tolnayee97dbf2017-11-19 14:24:38 -0800155
156 let passed = passed.load(Ordering::SeqCst);
157 let failed = failed.load(Ordering::SeqCst);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400158
159 errorf!("\n===== Precedence Test Results =====\n");
160 errorf!("{} passed | {} failed\n", passed, failed);
161
Michael Layzell53fc31a2017-06-07 09:21:53 -0400162 if failed > 0 {
163 panic!("{} failures", failed);
164 }
165}
166
David Tolnayee97dbf2017-11-19 14:24:38 -0800167fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400168 let mut passed = 0;
169 let mut failed = 0;
170
David Tolnayeb7d79b2018-03-31 22:52:17 +0200171 syntax::with_globals(|| {
172 for expr in exprs {
173 let raw = quote!(#expr).to_string();
Michael Layzell53fc31a2017-06-07 09:21:53 -0400174
David Tolnayeb7d79b2018-03-31 22:52:17 +0200175 let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
176 e
177 } else {
178 failed += 1;
179 errorf!("\nFAIL - libsyntax failed to parse raw\n");
180 continue;
181 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400182
David Tolnayeb7d79b2018-03-31 22:52:17 +0200183 let syn_expr = syn_brackets(expr);
184 let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
185 e
186 } else {
187 failed += 1;
188 errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
189 continue;
190 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400191
David Tolnayecd024d2018-07-21 09:07:56 -0700192 if SpanlessEq::eq(&syn_ast, &libsyntax_ast) {
David Tolnayeb7d79b2018-03-31 22:52:17 +0200193 passed += 1;
194 } else {
195 failed += 1;
196 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
197 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400198 }
David Tolnay5d1a3ee2018-03-17 19:41:36 -0700199 });
Michael Layzell53fc31a2017-06-07 09:21:53 -0400200
201 (passed, failed)
202}
203
David Tolnaycfa5cc02017-11-13 01:05:11 -0800204fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
David Tolnay3cede942017-12-26 12:29:24 -0500205 parse::libsyntax_expr(input).and_then(libsyntax_brackets)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400206}
207
208/// Wrap every expression which is not already wrapped in parens with parens, to
David Tolnay0ccb6d12018-08-14 22:43:00 -0700209/// reveal the precidence of the parsed expressions, and produce a stringified
210/// form of the resulting expression.
Michael Layzell53fc31a2017-06-07 09:21:53 -0400211///
David Tolnaycfa5cc02017-11-13 01:05:11 -0800212/// This method operates on libsyntax objects.
213fn libsyntax_brackets(libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
David Tolnayc8659922018-08-14 22:40:50 -0700214 use rustc_data_structures::thin_vec::ThinVec;
David Tolnay0eab7d92018-09-26 22:10:40 -0700215 use smallvec::SmallVec;
David Tolnay51382052017-12-27 13:46:21 -0500216 use syntax::ast::{Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
David Tolnayeb7d79b2018-03-31 22:52:17 +0200217 use syntax::ext::quote::rt::DUMMY_SP;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800218 use syntax::fold::{self, Folder};
Michael Layzell53fc31a2017-06-07 09:21:53 -0400219
Michael Layzell53fc31a2017-06-07 09:21:53 -0400220 struct BracketsFolder {
221 failed: bool,
222 };
223 impl Folder for BracketsFolder {
224 fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
David Tolnay5d314dc2018-07-21 16:40:01 -0700225 e.map(|e| match e.node {
226 ExprKind::If(..) | ExprKind::Block(..) | ExprKind::IfLet(..) => {
227 fold::noop_fold_expr(e, self)
228 }
229 _ => Expr {
230 id: ast::DUMMY_NODE_ID,
231 node: ExprKind::Paren(P(fold::noop_fold_expr(e, self))),
232 span: DUMMY_SP,
233 attrs: ThinVec::new(),
David Tolnay51382052017-12-27 13:46:21 -0500234 },
Michael Layzell53fc31a2017-06-07 09:21:53 -0400235 })
236 }
237
238 fn fold_field(&mut self, f: Field) -> Field {
239 Field {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400240 expr: if f.is_shorthand {
241 f.expr.map(|e| fold::noop_fold_expr(e, self))
242 } else {
243 self.fold_expr(f.expr)
244 },
David Tolnaya3174992018-04-06 22:41:59 -0700245 ..f
Michael Layzell53fc31a2017-06-07 09:21:53 -0400246 }
247 }
248
249 // We don't want to look at expressions that might appear in patterns or
250 // types yet. We'll look into comparing those in the future. For now
251 // focus on expressions appearing in other places.
252 fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
253 pat
254 }
255
256 fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
257 ty
258 }
259
David Tolnay0eab7d92018-09-26 22:10:40 -0700260 fn fold_stmt(&mut self, stmt: Stmt) -> SmallVec<[Stmt; 1]> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400261 let node = match stmt.node {
262 // Don't wrap toplevel expressions in statements.
David Tolnay51382052017-12-27 13:46:21 -0500263 StmtKind::Expr(e) => StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self))),
264 StmtKind::Semi(e) => StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self))),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400265 s => s,
266 };
267
David Tolnay0eab7d92018-09-26 22:10:40 -0700268 smallvec![Stmt { node, ..stmt }]
Michael Layzell53fc31a2017-06-07 09:21:53 -0400269 }
270
271 fn fold_mac(&mut self, mac: Mac) -> Mac {
David Tolnaycfa5cc02017-11-13 01:05:11 -0800272 // By default when folding over macros, libsyntax panics. This is
Michael Layzell53fc31a2017-06-07 09:21:53 -0400273 // because it's usually not what you want, you want to run after
274 // macro expansion. We do want to do that (syn doesn't do macro
275 // expansion), so we implement fold_mac to just return the macro
276 // unchanged.
277 mac
278 }
279 }
280
David Tolnay51382052017-12-27 13:46:21 -0500281 let mut folder = BracketsFolder { failed: false };
David Tolnaycfa5cc02017-11-13 01:05:11 -0800282 let e = folder.fold_expr(libsyntax_expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400283 if folder.failed {
284 None
285 } else {
286 Some(e)
287 }
288}
289
290/// Wrap every expression which is not already wrapped in parens with parens, to
David Tolnay0ccb6d12018-08-14 22:43:00 -0700291/// reveal the precedence of the parsed expressions, and produce a stringified
292/// form of the resulting expression.
Michael Layzell53fc31a2017-06-07 09:21:53 -0400293fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400294 use syn::fold::*;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200295 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400296
David Tolnayeb752062018-01-06 13:51:42 -0800297 struct ParenthesizeEveryExpr;
298 impl Fold for ParenthesizeEveryExpr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400299 fn fold_expr(&mut self, expr: Expr) -> Expr {
David Tolnay8c91b882017-12-28 23:04:32 -0500300 match expr {
301 Expr::Group(_) => unreachable!(),
David Tolnay9c119122018-09-01 18:47:02 -0700302 Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => {
David Tolnay3bc597f2017-12-31 02:31:11 -0500303 fold_expr(self, expr)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400304 }
David Tolnay5d314dc2018-07-21 16:40:01 -0700305 node => Expr::Paren(ExprParen {
306 attrs: Vec::new(),
307 expr: Box::new(fold_expr(self, node)),
308 paren_token: token::Paren::default(),
309 }),
David Tolnay8c91b882017-12-28 23:04:32 -0500310 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400311 }
312
313 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
314 match stmt {
315 // Don't wrap toplevel expressions in statements.
David Tolnay1f0b7b82018-01-06 16:07:14 -0800316 Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
317 Stmt::Semi(e, semi) => Stmt::Semi(fold_expr(self, e), semi),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400318 s => s,
319 }
320 }
321
322 // We don't want to look at expressions that might appear in patterns or
323 // types yet. We'll look into comparing those in the future. For now
324 // focus on expressions appearing in other places.
325 fn fold_pat(&mut self, pat: Pat) -> Pat {
326 pat
327 }
328
David Tolnayfd6bf5c2017-11-12 09:41:14 -0800329 fn fold_type(&mut self, ty: Type) -> Type {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400330 ty
331 }
332 }
333
David Tolnayeb752062018-01-06 13:51:42 -0800334 let mut folder = ParenthesizeEveryExpr;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400335 folder.fold_expr(syn_expr)
336}
337
338/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700339fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400340 use syn::fold::*;
David Tolnayf2cfd722017-12-31 18:02:51 -0500341 use syn::punctuated::Punctuated;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200342 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400343
David Tolnayeb752062018-01-06 13:51:42 -0800344 struct CollectExprs(Vec<Expr>);
345 impl Fold for CollectExprs {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400346 fn fold_expr(&mut self, expr: Expr) -> Expr {
347 self.0.push(expr);
348
David Tolnay8c91b882017-12-28 23:04:32 -0500349 Expr::Tuple(ExprTuple {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400350 attrs: vec![],
David Tolnayf2cfd722017-12-31 18:02:51 -0500351 elems: Punctuated::new(),
David Tolnay8c91b882017-12-28 23:04:32 -0500352 paren_token: token::Paren::default(),
353 })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400354 }
355 }
356
David Tolnayeb752062018-01-06 13:51:42 -0800357 let mut folder = CollectExprs(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700358 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400359 folder.0
360}