blob: 30de381c2b1267d1abb621b58f37f09081509d0c [file] [log] [blame]
David Tolnayf83b6c92019-03-09 22:58:26 -08001#![cfg(not(syn_disable_nightly_tests))]
David Tolnayecd024d2018-07-21 09:07:56 -07002#![recursion_limit = "1024"]
Nika Layzella2a1a4a2017-11-19 11:33:17 -05003#![feature(rustc_private)]
4
Michael Layzell53fc31a2017-06-07 09:21:53 -04005//! The tests in this module do the following:
6//!
David Tolnaycfa5cc02017-11-13 01:05:11 -08007//! 1. Parse a given expression in both `syn` and `libsyntax`.
Michael Layzell53fc31a2017-06-07 09:21:53 -04008//! 2. Fold over the expression adding brackets around each subexpression (with
David Tolnaycfa5cc02017-11-13 01:05:11 -08009//! some complications - see the `syn_brackets` and `libsyntax_brackets`
Michael Layzell53fc31a2017-06-07 09:21:53 -040010//! methods).
11//! 3. Serialize the `syn` expression back into a string, and re-parse it with
David Tolnaycfa5cc02017-11-13 01:05:11 -080012//! `libsyntax`.
David Tolnay0ccb6d12018-08-14 22:43:00 -070013//! 4. Respan all of the expressions, replacing the spans with the default
14//! spans.
Michael Layzell53fc31a2017-06-07 09:21:53 -040015//! 5. Compare the expressions with one another, if they are not equal fail.
16
Michael Layzell53fc31a2017-06-07 09:21:53 -040017extern 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 -070021extern crate smallvec;
Michael Layzell53fc31a2017-06-07 09:21:53 -040022extern crate syn;
David Tolnaycfa5cc02017-11-13 01:05:11 -080023extern crate syntax;
David Tolnayfcd4a672019-01-24 20:56:54 -080024extern crate syntax_pos;
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 Tolnaye9b448f2019-03-10 22:46:38 -070029use quote::quote;
David Tolnay51382052017-12-27 13:46:21 -050030use rayon::iter::{IntoParallelIterator, ParallelIterator};
David Tolnayd1c31cc2018-08-24 14:47:15 -040031use regex::Regex;
David Tolnaye9b448f2019-03-10 22:46:38 -070032use smallvec::smallvec;
David Tolnaycfa5cc02017-11-13 01:05:11 -080033use syntax::ast;
34use syntax::ptr::P;
David Tolnaye6eb5902019-05-23 00:00:18 -070035use syntax_pos::edition::Edition;
Igor Gnatenko951a52b2018-03-12 10:33:33 +010036use walkdir::{DirEntry, WalkDir};
David Tolnayee97dbf2017-11-19 14:24:38 -080037
38use std::fs::File;
39use std::io::Read;
David Tolnay3eaf7d82017-12-17 23:14:52 -080040use std::process;
David Tolnayee97dbf2017-11-19 14:24:38 -080041use std::sync::atomic::{AtomicUsize, Ordering};
Michael Layzell53fc31a2017-06-07 09:21:53 -040042
David Tolnayecd024d2018-07-21 09:07:56 -070043use common::eq::SpanlessEq;
44use common::parse;
Michael Layzell53fc31a2017-06-07 09:21:53 -040045
Michael Layzell53fc31a2017-06-07 09:21:53 -040046#[macro_use]
David Tolnaydd125562017-12-31 02:16:22 -050047mod macros;
48
49#[allow(dead_code)]
Michael Layzell53fc31a2017-06-07 09:21:53 -040050mod common;
51
52/// Test some pre-set expressions chosen by us.
53#[test]
54fn test_simple_precedence() {
55 const EXPRS: &[&str] = &[
56 "1 + 2 * 3 + 4",
57 "1 + 2 * ( 3 + 4 )",
58 "{ for i in r { } *some_ptr += 1; }",
59 "{ loop { break 5; } }",
60 "{ if true { () }.mthd() }",
Nika Layzell3aa0dc72017-12-04 13:41:28 -050061 "{ for i in unsafe { 20 } { } }",
Michael Layzell53fc31a2017-06-07 09:21:53 -040062 ];
63
64 let mut failed = 0;
65
66 for input in EXPRS {
67 let expr = if let Some(expr) = parse::syn_expr(input) {
68 expr
69 } else {
70 failed += 1;
71 continue;
72 };
73
74 let pf = match test_expressions(vec![expr]) {
75 (1, 0) => "passed",
76 (0, 1) => {
77 failed += 1;
78 "failed"
79 }
80 _ => unreachable!(),
81 };
82 errorf!("=== {}: {}\n", input, pf);
83 }
84
85 if failed > 0 {
86 panic!("Failed {} tests", failed);
87 }
88}
89
90/// Test expressions from rustc, like in `test_round_trip`.
91#[test]
cad97286b19f2019-01-23 23:08:25 -050092#[cfg_attr(target_os = "windows", ignore = "requires nix .sh")]
Michael Layzell53fc31a2017-06-07 09:21:53 -040093fn test_rustc_precedence() {
Alex Crichton86374772017-07-07 20:39:28 -070094 common::clone_rust();
Michael Layzell53fc31a2017-06-07 09:21:53 -040095 let abort_after = common::abort_after();
96 if abort_after == 0 {
97 panic!("Skipping all precedence tests");
98 }
99
David Tolnayee97dbf2017-11-19 14:24:38 -0800100 let passed = AtomicUsize::new(0);
101 let failed = AtomicUsize::new(0);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400102
David Tolnayd1c31cc2018-08-24 14:47:15 -0400103 // 2018 edition is hard
104 let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap();
105
David Tolnayee97dbf2017-11-19 14:24:38 -0800106 WalkDir::new("tests/rust")
Igor Gnatenko951a52b2018-03-12 10:33:33 +0100107 .sort_by(|a, b| a.file_name().cmp(b.file_name()))
David Tolnayee97dbf2017-11-19 14:24:38 -0800108 .into_iter()
109 .filter_entry(common::base_dir_filter)
110 .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
111 .unwrap()
112 .into_par_iter()
David Tolnay51382052017-12-27 13:46:21 -0500113 .for_each(|entry| {
114 let path = entry.path();
115 if path.is_dir() {
116 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400117 }
David Tolnay51382052017-12-27 13:46:21 -0500118
119 // Our version of `libsyntax` can't parse this tests
David Tolnay65fb5662018-05-20 20:02:28 -0700120 if path
121 .to_str()
David Tolnay51382052017-12-27 13:46:21 -0500122 .unwrap()
123 .ends_with("optional_comma_in_match_arm.rs")
124 {
125 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400126 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400127
David Tolnay51382052017-12-27 13:46:21 -0500128 let mut file = File::open(path).unwrap();
129 let mut content = String::new();
130 file.read_to_string(&mut content).unwrap();
David Tolnayd1c31cc2018-08-24 14:47:15 -0400131 let content = edition_regex.replace_all(&content, "_$0");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400132
David Tolnay51382052017-12-27 13:46:21 -0500133 let (l_passed, l_failed) = match syn::parse_file(&content) {
134 Ok(file) => {
135 let exprs = collect_exprs(file);
136 test_expressions(exprs)
137 }
138 Err(msg) => {
139 errorf!("syn failed to parse\n{:?}\n", msg);
140 (0, 1)
141 }
142 };
David Tolnayee97dbf2017-11-19 14:24:38 -0800143
David Tolnay51382052017-12-27 13:46:21 -0500144 errorf!(
145 "=== {}: {} passed | {} failed\n",
146 path.display(),
147 l_passed,
148 l_failed
149 );
150
151 passed.fetch_add(l_passed, Ordering::SeqCst);
152 let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);
153
154 if prev_failed + l_failed >= abort_after {
155 process::exit(1);
156 }
157 });
David Tolnayee97dbf2017-11-19 14:24:38 -0800158
159 let passed = passed.load(Ordering::SeqCst);
160 let failed = failed.load(Ordering::SeqCst);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400161
162 errorf!("\n===== Precedence Test Results =====\n");
163 errorf!("{} passed | {} failed\n", passed, failed);
164
Michael Layzell53fc31a2017-06-07 09:21:53 -0400165 if failed > 0 {
166 panic!("{} failures", failed);
167 }
168}
169
David Tolnayee97dbf2017-11-19 14:24:38 -0800170fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400171 let mut passed = 0;
172 let mut failed = 0;
173
David Tolnaye6eb5902019-05-23 00:00:18 -0700174 syntax::with_globals(Edition::Edition2018, || {
David Tolnayeb7d79b2018-03-31 22:52:17 +0200175 for expr in exprs {
176 let raw = quote!(#expr).to_string();
Michael Layzell53fc31a2017-06-07 09:21:53 -0400177
David Tolnayeb7d79b2018-03-31 22:52:17 +0200178 let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
179 e
180 } else {
181 failed += 1;
182 errorf!("\nFAIL - libsyntax failed to parse raw\n");
183 continue;
184 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400185
David Tolnayeb7d79b2018-03-31 22:52:17 +0200186 let syn_expr = syn_brackets(expr);
187 let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
188 e
189 } else {
190 failed += 1;
191 errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
192 continue;
193 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400194
David Tolnayecd024d2018-07-21 09:07:56 -0700195 if SpanlessEq::eq(&syn_ast, &libsyntax_ast) {
David Tolnayeb7d79b2018-03-31 22:52:17 +0200196 passed += 1;
197 } else {
198 failed += 1;
199 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
200 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400201 }
David Tolnay5d1a3ee2018-03-17 19:41:36 -0700202 });
Michael Layzell53fc31a2017-06-07 09:21:53 -0400203
204 (passed, failed)
205}
206
David Tolnaycfa5cc02017-11-13 01:05:11 -0800207fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
David Tolnay3cede942017-12-26 12:29:24 -0500208 parse::libsyntax_expr(input).and_then(libsyntax_brackets)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400209}
210
211/// Wrap every expression which is not already wrapped in parens with parens, to
David Tolnay0ccb6d12018-08-14 22:43:00 -0700212/// reveal the precidence of the parsed expressions, and produce a stringified
213/// form of the resulting expression.
Michael Layzell53fc31a2017-06-07 09:21:53 -0400214///
David Tolnaycfa5cc02017-11-13 01:05:11 -0800215/// This method operates on libsyntax objects.
David Tolnay176838f2019-02-07 17:28:37 +0100216fn libsyntax_brackets(mut libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
David Tolnayc8659922018-08-14 22:40:50 -0700217 use rustc_data_structures::thin_vec::ThinVec;
David Tolnay0eab7d92018-09-26 22:10:40 -0700218 use smallvec::SmallVec;
David Tolnay176838f2019-02-07 17:28:37 +0100219 use std::mem;
David Tolnaye6b37092019-05-08 18:29:54 -0700220 use syntax::ast::{AwaitOrigin, Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
David Tolnay176838f2019-02-07 17:28:37 +0100221 use syntax::mut_visit::{self, MutVisitor};
David Tolnayfcd4a672019-01-24 20:56:54 -0800222 use syntax_pos::DUMMY_SP;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400223
David Tolnay176838f2019-02-07 17:28:37 +0100224 struct BracketsVisitor {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400225 failed: bool,
226 };
David Tolnaye6b37092019-05-08 18:29:54 -0700227
228 impl BracketsVisitor {
229 fn recurse_expr(&mut self, e: &mut Expr) {
230 match e.node {
231 ExprKind::Await(AwaitOrigin::MacroLike, _) => {
232 // Syn sees await!() as macro and doesn't recurse inside, so
233 // skip it here too.
234 }
235 _ => mut_visit::noop_visit_expr(e, self),
236 }
237 }
238 }
239
David Tolnay176838f2019-02-07 17:28:37 +0100240 impl MutVisitor for BracketsVisitor {
241 fn visit_expr(&mut self, e: &mut P<Expr>) {
David Tolnaye6b37092019-05-08 18:29:54 -0700242 self.recurse_expr(e);
David Tolnay176838f2019-02-07 17:28:37 +0100243 match e.node {
David Tolnayc992d702019-06-23 18:48:23 -0700244 ExprKind::If(..) | ExprKind::Block(..) | ExprKind::Let(..) => {}
David Tolnay176838f2019-02-07 17:28:37 +0100245 _ => {
246 let inner = mem::replace(
247 e,
248 P(Expr {
249 id: ast::DUMMY_NODE_ID,
250 node: ExprKind::Err,
251 span: DUMMY_SP,
252 attrs: ThinVec::new(),
253 }),
254 );
255 e.node = ExprKind::Paren(inner);
David Tolnay5d314dc2018-07-21 16:40:01 -0700256 }
David Tolnay176838f2019-02-07 17:28:37 +0100257 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400258 }
259
David Tolnay176838f2019-02-07 17:28:37 +0100260 fn visit_field(&mut self, f: &mut Field) {
261 if f.is_shorthand {
David Tolnaye6b37092019-05-08 18:29:54 -0700262 self.recurse_expr(&mut f.expr);
David Tolnay176838f2019-02-07 17:28:37 +0100263 } else {
264 self.visit_expr(&mut f.expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400265 }
266 }
267
268 // We don't want to look at expressions that might appear in patterns or
269 // types yet. We'll look into comparing those in the future. For now
270 // focus on expressions appearing in other places.
David Tolnay176838f2019-02-07 17:28:37 +0100271 fn visit_pat(&mut self, pat: &mut P<Pat>) {
272 let _ = pat;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400273 }
274
David Tolnay176838f2019-02-07 17:28:37 +0100275 fn visit_ty(&mut self, ty: &mut P<Ty>) {
276 let _ = ty;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400277 }
278
David Tolnay176838f2019-02-07 17:28:37 +0100279 fn flat_map_stmt(&mut self, stmt: Stmt) -> SmallVec<[Stmt; 1]> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400280 let node = match stmt.node {
281 // Don't wrap toplevel expressions in statements.
David Tolnay176838f2019-02-07 17:28:37 +0100282 StmtKind::Expr(mut e) => {
David Tolnaye6b37092019-05-08 18:29:54 -0700283 self.recurse_expr(&mut e);
David Tolnay176838f2019-02-07 17:28:37 +0100284 StmtKind::Expr(e)
285 }
286 StmtKind::Semi(mut e) => {
David Tolnaye6b37092019-05-08 18:29:54 -0700287 self.recurse_expr(&mut e);
David Tolnay176838f2019-02-07 17:28:37 +0100288 StmtKind::Semi(e)
289 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400290 s => s,
291 };
292
David Tolnay0eab7d92018-09-26 22:10:40 -0700293 smallvec![Stmt { node, ..stmt }]
Michael Layzell53fc31a2017-06-07 09:21:53 -0400294 }
295
David Tolnay176838f2019-02-07 17:28:37 +0100296 fn visit_mac(&mut self, mac: &mut Mac) {
David Tolnaycfa5cc02017-11-13 01:05:11 -0800297 // By default when folding over macros, libsyntax panics. This is
Michael Layzell53fc31a2017-06-07 09:21:53 -0400298 // because it's usually not what you want, you want to run after
299 // macro expansion. We do want to do that (syn doesn't do macro
David Tolnay176838f2019-02-07 17:28:37 +0100300 // expansion), so we implement visit_mac to just return the macro
Michael Layzell53fc31a2017-06-07 09:21:53 -0400301 // unchanged.
David Tolnay176838f2019-02-07 17:28:37 +0100302 let _ = mac;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400303 }
304 }
305
David Tolnay176838f2019-02-07 17:28:37 +0100306 let mut folder = BracketsVisitor { failed: false };
307 folder.visit_expr(&mut libsyntax_expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400308 if folder.failed {
309 None
310 } else {
David Tolnay176838f2019-02-07 17:28:37 +0100311 Some(libsyntax_expr)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400312 }
313}
314
315/// Wrap every expression which is not already wrapped in parens with parens, to
David Tolnay0ccb6d12018-08-14 22:43:00 -0700316/// reveal the precedence of the parsed expressions, and produce a stringified
317/// form of the resulting expression.
Michael Layzell53fc31a2017-06-07 09:21:53 -0400318fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400319 use syn::fold::*;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200320 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400321
David Tolnayeb752062018-01-06 13:51:42 -0800322 struct ParenthesizeEveryExpr;
323 impl Fold for ParenthesizeEveryExpr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400324 fn fold_expr(&mut self, expr: Expr) -> Expr {
David Tolnay8c91b882017-12-28 23:04:32 -0500325 match expr {
326 Expr::Group(_) => unreachable!(),
David Tolnay9c119122018-09-01 18:47:02 -0700327 Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => {
David Tolnay3bc597f2017-12-31 02:31:11 -0500328 fold_expr(self, expr)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400329 }
David Tolnay5d314dc2018-07-21 16:40:01 -0700330 node => Expr::Paren(ExprParen {
331 attrs: Vec::new(),
332 expr: Box::new(fold_expr(self, node)),
333 paren_token: token::Paren::default(),
334 }),
David Tolnay8c91b882017-12-28 23:04:32 -0500335 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400336 }
337
338 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
339 match stmt {
340 // Don't wrap toplevel expressions in statements.
David Tolnay1f0b7b82018-01-06 16:07:14 -0800341 Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
342 Stmt::Semi(e, semi) => Stmt::Semi(fold_expr(self, e), semi),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400343 s => s,
344 }
345 }
346
347 // We don't want to look at expressions that might appear in patterns or
348 // types yet. We'll look into comparing those in the future. For now
349 // focus on expressions appearing in other places.
350 fn fold_pat(&mut self, pat: Pat) -> Pat {
351 pat
352 }
353
David Tolnayfd6bf5c2017-11-12 09:41:14 -0800354 fn fold_type(&mut self, ty: Type) -> Type {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400355 ty
356 }
357 }
358
David Tolnayeb752062018-01-06 13:51:42 -0800359 let mut folder = ParenthesizeEveryExpr;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400360 folder.fold_expr(syn_expr)
361}
362
363/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700364fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400365 use syn::fold::*;
David Tolnayf2cfd722017-12-31 18:02:51 -0500366 use syn::punctuated::Punctuated;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200367 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400368
David Tolnayeb752062018-01-06 13:51:42 -0800369 struct CollectExprs(Vec<Expr>);
370 impl Fold for CollectExprs {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400371 fn fold_expr(&mut self, expr: Expr) -> Expr {
372 self.0.push(expr);
373
David Tolnay8c91b882017-12-28 23:04:32 -0500374 Expr::Tuple(ExprTuple {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400375 attrs: vec![],
David Tolnayf2cfd722017-12-31 18:02:51 -0500376 elems: Punctuated::new(),
David Tolnay8c91b882017-12-28 23:04:32 -0500377 paren_token: token::Paren::default(),
378 })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400379 }
380 }
381
David Tolnayeb752062018-01-06 13:51:42 -0800382 let mut folder = CollectExprs(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700383 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400384 folder.0
385}