blob: b272d49ab9fcebde6881537ac3438fd6da39142c [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"))]
David Tolnayecd024d2018-07-21 09:07:56 -070010#![recursion_limit = "1024"]
Nika Layzella2a1a4a2017-11-19 11:33:17 -050011#![feature(rustc_private)]
12
Michael Layzell53fc31a2017-06-07 09:21:53 -040013//! The tests in this module do the following:
14//!
David Tolnaycfa5cc02017-11-13 01:05:11 -080015//! 1. Parse a given expression in both `syn` and `libsyntax`.
Michael Layzell53fc31a2017-06-07 09:21:53 -040016//! 2. Fold over the expression adding brackets around each subexpression (with
David Tolnaycfa5cc02017-11-13 01:05:11 -080017//! some complications - see the `syn_brackets` and `libsyntax_brackets`
Michael Layzell53fc31a2017-06-07 09:21:53 -040018//! methods).
19//! 3. Serialize the `syn` expression back into a string, and re-parse it with
David Tolnaycfa5cc02017-11-13 01:05:11 -080020//! `libsyntax`.
David Tolnay0ccb6d12018-08-14 22:43:00 -070021//! 4. Respan all of the expressions, replacing the spans with the default
22//! spans.
Michael Layzell53fc31a2017-06-07 09:21:53 -040023//! 5. Compare the expressions with one another, if they are not equal fail.
24
25#[macro_use]
26extern crate quote;
David Tolnayee97dbf2017-11-19 14:24:38 -080027extern crate rayon;
David Tolnayd1c31cc2018-08-24 14:47:15 -040028extern crate regex;
David Tolnayc8659922018-08-14 22:40:50 -070029extern crate rustc_data_structures;
Michael Layzell53fc31a2017-06-07 09:21:53 -040030extern crate syn;
David Tolnaycfa5cc02017-11-13 01:05:11 -080031extern crate syntax;
Michael Layzell53fc31a2017-06-07 09:21:53 -040032extern crate walkdir;
Michael Layzell53fc31a2017-06-07 09:21:53 -040033
David Tolnay51382052017-12-27 13:46:21 -050034use rayon::iter::{IntoParallelIterator, ParallelIterator};
David Tolnayd1c31cc2018-08-24 14:47:15 -040035use regex::Regex;
David Tolnaycfa5cc02017-11-13 01:05:11 -080036use syntax::ast;
37use syntax::ptr::P;
Igor Gnatenko951a52b2018-03-12 10:33:33 +010038use walkdir::{DirEntry, WalkDir};
David Tolnayee97dbf2017-11-19 14:24:38 -080039
40use std::fs::File;
41use std::io::Read;
David Tolnay3eaf7d82017-12-17 23:14:52 -080042use std::process;
David Tolnayee97dbf2017-11-19 14:24:38 -080043use std::sync::atomic::{AtomicUsize, Ordering};
Michael Layzell53fc31a2017-06-07 09:21:53 -040044
David Tolnayecd024d2018-07-21 09:07:56 -070045use common::eq::SpanlessEq;
46use common::parse;
Michael Layzell53fc31a2017-06-07 09:21:53 -040047
Michael Layzell53fc31a2017-06-07 09:21:53 -040048#[macro_use]
David Tolnaydd125562017-12-31 02:16:22 -050049mod macros;
50
51#[allow(dead_code)]
Michael Layzell53fc31a2017-06-07 09:21:53 -040052mod common;
53
54/// Test some pre-set expressions chosen by us.
55#[test]
56fn test_simple_precedence() {
57 const EXPRS: &[&str] = &[
58 "1 + 2 * 3 + 4",
59 "1 + 2 * ( 3 + 4 )",
60 "{ for i in r { } *some_ptr += 1; }",
61 "{ loop { break 5; } }",
62 "{ if true { () }.mthd() }",
Nika Layzell3aa0dc72017-12-04 13:41:28 -050063 "{ for i in unsafe { 20 } { } }",
Michael Layzell53fc31a2017-06-07 09:21:53 -040064 ];
65
66 let mut failed = 0;
67
68 for input in EXPRS {
69 let expr = if let Some(expr) = parse::syn_expr(input) {
70 expr
71 } else {
72 failed += 1;
73 continue;
74 };
75
76 let pf = match test_expressions(vec![expr]) {
77 (1, 0) => "passed",
78 (0, 1) => {
79 failed += 1;
80 "failed"
81 }
82 _ => unreachable!(),
83 };
84 errorf!("=== {}: {}\n", input, pf);
85 }
86
87 if failed > 0 {
88 panic!("Failed {} tests", failed);
89 }
90}
91
92/// Test expressions from rustc, like in `test_round_trip`.
93#[test]
94fn test_rustc_precedence() {
Michael Layzell53fc31a2017-06-07 09:21:53 -040095 common::check_min_stack();
Alex Crichton86374772017-07-07 20:39:28 -070096 common::clone_rust();
Michael Layzell53fc31a2017-06-07 09:21:53 -040097 let abort_after = common::abort_after();
98 if abort_after == 0 {
99 panic!("Skipping all precedence tests");
100 }
101
David Tolnayee97dbf2017-11-19 14:24:38 -0800102 let passed = AtomicUsize::new(0);
103 let failed = AtomicUsize::new(0);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400104
David Tolnayd1c31cc2018-08-24 14:47:15 -0400105 // 2018 edition is hard
106 let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap();
107
David Tolnayee97dbf2017-11-19 14:24:38 -0800108 WalkDir::new("tests/rust")
Igor Gnatenko951a52b2018-03-12 10:33:33 +0100109 .sort_by(|a, b| a.file_name().cmp(b.file_name()))
David Tolnayee97dbf2017-11-19 14:24:38 -0800110 .into_iter()
111 .filter_entry(common::base_dir_filter)
112 .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
113 .unwrap()
114 .into_par_iter()
David Tolnay51382052017-12-27 13:46:21 -0500115 .for_each(|entry| {
116 let path = entry.path();
117 if path.is_dir() {
118 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400119 }
David Tolnay51382052017-12-27 13:46:21 -0500120
121 // Our version of `libsyntax` can't parse this tests
David Tolnay65fb5662018-05-20 20:02:28 -0700122 if path
123 .to_str()
David Tolnay51382052017-12-27 13:46:21 -0500124 .unwrap()
125 .ends_with("optional_comma_in_match_arm.rs")
126 {
127 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400128 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400129
David Tolnay51382052017-12-27 13:46:21 -0500130 let mut file = File::open(path).unwrap();
131 let mut content = String::new();
132 file.read_to_string(&mut content).unwrap();
David Tolnayd1c31cc2018-08-24 14:47:15 -0400133 let content = edition_regex.replace_all(&content, "_$0");
Michael Layzell53fc31a2017-06-07 09:21:53 -0400134
David Tolnay51382052017-12-27 13:46:21 -0500135 let (l_passed, l_failed) = match syn::parse_file(&content) {
136 Ok(file) => {
137 let exprs = collect_exprs(file);
138 test_expressions(exprs)
139 }
140 Err(msg) => {
141 errorf!("syn failed to parse\n{:?}\n", msg);
142 (0, 1)
143 }
144 };
David Tolnayee97dbf2017-11-19 14:24:38 -0800145
David Tolnay51382052017-12-27 13:46:21 -0500146 errorf!(
147 "=== {}: {} passed | {} failed\n",
148 path.display(),
149 l_passed,
150 l_failed
151 );
152
153 passed.fetch_add(l_passed, Ordering::SeqCst);
154 let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);
155
156 if prev_failed + l_failed >= abort_after {
157 process::exit(1);
158 }
159 });
David Tolnayee97dbf2017-11-19 14:24:38 -0800160
161 let passed = passed.load(Ordering::SeqCst);
162 let failed = failed.load(Ordering::SeqCst);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400163
164 errorf!("\n===== Precedence Test Results =====\n");
165 errorf!("{} passed | {} failed\n", passed, failed);
166
Michael Layzell53fc31a2017-06-07 09:21:53 -0400167 if failed > 0 {
168 panic!("{} failures", failed);
169 }
170}
171
David Tolnayee97dbf2017-11-19 14:24:38 -0800172fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400173 let mut passed = 0;
174 let mut failed = 0;
175
David Tolnayeb7d79b2018-03-31 22:52:17 +0200176 syntax::with_globals(|| {
177 for expr in exprs {
178 let raw = quote!(#expr).to_string();
Michael Layzell53fc31a2017-06-07 09:21:53 -0400179
David Tolnayeb7d79b2018-03-31 22:52:17 +0200180 let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
181 e
182 } else {
183 failed += 1;
184 errorf!("\nFAIL - libsyntax failed to parse raw\n");
185 continue;
186 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400187
David Tolnayeb7d79b2018-03-31 22:52:17 +0200188 let syn_expr = syn_brackets(expr);
189 let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
190 e
191 } else {
192 failed += 1;
193 errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
194 continue;
195 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400196
David Tolnayecd024d2018-07-21 09:07:56 -0700197 if SpanlessEq::eq(&syn_ast, &libsyntax_ast) {
David Tolnayeb7d79b2018-03-31 22:52:17 +0200198 passed += 1;
199 } else {
200 failed += 1;
201 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
202 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400203 }
David Tolnay5d1a3ee2018-03-17 19:41:36 -0700204 });
Michael Layzell53fc31a2017-06-07 09:21:53 -0400205
206 (passed, failed)
207}
208
David Tolnaycfa5cc02017-11-13 01:05:11 -0800209fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
David Tolnay3cede942017-12-26 12:29:24 -0500210 parse::libsyntax_expr(input).and_then(libsyntax_brackets)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400211}
212
213/// Wrap every expression which is not already wrapped in parens with parens, to
David Tolnay0ccb6d12018-08-14 22:43:00 -0700214/// reveal the precidence of the parsed expressions, and produce a stringified
215/// form of the resulting expression.
Michael Layzell53fc31a2017-06-07 09:21:53 -0400216///
David Tolnaycfa5cc02017-11-13 01:05:11 -0800217/// This method operates on libsyntax objects.
218fn libsyntax_brackets(libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
David Tolnayc8659922018-08-14 22:40:50 -0700219 use rustc_data_structures::small_vec::OneVector;
220 use rustc_data_structures::thin_vec::ThinVec;
David Tolnay51382052017-12-27 13:46:21 -0500221 use syntax::ast::{Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
David Tolnayeb7d79b2018-03-31 22:52:17 +0200222 use syntax::ext::quote::rt::DUMMY_SP;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800223 use syntax::fold::{self, Folder};
Michael Layzell53fc31a2017-06-07 09:21:53 -0400224
Michael Layzell53fc31a2017-06-07 09:21:53 -0400225 struct BracketsFolder {
226 failed: bool,
227 };
228 impl Folder for BracketsFolder {
229 fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
David Tolnay5d314dc2018-07-21 16:40:01 -0700230 e.map(|e| match e.node {
David Tolnay61e15e52018-08-01 00:28:36 -0700231 ExprKind::Block(_, label) if label.is_some() => Expr {
232 id: ast::DUMMY_NODE_ID,
233 node: ExprKind::Paren(P(e)),
234 span: DUMMY_SP,
235 attrs: ThinVec::new(),
236 },
David Tolnay0c7fed62018-08-24 15:56:24 -0400237 ExprKind::TryBlock(_) => Expr {
238 id: ast::DUMMY_NODE_ID,
239 node: ExprKind::Paren(P(e)),
240 span: DUMMY_SP,
241 attrs: ThinVec::new(),
242 },
David Tolnay5d314dc2018-07-21 16:40:01 -0700243 ExprKind::If(..) | ExprKind::Block(..) | ExprKind::IfLet(..) => {
244 fold::noop_fold_expr(e, self)
245 }
246 _ => Expr {
247 id: ast::DUMMY_NODE_ID,
248 node: ExprKind::Paren(P(fold::noop_fold_expr(e, self))),
249 span: DUMMY_SP,
250 attrs: ThinVec::new(),
David Tolnay51382052017-12-27 13:46:21 -0500251 },
Michael Layzell53fc31a2017-06-07 09:21:53 -0400252 })
253 }
254
255 fn fold_field(&mut self, f: Field) -> Field {
256 Field {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400257 expr: if f.is_shorthand {
258 f.expr.map(|e| fold::noop_fold_expr(e, self))
259 } else {
260 self.fold_expr(f.expr)
261 },
David Tolnaya3174992018-04-06 22:41:59 -0700262 ..f
Michael Layzell53fc31a2017-06-07 09:21:53 -0400263 }
264 }
265
266 // We don't want to look at expressions that might appear in patterns or
267 // types yet. We'll look into comparing those in the future. For now
268 // focus on expressions appearing in other places.
269 fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
270 pat
271 }
272
273 fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
274 ty
275 }
276
David Tolnayc8659922018-08-14 22:40:50 -0700277 fn fold_stmt(&mut self, stmt: Stmt) -> OneVector<Stmt> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400278 let node = match stmt.node {
279 // Don't wrap toplevel expressions in statements.
David Tolnay51382052017-12-27 13:46:21 -0500280 StmtKind::Expr(e) => StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self))),
281 StmtKind::Semi(e) => StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self))),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400282 s => s,
283 };
284
David Tolnayd1c31cc2018-08-24 14:47:15 -0400285 OneVector::from_vec(vec![Stmt { node, ..stmt }])
Michael Layzell53fc31a2017-06-07 09:21:53 -0400286 }
287
288 fn fold_mac(&mut self, mac: Mac) -> Mac {
David Tolnaycfa5cc02017-11-13 01:05:11 -0800289 // By default when folding over macros, libsyntax panics. This is
Michael Layzell53fc31a2017-06-07 09:21:53 -0400290 // because it's usually not what you want, you want to run after
291 // macro expansion. We do want to do that (syn doesn't do macro
292 // expansion), so we implement fold_mac to just return the macro
293 // unchanged.
294 mac
295 }
296 }
297
David Tolnay51382052017-12-27 13:46:21 -0500298 let mut folder = BracketsFolder { failed: false };
David Tolnaycfa5cc02017-11-13 01:05:11 -0800299 let e = folder.fold_expr(libsyntax_expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400300 if folder.failed {
301 None
302 } else {
303 Some(e)
304 }
305}
306
307/// Wrap every expression which is not already wrapped in parens with parens, to
David Tolnay0ccb6d12018-08-14 22:43:00 -0700308/// reveal the precedence of the parsed expressions, and produce a stringified
309/// form of the resulting expression.
Michael Layzell53fc31a2017-06-07 09:21:53 -0400310fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400311 use syn::fold::*;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200312 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400313
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!(),
David Tolnay61037c62018-01-05 16:21:03 -0800319 Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::IfLet(..) => {
David Tolnay3bc597f2017-12-31 02:31:11 -0500320 fold_expr(self, expr)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400321 }
David Tolnay5d314dc2018-07-21 16:40:01 -0700322 node => Expr::Paren(ExprParen {
323 attrs: Vec::new(),
324 expr: Box::new(fold_expr(self, node)),
325 paren_token: token::Paren::default(),
326 }),
David Tolnay8c91b882017-12-28 23:04:32 -0500327 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400328 }
329
330 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
331 match stmt {
332 // Don't wrap toplevel expressions in statements.
David Tolnay1f0b7b82018-01-06 16:07:14 -0800333 Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
334 Stmt::Semi(e, semi) => Stmt::Semi(fold_expr(self, e), semi),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400335 s => s,
336 }
337 }
338
339 // We don't want to look at expressions that might appear in patterns or
340 // types yet. We'll look into comparing those in the future. For now
341 // focus on expressions appearing in other places.
342 fn fold_pat(&mut self, pat: Pat) -> Pat {
343 pat
344 }
345
David Tolnayfd6bf5c2017-11-12 09:41:14 -0800346 fn fold_type(&mut self, ty: Type) -> Type {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400347 ty
348 }
349 }
350
David Tolnayeb752062018-01-06 13:51:42 -0800351 let mut folder = ParenthesizeEveryExpr;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400352 folder.fold_expr(syn_expr)
353}
354
355/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700356fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400357 use syn::fold::*;
David Tolnayf2cfd722017-12-31 18:02:51 -0500358 use syn::punctuated::Punctuated;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200359 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400360
David Tolnayeb752062018-01-06 13:51:42 -0800361 struct CollectExprs(Vec<Expr>);
362 impl Fold for CollectExprs {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400363 fn fold_expr(&mut self, expr: Expr) -> Expr {
364 self.0.push(expr);
365
David Tolnay8c91b882017-12-28 23:04:32 -0500366 Expr::Tuple(ExprTuple {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400367 attrs: vec![],
David Tolnayf2cfd722017-12-31 18:02:51 -0500368 elems: Punctuated::new(),
David Tolnay8c91b882017-12-28 23:04:32 -0500369 paren_token: token::Paren::default(),
370 })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400371 }
372 }
373
David Tolnayeb752062018-01-06 13:51:42 -0800374 let mut folder = CollectExprs(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700375 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400376 folder.0
377}