blob: cd0b0419fe5f93342938e6be2839aa520f620db7 [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;
Igor Gnatenko951a52b2018-03-12 10:33:33 +010035use walkdir::{DirEntry, WalkDir};
David Tolnayee97dbf2017-11-19 14:24:38 -080036
37use std::fs::File;
38use std::io::Read;
David Tolnay3eaf7d82017-12-17 23:14:52 -080039use std::process;
David Tolnayee97dbf2017-11-19 14:24:38 -080040use std::sync::atomic::{AtomicUsize, Ordering};
Michael Layzell53fc31a2017-06-07 09:21:53 -040041
David Tolnayecd024d2018-07-21 09:07:56 -070042use common::eq::SpanlessEq;
43use common::parse;
Michael Layzell53fc31a2017-06-07 09:21:53 -040044
Michael Layzell53fc31a2017-06-07 09:21:53 -040045#[macro_use]
David Tolnaydd125562017-12-31 02:16:22 -050046mod macros;
47
48#[allow(dead_code)]
Michael Layzell53fc31a2017-06-07 09:21:53 -040049mod common;
50
51/// Test some pre-set expressions chosen by us.
52#[test]
53fn test_simple_precedence() {
54 const EXPRS: &[&str] = &[
55 "1 + 2 * 3 + 4",
56 "1 + 2 * ( 3 + 4 )",
57 "{ for i in r { } *some_ptr += 1; }",
58 "{ loop { break 5; } }",
59 "{ if true { () }.mthd() }",
Nika Layzell3aa0dc72017-12-04 13:41:28 -050060 "{ for i in unsafe { 20 } { } }",
Michael Layzell53fc31a2017-06-07 09:21:53 -040061 ];
62
63 let mut failed = 0;
64
65 for input in EXPRS {
66 let expr = if let Some(expr) = parse::syn_expr(input) {
67 expr
68 } else {
69 failed += 1;
70 continue;
71 };
72
73 let pf = match test_expressions(vec![expr]) {
74 (1, 0) => "passed",
75 (0, 1) => {
76 failed += 1;
77 "failed"
78 }
79 _ => unreachable!(),
80 };
81 errorf!("=== {}: {}\n", input, pf);
82 }
83
84 if failed > 0 {
85 panic!("Failed {} tests", failed);
86 }
87}
88
89/// Test expressions from rustc, like in `test_round_trip`.
90#[test]
cad97286b19f2019-01-23 23:08:25 -050091#[cfg_attr(target_os = "windows", ignore = "requires nix .sh")]
Michael Layzell53fc31a2017-06-07 09:21:53 -040092fn test_rustc_precedence() {
Alex Crichton86374772017-07-07 20:39:28 -070093 common::clone_rust();
Michael Layzell53fc31a2017-06-07 09:21:53 -040094 let abort_after = common::abort_after();
95 if abort_after == 0 {
96 panic!("Skipping all precedence tests");
97 }
98
David Tolnayee97dbf2017-11-19 14:24:38 -080099 let passed = AtomicUsize::new(0);
100 let failed = AtomicUsize::new(0);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400101
David Tolnayd1c31cc2018-08-24 14:47:15 -0400102 // 2018 edition is hard
103 let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap();
104
David Tolnayee97dbf2017-11-19 14:24:38 -0800105 WalkDir::new("tests/rust")
Igor Gnatenko951a52b2018-03-12 10:33:33 +0100106 .sort_by(|a, b| a.file_name().cmp(b.file_name()))
David Tolnayee97dbf2017-11-19 14:24:38 -0800107 .into_iter()
108 .filter_entry(common::base_dir_filter)
109 .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
110 .unwrap()
111 .into_par_iter()
David Tolnay51382052017-12-27 13:46:21 -0500112 .for_each(|entry| {
113 let path = entry.path();
114 if path.is_dir() {
115 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400116 }
David Tolnay51382052017-12-27 13:46:21 -0500117
118 // Our version of `libsyntax` can't parse this tests
David Tolnay65fb5662018-05-20 20:02:28 -0700119 if path
120 .to_str()
David Tolnay51382052017-12-27 13:46:21 -0500121 .unwrap()
122 .ends_with("optional_comma_in_match_arm.rs")
123 {
124 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400125 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400126
David Tolnay51382052017-12-27 13:46:21 -0500127 let mut file = File::open(path).unwrap();
128 let mut content = String::new();
129 file.read_to_string(&mut content).unwrap();
David Tolnayd1c31cc2018-08-24 14:47:15 -0400130 let content = edition_regex.replace_all(&content, "_$0");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400131
David Tolnay51382052017-12-27 13:46:21 -0500132 let (l_passed, l_failed) = match syn::parse_file(&content) {
133 Ok(file) => {
134 let exprs = collect_exprs(file);
135 test_expressions(exprs)
136 }
137 Err(msg) => {
138 errorf!("syn failed to parse\n{:?}\n", msg);
139 (0, 1)
140 }
141 };
David Tolnayee97dbf2017-11-19 14:24:38 -0800142
David Tolnay51382052017-12-27 13:46:21 -0500143 errorf!(
144 "=== {}: {} passed | {} failed\n",
145 path.display(),
146 l_passed,
147 l_failed
148 );
149
150 passed.fetch_add(l_passed, Ordering::SeqCst);
151 let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);
152
153 if prev_failed + l_failed >= abort_after {
154 process::exit(1);
155 }
156 });
David Tolnayee97dbf2017-11-19 14:24:38 -0800157
158 let passed = passed.load(Ordering::SeqCst);
159 let failed = failed.load(Ordering::SeqCst);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400160
161 errorf!("\n===== Precedence Test Results =====\n");
162 errorf!("{} passed | {} failed\n", passed, failed);
163
Michael Layzell53fc31a2017-06-07 09:21:53 -0400164 if failed > 0 {
165 panic!("{} failures", failed);
166 }
167}
168
David Tolnayee97dbf2017-11-19 14:24:38 -0800169fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400170 let mut passed = 0;
171 let mut failed = 0;
172
David Tolnayeb7d79b2018-03-31 22:52:17 +0200173 syntax::with_globals(|| {
174 for expr in exprs {
175 let raw = quote!(#expr).to_string();
Michael Layzell53fc31a2017-06-07 09:21:53 -0400176
David Tolnayeb7d79b2018-03-31 22:52:17 +0200177 let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
178 e
179 } else {
180 failed += 1;
181 errorf!("\nFAIL - libsyntax failed to parse raw\n");
182 continue;
183 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400184
David Tolnayeb7d79b2018-03-31 22:52:17 +0200185 let syn_expr = syn_brackets(expr);
186 let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
187 e
188 } else {
189 failed += 1;
190 errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
191 continue;
192 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400193
David Tolnayecd024d2018-07-21 09:07:56 -0700194 if SpanlessEq::eq(&syn_ast, &libsyntax_ast) {
David Tolnayeb7d79b2018-03-31 22:52:17 +0200195 passed += 1;
196 } else {
197 failed += 1;
198 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
199 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400200 }
David Tolnay5d1a3ee2018-03-17 19:41:36 -0700201 });
Michael Layzell53fc31a2017-06-07 09:21:53 -0400202
203 (passed, failed)
204}
205
David Tolnaycfa5cc02017-11-13 01:05:11 -0800206fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
David Tolnay3cede942017-12-26 12:29:24 -0500207 parse::libsyntax_expr(input).and_then(libsyntax_brackets)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400208}
209
210/// Wrap every expression which is not already wrapped in parens with parens, to
David Tolnay0ccb6d12018-08-14 22:43:00 -0700211/// reveal the precidence of the parsed expressions, and produce a stringified
212/// form of the resulting expression.
Michael Layzell53fc31a2017-06-07 09:21:53 -0400213///
David Tolnaycfa5cc02017-11-13 01:05:11 -0800214/// This method operates on libsyntax objects.
David Tolnay176838f2019-02-07 17:28:37 +0100215fn libsyntax_brackets(mut libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
David Tolnayc8659922018-08-14 22:40:50 -0700216 use rustc_data_structures::thin_vec::ThinVec;
David Tolnay0eab7d92018-09-26 22:10:40 -0700217 use smallvec::SmallVec;
David Tolnay176838f2019-02-07 17:28:37 +0100218 use std::mem;
David Tolnaye6b37092019-05-08 18:29:54 -0700219 use syntax::ast::{AwaitOrigin, Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
David Tolnay176838f2019-02-07 17:28:37 +0100220 use syntax::mut_visit::{self, MutVisitor};
David Tolnayfcd4a672019-01-24 20:56:54 -0800221 use syntax_pos::DUMMY_SP;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400222
David Tolnay176838f2019-02-07 17:28:37 +0100223 struct BracketsVisitor {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400224 failed: bool,
225 };
David Tolnaye6b37092019-05-08 18:29:54 -0700226
227 impl BracketsVisitor {
228 fn recurse_expr(&mut self, e: &mut Expr) {
229 match e.node {
230 ExprKind::Await(AwaitOrigin::MacroLike, _) => {
231 // Syn sees await!() as macro and doesn't recurse inside, so
232 // skip it here too.
233 }
234 _ => mut_visit::noop_visit_expr(e, self),
235 }
236 }
237 }
238
David Tolnay176838f2019-02-07 17:28:37 +0100239 impl MutVisitor for BracketsVisitor {
240 fn visit_expr(&mut self, e: &mut P<Expr>) {
David Tolnaye6b37092019-05-08 18:29:54 -0700241 self.recurse_expr(e);
David Tolnay176838f2019-02-07 17:28:37 +0100242 match e.node {
243 ExprKind::If(..) | ExprKind::Block(..) | ExprKind::IfLet(..) => {}
244 _ => {
245 let inner = mem::replace(
246 e,
247 P(Expr {
248 id: ast::DUMMY_NODE_ID,
249 node: ExprKind::Err,
250 span: DUMMY_SP,
251 attrs: ThinVec::new(),
252 }),
253 );
254 e.node = ExprKind::Paren(inner);
David Tolnay5d314dc2018-07-21 16:40:01 -0700255 }
David Tolnay176838f2019-02-07 17:28:37 +0100256 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400257 }
258
David Tolnay176838f2019-02-07 17:28:37 +0100259 fn visit_field(&mut self, f: &mut Field) {
260 if f.is_shorthand {
David Tolnaye6b37092019-05-08 18:29:54 -0700261 self.recurse_expr(&mut f.expr);
David Tolnay176838f2019-02-07 17:28:37 +0100262 } else {
263 self.visit_expr(&mut f.expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400264 }
265 }
266
267 // We don't want to look at expressions that might appear in patterns or
268 // types yet. We'll look into comparing those in the future. For now
269 // focus on expressions appearing in other places.
David Tolnay176838f2019-02-07 17:28:37 +0100270 fn visit_pat(&mut self, pat: &mut P<Pat>) {
271 let _ = pat;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400272 }
273
David Tolnay176838f2019-02-07 17:28:37 +0100274 fn visit_ty(&mut self, ty: &mut P<Ty>) {
275 let _ = ty;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400276 }
277
David Tolnay176838f2019-02-07 17:28:37 +0100278 fn flat_map_stmt(&mut self, stmt: Stmt) -> SmallVec<[Stmt; 1]> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400279 let node = match stmt.node {
280 // Don't wrap toplevel expressions in statements.
David Tolnay176838f2019-02-07 17:28:37 +0100281 StmtKind::Expr(mut e) => {
David Tolnaye6b37092019-05-08 18:29:54 -0700282 self.recurse_expr(&mut e);
David Tolnay176838f2019-02-07 17:28:37 +0100283 StmtKind::Expr(e)
284 }
285 StmtKind::Semi(mut e) => {
David Tolnaye6b37092019-05-08 18:29:54 -0700286 self.recurse_expr(&mut e);
David Tolnay176838f2019-02-07 17:28:37 +0100287 StmtKind::Semi(e)
288 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400289 s => s,
290 };
291
David Tolnay0eab7d92018-09-26 22:10:40 -0700292 smallvec![Stmt { node, ..stmt }]
Michael Layzell53fc31a2017-06-07 09:21:53 -0400293 }
294
David Tolnay176838f2019-02-07 17:28:37 +0100295 fn visit_mac(&mut self, mac: &mut Mac) {
David Tolnaycfa5cc02017-11-13 01:05:11 -0800296 // By default when folding over macros, libsyntax panics. This is
Michael Layzell53fc31a2017-06-07 09:21:53 -0400297 // because it's usually not what you want, you want to run after
298 // macro expansion. We do want to do that (syn doesn't do macro
David Tolnay176838f2019-02-07 17:28:37 +0100299 // expansion), so we implement visit_mac to just return the macro
Michael Layzell53fc31a2017-06-07 09:21:53 -0400300 // unchanged.
David Tolnay176838f2019-02-07 17:28:37 +0100301 let _ = mac;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400302 }
303 }
304
David Tolnay176838f2019-02-07 17:28:37 +0100305 let mut folder = BracketsVisitor { failed: false };
306 folder.visit_expr(&mut libsyntax_expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400307 if folder.failed {
308 None
309 } else {
David Tolnay176838f2019-02-07 17:28:37 +0100310 Some(libsyntax_expr)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400311 }
312}
313
314/// Wrap every expression which is not already wrapped in parens with parens, to
David Tolnay0ccb6d12018-08-14 22:43:00 -0700315/// reveal the precedence of the parsed expressions, and produce a stringified
316/// form of the resulting expression.
Michael Layzell53fc31a2017-06-07 09:21:53 -0400317fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400318 use syn::fold::*;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200319 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400320
David Tolnayeb752062018-01-06 13:51:42 -0800321 struct ParenthesizeEveryExpr;
322 impl Fold for ParenthesizeEveryExpr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400323 fn fold_expr(&mut self, expr: Expr) -> Expr {
David Tolnay8c91b882017-12-28 23:04:32 -0500324 match expr {
325 Expr::Group(_) => unreachable!(),
David Tolnay9c119122018-09-01 18:47:02 -0700326 Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => {
David Tolnay3bc597f2017-12-31 02:31:11 -0500327 fold_expr(self, expr)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400328 }
David Tolnay5d314dc2018-07-21 16:40:01 -0700329 node => Expr::Paren(ExprParen {
330 attrs: Vec::new(),
331 expr: Box::new(fold_expr(self, node)),
332 paren_token: token::Paren::default(),
333 }),
David Tolnay8c91b882017-12-28 23:04:32 -0500334 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400335 }
336
337 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
338 match stmt {
339 // Don't wrap toplevel expressions in statements.
David Tolnay1f0b7b82018-01-06 16:07:14 -0800340 Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
341 Stmt::Semi(e, semi) => Stmt::Semi(fold_expr(self, e), semi),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400342 s => s,
343 }
344 }
345
346 // We don't want to look at expressions that might appear in patterns or
347 // types yet. We'll look into comparing those in the future. For now
348 // focus on expressions appearing in other places.
349 fn fold_pat(&mut self, pat: Pat) -> Pat {
350 pat
351 }
352
David Tolnayfd6bf5c2017-11-12 09:41:14 -0800353 fn fold_type(&mut self, ty: Type) -> Type {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400354 ty
355 }
356 }
357
David Tolnayeb752062018-01-06 13:51:42 -0800358 let mut folder = ParenthesizeEveryExpr;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400359 folder.fold_expr(syn_expr)
360}
361
362/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700363fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400364 use syn::fold::*;
David Tolnayf2cfd722017-12-31 18:02:51 -0500365 use syn::punctuated::Punctuated;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200366 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400367
David Tolnayeb752062018-01-06 13:51:42 -0800368 struct CollectExprs(Vec<Expr>);
369 impl Fold for CollectExprs {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400370 fn fold_expr(&mut self, expr: Expr) -> Expr {
371 self.0.push(expr);
372
David Tolnay8c91b882017-12-28 23:04:32 -0500373 Expr::Tuple(ExprTuple {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400374 attrs: vec![],
David Tolnayf2cfd722017-12-31 18:02:51 -0500375 elems: Punctuated::new(),
David Tolnay8c91b882017-12-28 23:04:32 -0500376 paren_token: token::Paren::default(),
377 })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400378 }
379 }
380
David Tolnayeb752062018-01-06 13:51:42 -0800381 let mut folder = CollectExprs(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700382 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400383 folder.0
384}