blob: 5dbaa2da53d1baedcfaef06ee518a5264a21a0b8 [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
David Tolnayecd024d2018-07-21 09:07:56 -07009#![recursion_limit = "1024"]
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`.
David Tolnay0ccb6d12018-08-14 22:43:00 -070020//! 4. Respan all of the expressions, replacing the spans with the default
21//! spans.
Michael Layzell53fc31a2017-06-07 09:21:53 -040022//! 5. Compare the expressions with one another, if they are not equal fail.
23
24#[macro_use]
25extern crate quote;
David Tolnayee97dbf2017-11-19 14:24:38 -080026extern crate rayon;
David Tolnayd1c31cc2018-08-24 14:47:15 -040027extern crate regex;
David Tolnayc8659922018-08-14 22:40:50 -070028extern crate rustc_data_structures;
David Tolnay0eab7d92018-09-26 22:10:40 -070029#[macro_use]
30extern crate smallvec;
Michael Layzell53fc31a2017-06-07 09:21:53 -040031extern crate syn;
David Tolnaycfa5cc02017-11-13 01:05:11 -080032extern crate syntax;
Michael Layzell53fc31a2017-06-07 09:21:53 -040033extern crate walkdir;
Michael Layzell53fc31a2017-06-07 09:21:53 -040034
David Tolnayc3f98562018-11-02 08:55:05 -070035mod features;
36
David Tolnay51382052017-12-27 13:46:21 -050037use rayon::iter::{IntoParallelIterator, ParallelIterator};
David Tolnayd1c31cc2018-08-24 14:47:15 -040038use regex::Regex;
David Tolnaycfa5cc02017-11-13 01:05:11 -080039use syntax::ast;
40use syntax::ptr::P;
Igor Gnatenko951a52b2018-03-12 10:33:33 +010041use walkdir::{DirEntry, WalkDir};
David Tolnayee97dbf2017-11-19 14:24:38 -080042
43use std::fs::File;
44use std::io::Read;
David Tolnay3eaf7d82017-12-17 23:14:52 -080045use std::process;
David Tolnayee97dbf2017-11-19 14:24:38 -080046use std::sync::atomic::{AtomicUsize, Ordering};
Michael Layzell53fc31a2017-06-07 09:21:53 -040047
David Tolnayecd024d2018-07-21 09:07:56 -070048use common::eq::SpanlessEq;
49use common::parse;
Michael Layzell53fc31a2017-06-07 09:21:53 -040050
Michael Layzell53fc31a2017-06-07 09:21:53 -040051#[macro_use]
David Tolnaydd125562017-12-31 02:16:22 -050052mod macros;
53
54#[allow(dead_code)]
Michael Layzell53fc31a2017-06-07 09:21:53 -040055mod common;
56
57/// Test some pre-set expressions chosen by us.
58#[test]
59fn test_simple_precedence() {
60 const EXPRS: &[&str] = &[
61 "1 + 2 * 3 + 4",
62 "1 + 2 * ( 3 + 4 )",
63 "{ for i in r { } *some_ptr += 1; }",
64 "{ loop { break 5; } }",
65 "{ if true { () }.mthd() }",
Nika Layzell3aa0dc72017-12-04 13:41:28 -050066 "{ for i in unsafe { 20 } { } }",
Michael Layzell53fc31a2017-06-07 09:21:53 -040067 ];
68
69 let mut failed = 0;
70
71 for input in EXPRS {
72 let expr = if let Some(expr) = parse::syn_expr(input) {
73 expr
74 } else {
75 failed += 1;
76 continue;
77 };
78
79 let pf = match test_expressions(vec![expr]) {
80 (1, 0) => "passed",
81 (0, 1) => {
82 failed += 1;
83 "failed"
84 }
85 _ => unreachable!(),
86 };
87 errorf!("=== {}: {}\n", input, pf);
88 }
89
90 if failed > 0 {
91 panic!("Failed {} tests", failed);
92 }
93}
94
95/// Test expressions from rustc, like in `test_round_trip`.
96#[test]
97fn test_rustc_precedence() {
Alex Crichton86374772017-07-07 20:39:28 -070098 common::clone_rust();
Michael Layzell53fc31a2017-06-07 09:21:53 -040099 let abort_after = common::abort_after();
100 if abort_after == 0 {
101 panic!("Skipping all precedence tests");
102 }
103
David Tolnayee97dbf2017-11-19 14:24:38 -0800104 let passed = AtomicUsize::new(0);
105 let failed = AtomicUsize::new(0);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400106
David Tolnayd1c31cc2018-08-24 14:47:15 -0400107 // 2018 edition is hard
108 let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap();
109
David Tolnayee97dbf2017-11-19 14:24:38 -0800110 WalkDir::new("tests/rust")
Igor Gnatenko951a52b2018-03-12 10:33:33 +0100111 .sort_by(|a, b| a.file_name().cmp(b.file_name()))
David Tolnayee97dbf2017-11-19 14:24:38 -0800112 .into_iter()
113 .filter_entry(common::base_dir_filter)
114 .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
115 .unwrap()
116 .into_par_iter()
David Tolnay51382052017-12-27 13:46:21 -0500117 .for_each(|entry| {
118 let path = entry.path();
119 if path.is_dir() {
120 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400121 }
David Tolnay51382052017-12-27 13:46:21 -0500122
123 // Our version of `libsyntax` can't parse this tests
David Tolnay65fb5662018-05-20 20:02:28 -0700124 if path
125 .to_str()
David Tolnay51382052017-12-27 13:46:21 -0500126 .unwrap()
127 .ends_with("optional_comma_in_match_arm.rs")
128 {
129 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400130 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400131
David Tolnay51382052017-12-27 13:46:21 -0500132 let mut file = File::open(path).unwrap();
133 let mut content = String::new();
134 file.read_to_string(&mut content).unwrap();
David Tolnayd1c31cc2018-08-24 14:47:15 -0400135 let content = edition_regex.replace_all(&content, "_$0");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400136
David Tolnay51382052017-12-27 13:46:21 -0500137 let (l_passed, l_failed) = match syn::parse_file(&content) {
138 Ok(file) => {
139 let exprs = collect_exprs(file);
140 test_expressions(exprs)
141 }
142 Err(msg) => {
143 errorf!("syn failed to parse\n{:?}\n", msg);
144 (0, 1)
145 }
146 };
David Tolnayee97dbf2017-11-19 14:24:38 -0800147
David Tolnay51382052017-12-27 13:46:21 -0500148 errorf!(
149 "=== {}: {} passed | {} failed\n",
150 path.display(),
151 l_passed,
152 l_failed
153 );
154
155 passed.fetch_add(l_passed, Ordering::SeqCst);
156 let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);
157
158 if prev_failed + l_failed >= abort_after {
159 process::exit(1);
160 }
161 });
David Tolnayee97dbf2017-11-19 14:24:38 -0800162
163 let passed = passed.load(Ordering::SeqCst);
164 let failed = failed.load(Ordering::SeqCst);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400165
166 errorf!("\n===== Precedence Test Results =====\n");
167 errorf!("{} passed | {} failed\n", passed, failed);
168
Michael Layzell53fc31a2017-06-07 09:21:53 -0400169 if failed > 0 {
170 panic!("{} failures", failed);
171 }
172}
173
David Tolnayee97dbf2017-11-19 14:24:38 -0800174fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400175 let mut passed = 0;
176 let mut failed = 0;
177
David Tolnayeb7d79b2018-03-31 22:52:17 +0200178 syntax::with_globals(|| {
179 for expr in exprs {
180 let raw = quote!(#expr).to_string();
Michael Layzell53fc31a2017-06-07 09:21:53 -0400181
David Tolnayeb7d79b2018-03-31 22:52:17 +0200182 let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
183 e
184 } else {
185 failed += 1;
186 errorf!("\nFAIL - libsyntax failed to parse raw\n");
187 continue;
188 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400189
David Tolnayeb7d79b2018-03-31 22:52:17 +0200190 let syn_expr = syn_brackets(expr);
191 let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
192 e
193 } else {
194 failed += 1;
195 errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
196 continue;
197 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400198
David Tolnayecd024d2018-07-21 09:07:56 -0700199 if SpanlessEq::eq(&syn_ast, &libsyntax_ast) {
David Tolnayeb7d79b2018-03-31 22:52:17 +0200200 passed += 1;
201 } else {
202 failed += 1;
203 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
204 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400205 }
David Tolnay5d1a3ee2018-03-17 19:41:36 -0700206 });
Michael Layzell53fc31a2017-06-07 09:21:53 -0400207
208 (passed, failed)
209}
210
David Tolnaycfa5cc02017-11-13 01:05:11 -0800211fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
David Tolnay3cede942017-12-26 12:29:24 -0500212 parse::libsyntax_expr(input).and_then(libsyntax_brackets)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400213}
214
215/// Wrap every expression which is not already wrapped in parens with parens, to
David Tolnay0ccb6d12018-08-14 22:43:00 -0700216/// reveal the precidence of the parsed expressions, and produce a stringified
217/// form of the resulting expression.
Michael Layzell53fc31a2017-06-07 09:21:53 -0400218///
David Tolnaycfa5cc02017-11-13 01:05:11 -0800219/// This method operates on libsyntax objects.
220fn libsyntax_brackets(libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
David Tolnayc8659922018-08-14 22:40:50 -0700221 use rustc_data_structures::thin_vec::ThinVec;
David Tolnay0eab7d92018-09-26 22:10:40 -0700222 use smallvec::SmallVec;
David Tolnay51382052017-12-27 13:46:21 -0500223 use syntax::ast::{Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
David Tolnayeb7d79b2018-03-31 22:52:17 +0200224 use syntax::ext::quote::rt::DUMMY_SP;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800225 use syntax::fold::{self, Folder};
Michael Layzell53fc31a2017-06-07 09:21:53 -0400226
Michael Layzell53fc31a2017-06-07 09:21:53 -0400227 struct BracketsFolder {
228 failed: bool,
229 };
230 impl Folder for BracketsFolder {
231 fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
David Tolnay5d314dc2018-07-21 16:40:01 -0700232 e.map(|e| match e.node {
233 ExprKind::If(..) | ExprKind::Block(..) | ExprKind::IfLet(..) => {
234 fold::noop_fold_expr(e, self)
235 }
236 _ => Expr {
237 id: ast::DUMMY_NODE_ID,
238 node: ExprKind::Paren(P(fold::noop_fold_expr(e, self))),
239 span: DUMMY_SP,
240 attrs: ThinVec::new(),
David Tolnay51382052017-12-27 13:46:21 -0500241 },
Michael Layzell53fc31a2017-06-07 09:21:53 -0400242 })
243 }
244
245 fn fold_field(&mut self, f: Field) -> Field {
246 Field {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400247 expr: if f.is_shorthand {
248 f.expr.map(|e| fold::noop_fold_expr(e, self))
249 } else {
250 self.fold_expr(f.expr)
251 },
David Tolnaya3174992018-04-06 22:41:59 -0700252 ..f
Michael Layzell53fc31a2017-06-07 09:21:53 -0400253 }
254 }
255
256 // We don't want to look at expressions that might appear in patterns or
257 // types yet. We'll look into comparing those in the future. For now
258 // focus on expressions appearing in other places.
259 fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
260 pat
261 }
262
263 fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
264 ty
265 }
266
David Tolnay0eab7d92018-09-26 22:10:40 -0700267 fn fold_stmt(&mut self, stmt: Stmt) -> SmallVec<[Stmt; 1]> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400268 let node = match stmt.node {
269 // Don't wrap toplevel expressions in statements.
David Tolnay51382052017-12-27 13:46:21 -0500270 StmtKind::Expr(e) => StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self))),
271 StmtKind::Semi(e) => StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self))),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400272 s => s,
273 };
274
David Tolnay0eab7d92018-09-26 22:10:40 -0700275 smallvec![Stmt { node, ..stmt }]
Michael Layzell53fc31a2017-06-07 09:21:53 -0400276 }
277
278 fn fold_mac(&mut self, mac: Mac) -> Mac {
David Tolnaycfa5cc02017-11-13 01:05:11 -0800279 // By default when folding over macros, libsyntax panics. This is
Michael Layzell53fc31a2017-06-07 09:21:53 -0400280 // because it's usually not what you want, you want to run after
281 // macro expansion. We do want to do that (syn doesn't do macro
282 // expansion), so we implement fold_mac to just return the macro
283 // unchanged.
284 mac
285 }
286 }
287
David Tolnay51382052017-12-27 13:46:21 -0500288 let mut folder = BracketsFolder { failed: false };
David Tolnaycfa5cc02017-11-13 01:05:11 -0800289 let e = folder.fold_expr(libsyntax_expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400290 if folder.failed {
291 None
292 } else {
293 Some(e)
294 }
295}
296
297/// Wrap every expression which is not already wrapped in parens with parens, to
David Tolnay0ccb6d12018-08-14 22:43:00 -0700298/// reveal the precedence of the parsed expressions, and produce a stringified
299/// form of the resulting expression.
Michael Layzell53fc31a2017-06-07 09:21:53 -0400300fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400301 use syn::fold::*;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200302 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400303
David Tolnayeb752062018-01-06 13:51:42 -0800304 struct ParenthesizeEveryExpr;
305 impl Fold for ParenthesizeEveryExpr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400306 fn fold_expr(&mut self, expr: Expr) -> Expr {
David Tolnay8c91b882017-12-28 23:04:32 -0500307 match expr {
308 Expr::Group(_) => unreachable!(),
David Tolnay9c119122018-09-01 18:47:02 -0700309 Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => {
David Tolnay3bc597f2017-12-31 02:31:11 -0500310 fold_expr(self, expr)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400311 }
David Tolnay5d314dc2018-07-21 16:40:01 -0700312 node => Expr::Paren(ExprParen {
313 attrs: Vec::new(),
314 expr: Box::new(fold_expr(self, node)),
315 paren_token: token::Paren::default(),
316 }),
David Tolnay8c91b882017-12-28 23:04:32 -0500317 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400318 }
319
320 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
321 match stmt {
322 // Don't wrap toplevel expressions in statements.
David Tolnay1f0b7b82018-01-06 16:07:14 -0800323 Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
324 Stmt::Semi(e, semi) => Stmt::Semi(fold_expr(self, e), semi),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400325 s => s,
326 }
327 }
328
329 // We don't want to look at expressions that might appear in patterns or
330 // types yet. We'll look into comparing those in the future. For now
331 // focus on expressions appearing in other places.
332 fn fold_pat(&mut self, pat: Pat) -> Pat {
333 pat
334 }
335
David Tolnayfd6bf5c2017-11-12 09:41:14 -0800336 fn fold_type(&mut self, ty: Type) -> Type {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400337 ty
338 }
339 }
340
David Tolnayeb752062018-01-06 13:51:42 -0800341 let mut folder = ParenthesizeEveryExpr;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400342 folder.fold_expr(syn_expr)
343}
344
345/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700346fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400347 use syn::fold::*;
David Tolnayf2cfd722017-12-31 18:02:51 -0500348 use syn::punctuated::Punctuated;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200349 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400350
David Tolnayeb752062018-01-06 13:51:42 -0800351 struct CollectExprs(Vec<Expr>);
352 impl Fold for CollectExprs {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400353 fn fold_expr(&mut self, expr: Expr) -> Expr {
354 self.0.push(expr);
355
David Tolnay8c91b882017-12-28 23:04:32 -0500356 Expr::Tuple(ExprTuple {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400357 attrs: vec![],
David Tolnayf2cfd722017-12-31 18:02:51 -0500358 elems: Punctuated::new(),
David Tolnay8c91b882017-12-28 23:04:32 -0500359 paren_token: token::Paren::default(),
360 })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400361 }
362 }
363
David Tolnayeb752062018-01-06 13:51:42 -0800364 let mut folder = CollectExprs(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700365 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400366 folder.0
367}