blob: 03c925826e2190c86ac2ef4601c43239ab5f1144 [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`.
Michael Layzell53fc31a2017-06-07 09:21:53 -040021//! 4. Respan all of the expressions, replacing the spans with the default spans.
22//! 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;
Michael Layzell53fc31a2017-06-07 09:21:53 -040027extern crate syn;
David Tolnaycfa5cc02017-11-13 01:05:11 -080028extern crate syntax;
Michael Layzell53fc31a2017-06-07 09:21:53 -040029extern crate walkdir;
Michael Layzell53fc31a2017-06-07 09:21:53 -040030
David Tolnay51382052017-12-27 13:46:21 -050031use rayon::iter::{IntoParallelIterator, ParallelIterator};
David Tolnaycfa5cc02017-11-13 01:05:11 -080032use syntax::ast;
33use syntax::ptr::P;
Igor Gnatenko951a52b2018-03-12 10:33:33 +010034use walkdir::{DirEntry, WalkDir};
David Tolnayee97dbf2017-11-19 14:24:38 -080035
36use std::fs::File;
37use std::io::Read;
David Tolnay3eaf7d82017-12-17 23:14:52 -080038use std::process;
David Tolnayee97dbf2017-11-19 14:24:38 -080039use std::sync::atomic::{AtomicUsize, Ordering};
Michael Layzell53fc31a2017-06-07 09:21:53 -040040
David Tolnayecd024d2018-07-21 09:07:56 -070041use common::eq::SpanlessEq;
42use common::parse;
Michael Layzell53fc31a2017-06-07 09:21:53 -040043
Michael Layzell53fc31a2017-06-07 09:21:53 -040044#[macro_use]
David Tolnaydd125562017-12-31 02:16:22 -050045mod macros;
46
47#[allow(dead_code)]
Michael Layzell53fc31a2017-06-07 09:21:53 -040048mod common;
49
50/// Test some pre-set expressions chosen by us.
51#[test]
52fn test_simple_precedence() {
53 const EXPRS: &[&str] = &[
54 "1 + 2 * 3 + 4",
55 "1 + 2 * ( 3 + 4 )",
56 "{ for i in r { } *some_ptr += 1; }",
57 "{ loop { break 5; } }",
58 "{ if true { () }.mthd() }",
Nika Layzell3aa0dc72017-12-04 13:41:28 -050059 "{ for i in unsafe { 20 } { } }",
Michael Layzell53fc31a2017-06-07 09:21:53 -040060 ];
61
62 let mut failed = 0;
63
64 for input in EXPRS {
65 let expr = if let Some(expr) = parse::syn_expr(input) {
66 expr
67 } else {
68 failed += 1;
69 continue;
70 };
71
72 let pf = match test_expressions(vec![expr]) {
73 (1, 0) => "passed",
74 (0, 1) => {
75 failed += 1;
76 "failed"
77 }
78 _ => unreachable!(),
79 };
80 errorf!("=== {}: {}\n", input, pf);
81 }
82
83 if failed > 0 {
84 panic!("Failed {} tests", failed);
85 }
86}
87
88/// Test expressions from rustc, like in `test_round_trip`.
89#[test]
90fn test_rustc_precedence() {
Michael Layzell53fc31a2017-06-07 09:21:53 -040091 common::check_min_stack();
Alex Crichton86374772017-07-07 20:39:28 -070092 common::clone_rust();
Michael Layzell53fc31a2017-06-07 09:21:53 -040093 let abort_after = common::abort_after();
94 if abort_after == 0 {
95 panic!("Skipping all precedence tests");
96 }
97
David Tolnayee97dbf2017-11-19 14:24:38 -080098 let passed = AtomicUsize::new(0);
99 let failed = AtomicUsize::new(0);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400100
David Tolnayee97dbf2017-11-19 14:24:38 -0800101 WalkDir::new("tests/rust")
Igor Gnatenko951a52b2018-03-12 10:33:33 +0100102 .sort_by(|a, b| a.file_name().cmp(b.file_name()))
David Tolnayee97dbf2017-11-19 14:24:38 -0800103 .into_iter()
104 .filter_entry(common::base_dir_filter)
105 .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
106 .unwrap()
107 .into_par_iter()
David Tolnay51382052017-12-27 13:46:21 -0500108 .for_each(|entry| {
109 let path = entry.path();
110 if path.is_dir() {
111 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400112 }
David Tolnay51382052017-12-27 13:46:21 -0500113
114 // Our version of `libsyntax` can't parse this tests
David Tolnay65fb5662018-05-20 20:02:28 -0700115 if path
116 .to_str()
David Tolnay51382052017-12-27 13:46:21 -0500117 .unwrap()
118 .ends_with("optional_comma_in_match_arm.rs")
119 {
120 return;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400121 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400122
David Tolnay51382052017-12-27 13:46:21 -0500123 let mut file = File::open(path).unwrap();
124 let mut content = String::new();
125 file.read_to_string(&mut content).unwrap();
Michael Layzell53fc31a2017-06-07 09:21:53 -0400126
David Tolnay51382052017-12-27 13:46:21 -0500127 let (l_passed, l_failed) = match syn::parse_file(&content) {
128 Ok(file) => {
129 let exprs = collect_exprs(file);
130 test_expressions(exprs)
131 }
132 Err(msg) => {
133 errorf!("syn failed to parse\n{:?}\n", msg);
134 (0, 1)
135 }
136 };
David Tolnayee97dbf2017-11-19 14:24:38 -0800137
David Tolnay51382052017-12-27 13:46:21 -0500138 errorf!(
139 "=== {}: {} passed | {} failed\n",
140 path.display(),
141 l_passed,
142 l_failed
143 );
144
145 passed.fetch_add(l_passed, Ordering::SeqCst);
146 let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);
147
148 if prev_failed + l_failed >= abort_after {
149 process::exit(1);
150 }
151 });
David Tolnayee97dbf2017-11-19 14:24:38 -0800152
153 let passed = passed.load(Ordering::SeqCst);
154 let failed = failed.load(Ordering::SeqCst);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400155
156 errorf!("\n===== Precedence Test Results =====\n");
157 errorf!("{} passed | {} failed\n", passed, failed);
158
Michael Layzell53fc31a2017-06-07 09:21:53 -0400159 if failed > 0 {
160 panic!("{} failures", failed);
161 }
162}
163
David Tolnayee97dbf2017-11-19 14:24:38 -0800164fn test_expressions(exprs: Vec<syn::Expr>) -> (usize, usize) {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400165 let mut passed = 0;
166 let mut failed = 0;
167
David Tolnayeb7d79b2018-03-31 22:52:17 +0200168 syntax::with_globals(|| {
169 for expr in exprs {
170 let raw = quote!(#expr).to_string();
Michael Layzell53fc31a2017-06-07 09:21:53 -0400171
David Tolnayeb7d79b2018-03-31 22:52:17 +0200172 let libsyntax_ast = if let Some(e) = libsyntax_parse_and_rewrite(&raw) {
173 e
174 } else {
175 failed += 1;
176 errorf!("\nFAIL - libsyntax failed to parse raw\n");
177 continue;
178 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400179
David Tolnayeb7d79b2018-03-31 22:52:17 +0200180 let syn_expr = syn_brackets(expr);
181 let syn_ast = if let Some(e) = parse::libsyntax_expr(&quote!(#syn_expr).to_string()) {
182 e
183 } else {
184 failed += 1;
185 errorf!("\nFAIL - libsyntax failed to parse bracketed\n");
186 continue;
187 };
Michael Layzell53fc31a2017-06-07 09:21:53 -0400188
David Tolnayecd024d2018-07-21 09:07:56 -0700189 if SpanlessEq::eq(&syn_ast, &libsyntax_ast) {
David Tolnayeb7d79b2018-03-31 22:52:17 +0200190 passed += 1;
191 } else {
192 failed += 1;
193 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, libsyntax_ast);
194 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400195 }
David Tolnay5d1a3ee2018-03-17 19:41:36 -0700196 });
Michael Layzell53fc31a2017-06-07 09:21:53 -0400197
198 (passed, failed)
199}
200
David Tolnaycfa5cc02017-11-13 01:05:11 -0800201fn libsyntax_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
David Tolnay3cede942017-12-26 12:29:24 -0500202 parse::libsyntax_expr(input).and_then(libsyntax_brackets)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400203}
204
205/// Wrap every expression which is not already wrapped in parens with parens, to
206/// reveal the precidence of the parsed expressions, and produce a stringified form
207/// of the resulting expression.
208///
David Tolnaycfa5cc02017-11-13 01:05:11 -0800209/// This method operates on libsyntax objects.
210fn libsyntax_brackets(libsyntax_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
David Tolnay51382052017-12-27 13:46:21 -0500211 use syntax::ast::{Expr, ExprKind, Field, Mac, Pat, Stmt, StmtKind, Ty};
David Tolnayeb7d79b2018-03-31 22:52:17 +0200212 use syntax::ext::quote::rt::DUMMY_SP;
David Tolnaycfa5cc02017-11-13 01:05:11 -0800213 use syntax::fold::{self, Folder};
David Tolnaycfa5cc02017-11-13 01:05:11 -0800214 use syntax::util::small_vector::SmallVector;
David Tolnay94d2b792018-04-29 12:26:10 -0700215 use syntax::util::ThinVec;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400216
Michael Layzell53fc31a2017-06-07 09:21:53 -0400217 struct BracketsFolder {
218 failed: bool,
219 };
220 impl Folder for BracketsFolder {
221 fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
David Tolnay5d314dc2018-07-21 16:40:01 -0700222 e.map(|e| match e.node {
223 ExprKind::If(..) | ExprKind::Block(..) | ExprKind::IfLet(..) => {
224 fold::noop_fold_expr(e, self)
225 }
226 _ => Expr {
227 id: ast::DUMMY_NODE_ID,
228 node: ExprKind::Paren(P(fold::noop_fold_expr(e, self))),
229 span: DUMMY_SP,
230 attrs: ThinVec::new(),
David Tolnay51382052017-12-27 13:46:21 -0500231 },
Michael Layzell53fc31a2017-06-07 09:21:53 -0400232 })
233 }
234
235 fn fold_field(&mut self, f: Field) -> Field {
236 Field {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400237 expr: if f.is_shorthand {
238 f.expr.map(|e| fold::noop_fold_expr(e, self))
239 } else {
240 self.fold_expr(f.expr)
241 },
David Tolnaya3174992018-04-06 22:41:59 -0700242 ..f
Michael Layzell53fc31a2017-06-07 09:21:53 -0400243 }
244 }
245
246 // We don't want to look at expressions that might appear in patterns or
247 // types yet. We'll look into comparing those in the future. For now
248 // focus on expressions appearing in other places.
249 fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
250 pat
251 }
252
253 fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
254 ty
255 }
256
257 fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
258 let node = match stmt.node {
259 // Don't wrap toplevel expressions in statements.
David Tolnay51382052017-12-27 13:46:21 -0500260 StmtKind::Expr(e) => StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self))),
261 StmtKind::Semi(e) => StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self))),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400262 s => s,
263 };
264
David Tolnay54bdb4f2018-03-17 13:00:42 -0700265 SmallVector::one(Stmt { node, ..stmt })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400266 }
267
268 fn fold_mac(&mut self, mac: Mac) -> Mac {
David Tolnaycfa5cc02017-11-13 01:05:11 -0800269 // By default when folding over macros, libsyntax panics. This is
Michael Layzell53fc31a2017-06-07 09:21:53 -0400270 // because it's usually not what you want, you want to run after
271 // macro expansion. We do want to do that (syn doesn't do macro
272 // expansion), so we implement fold_mac to just return the macro
273 // unchanged.
274 mac
275 }
276 }
277
David Tolnay51382052017-12-27 13:46:21 -0500278 let mut folder = BracketsFolder { failed: false };
David Tolnaycfa5cc02017-11-13 01:05:11 -0800279 let e = folder.fold_expr(libsyntax_expr);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400280 if folder.failed {
281 None
282 } else {
283 Some(e)
284 }
285}
286
287/// Wrap every expression which is not already wrapped in parens with parens, to
David Tolnayeb752062018-01-06 13:51:42 -0800288/// reveal the precedence of the parsed expressions, and produce a stringified form
Michael Layzell53fc31a2017-06-07 09:21:53 -0400289/// of the resulting expression.
290fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400291 use syn::fold::*;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200292 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400293
David Tolnayeb752062018-01-06 13:51:42 -0800294 struct ParenthesizeEveryExpr;
295 impl Fold for ParenthesizeEveryExpr {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400296 fn fold_expr(&mut self, expr: Expr) -> Expr {
David Tolnay8c91b882017-12-28 23:04:32 -0500297 match expr {
298 Expr::Group(_) => unreachable!(),
David Tolnay61037c62018-01-05 16:21:03 -0800299 Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::IfLet(..) => {
David Tolnay3bc597f2017-12-31 02:31:11 -0500300 fold_expr(self, expr)
Michael Layzell53fc31a2017-06-07 09:21:53 -0400301 }
David Tolnay5d314dc2018-07-21 16:40:01 -0700302 node => Expr::Paren(ExprParen {
303 attrs: Vec::new(),
304 expr: Box::new(fold_expr(self, node)),
305 paren_token: token::Paren::default(),
306 }),
David Tolnay8c91b882017-12-28 23:04:32 -0500307 }
Michael Layzell53fc31a2017-06-07 09:21:53 -0400308 }
309
310 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
311 match stmt {
312 // Don't wrap toplevel expressions in statements.
David Tolnay1f0b7b82018-01-06 16:07:14 -0800313 Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
314 Stmt::Semi(e, semi) => Stmt::Semi(fold_expr(self, e), semi),
Michael Layzell53fc31a2017-06-07 09:21:53 -0400315 s => s,
316 }
317 }
318
319 // We don't want to look at expressions that might appear in patterns or
320 // types yet. We'll look into comparing those in the future. For now
321 // focus on expressions appearing in other places.
322 fn fold_pat(&mut self, pat: Pat) -> Pat {
323 pat
324 }
325
David Tolnayfd6bf5c2017-11-12 09:41:14 -0800326 fn fold_type(&mut self, ty: Type) -> Type {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400327 ty
328 }
329 }
330
David Tolnayeb752062018-01-06 13:51:42 -0800331 let mut folder = ParenthesizeEveryExpr;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400332 folder.fold_expr(syn_expr)
333}
334
335/// Walk through a crate collecting all expressions we can find in it.
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700336fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400337 use syn::fold::*;
David Tolnayf2cfd722017-12-31 18:02:51 -0500338 use syn::punctuated::Punctuated;
David Tolnayeb7d79b2018-03-31 22:52:17 +0200339 use syn::*;
Michael Layzell53fc31a2017-06-07 09:21:53 -0400340
David Tolnayeb752062018-01-06 13:51:42 -0800341 struct CollectExprs(Vec<Expr>);
342 impl Fold for CollectExprs {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400343 fn fold_expr(&mut self, expr: Expr) -> Expr {
344 self.0.push(expr);
345
David Tolnay8c91b882017-12-28 23:04:32 -0500346 Expr::Tuple(ExprTuple {
Michael Layzell53fc31a2017-06-07 09:21:53 -0400347 attrs: vec![],
David Tolnayf2cfd722017-12-31 18:02:51 -0500348 elems: Punctuated::new(),
David Tolnay8c91b882017-12-28 23:04:32 -0500349 paren_token: token::Paren::default(),
350 })
Michael Layzell53fc31a2017-06-07 09:21:53 -0400351 }
352 }
353
David Tolnayeb752062018-01-06 13:51:42 -0800354 let mut folder = CollectExprs(vec![]);
David Tolnayc7a5d3d2017-06-04 12:11:05 -0700355 folder.fold_file(file);
Michael Layzell53fc31a2017-06-07 09:21:53 -0400356 folder.0
357}