]> git.proxmox.com Git - rustc.git/blame - vendor/syn/tests/test_precedence.rs
New upstream version 1.51.0+dfsg1
[rustc.git] / vendor / syn / tests / test_precedence.rs
CommitLineData
e74abb32
XL
1#![cfg(not(syn_disable_nightly_tests))]
2#![recursion_limit = "1024"]
3#![feature(rustc_private)]
4
5//! The tests in this module do the following:
6//!
f035d41b 7//! 1. Parse a given expression in both `syn` and `librustc`.
e74abb32 8//! 2. Fold over the expression adding brackets around each subexpression (with
f035d41b 9//! some complications - see the `syn_brackets` and `librustc_brackets`
e74abb32
XL
10//! methods).
11//! 3. Serialize the `syn` expression back into a string, and re-parse it with
f035d41b 12//! `librustc`.
e74abb32
XL
13//! 4. Respan all of the expressions, replacing the spans with the default
14//! spans.
15//! 5. Compare the expressions with one another, if they are not equal fail.
16
f035d41b 17extern crate rustc_ast;
e74abb32 18extern crate rustc_data_structures;
f035d41b 19extern crate rustc_span;
e74abb32 20
29967ef6
XL
21use crate::common::eq::SpanlessEq;
22use crate::common::parse;
e74abb32
XL
23use quote::quote;
24use rayon::iter::{IntoParallelIterator, ParallelIterator};
25use regex::Regex;
f035d41b
XL
26use rustc_ast::ast;
27use rustc_ast::ptr::P;
28use rustc_span::edition::Edition;
5869c6ff 29use std::fs;
e74abb32
XL
30use std::process;
31use std::sync::atomic::{AtomicUsize, Ordering};
29967ef6 32use walkdir::{DirEntry, WalkDir};
e74abb32
XL
33
34#[macro_use]
35mod macros;
36
37#[allow(dead_code)]
38mod common;
39
40mod repo;
41
42/// Test some pre-set expressions chosen by us.
43#[test]
44fn test_simple_precedence() {
45 const EXPRS: &[&str] = &[
46 "1 + 2 * 3 + 4",
47 "1 + 2 * ( 3 + 4 )",
48 "{ for i in r { } *some_ptr += 1; }",
49 "{ loop { break 5; } }",
50 "{ if true { () }.mthd() }",
51 "{ for i in unsafe { 20 } { } }",
52 ];
53
54 let mut failed = 0;
55
56 for input in EXPRS {
57 let expr = if let Some(expr) = parse::syn_expr(input) {
58 expr
59 } else {
60 failed += 1;
61 continue;
62 };
63
f035d41b 64 let pf = match test_expressions(Edition::Edition2018, vec![expr]) {
e74abb32
XL
65 (1, 0) => "passed",
66 (0, 1) => {
67 failed += 1;
68 "failed"
69 }
70 _ => unreachable!(),
71 };
72 errorf!("=== {}: {}\n", input, pf);
73 }
74
75 if failed > 0 {
76 panic!("Failed {} tests", failed);
77 }
78}
79
80/// Test expressions from rustc, like in `test_round_trip`.
81#[test]
e74abb32 82fn test_rustc_precedence() {
f035d41b 83 common::rayon_init();
e74abb32
XL
84 repo::clone_rust();
85 let abort_after = common::abort_after();
86 if abort_after == 0 {
87 panic!("Skipping all precedence tests");
88 }
89
90 let passed = AtomicUsize::new(0);
91 let failed = AtomicUsize::new(0);
92
93 // 2018 edition is hard
94 let edition_regex = Regex::new(r"\b(async|try)[!(]").unwrap();
95
96 WalkDir::new("tests/rust")
97 .sort_by(|a, b| a.file_name().cmp(b.file_name()))
98 .into_iter()
99 .filter_entry(repo::base_dir_filter)
100 .collect::<Result<Vec<DirEntry>, walkdir::Error>>()
101 .unwrap()
102 .into_par_iter()
103 .for_each(|entry| {
104 let path = entry.path();
105 if path.is_dir() {
106 return;
107 }
108
5869c6ff 109 let content = fs::read_to_string(path).unwrap();
e74abb32
XL
110 let content = edition_regex.replace_all(&content, "_$0");
111
112 let (l_passed, l_failed) = match syn::parse_file(&content) {
113 Ok(file) => {
f035d41b 114 let edition = repo::edition(path).parse().unwrap();
e74abb32 115 let exprs = collect_exprs(file);
f035d41b 116 test_expressions(edition, exprs)
e74abb32
XL
117 }
118 Err(msg) => {
119 errorf!("syn failed to parse\n{:?}\n", msg);
120 (0, 1)
121 }
122 };
123
124 errorf!(
125 "=== {}: {} passed | {} failed\n",
126 path.display(),
127 l_passed,
128 l_failed
129 );
130
131 passed.fetch_add(l_passed, Ordering::SeqCst);
132 let prev_failed = failed.fetch_add(l_failed, Ordering::SeqCst);
133
134 if prev_failed + l_failed >= abort_after {
135 process::exit(1);
136 }
137 });
138
139 let passed = passed.load(Ordering::SeqCst);
140 let failed = failed.load(Ordering::SeqCst);
141
142 errorf!("\n===== Precedence Test Results =====\n");
143 errorf!("{} passed | {} failed\n", passed, failed);
144
145 if failed > 0 {
146 panic!("{} failures", failed);
147 }
148}
149
f035d41b 150fn test_expressions(edition: Edition, exprs: Vec<syn::Expr>) -> (usize, usize) {
e74abb32
XL
151 let mut passed = 0;
152 let mut failed = 0;
153
1b1a35ee 154 rustc_span::with_session_globals(edition, || {
e74abb32
XL
155 for expr in exprs {
156 let raw = quote!(#expr).to_string();
157
f035d41b 158 let librustc_ast = if let Some(e) = librustc_parse_and_rewrite(&raw) {
e74abb32
XL
159 e
160 } else {
161 failed += 1;
f035d41b 162 errorf!("\nFAIL - librustc failed to parse raw\n");
e74abb32
XL
163 continue;
164 };
165
166 let syn_expr = syn_brackets(expr);
f035d41b 167 let syn_ast = if let Some(e) = parse::librustc_expr(&quote!(#syn_expr).to_string()) {
e74abb32
XL
168 e
169 } else {
170 failed += 1;
f035d41b 171 errorf!("\nFAIL - librustc failed to parse bracketed\n");
e74abb32
XL
172 continue;
173 };
174
f035d41b 175 if SpanlessEq::eq(&syn_ast, &librustc_ast) {
e74abb32
XL
176 passed += 1;
177 } else {
178 failed += 1;
f035d41b 179 errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, librustc_ast);
e74abb32
XL
180 }
181 }
182 });
183
184 (passed, failed)
185}
186
f035d41b
XL
187fn librustc_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
188 parse::librustc_expr(input).and_then(librustc_brackets)
e74abb32
XL
189}
190
191/// Wrap every expression which is not already wrapped in parens with parens, to
192/// reveal the precidence of the parsed expressions, and produce a stringified
193/// form of the resulting expression.
194///
f035d41b
XL
195/// This method operates on librustc objects.
196fn librustc_brackets(mut librustc_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
197 use rustc_ast::ast::{
5869c6ff 198 Block, BorrowKind, Expr, ExprKind, Field, GenericArg, Pat, Stmt, StmtKind, StructRest, Ty,
f035d41b
XL
199 };
200 use rustc_ast::mut_visit::{noop_visit_generic_arg, MutVisitor};
201 use rustc_data_structures::map_in_place::MapInPlace;
e74abb32 202 use rustc_data_structures::thin_vec::ThinVec;
f035d41b 203 use rustc_span::DUMMY_SP;
e74abb32 204 use std::mem;
e74abb32
XL
205
206 struct BracketsVisitor {
207 failed: bool,
5869c6ff 208 }
e74abb32 209
f035d41b
XL
210 fn flat_map_field<T: MutVisitor>(mut f: Field, vis: &mut T) -> Vec<Field> {
211 if f.is_shorthand {
212 noop_visit_expr(&mut f.expr, vis);
213 } else {
214 vis.visit_expr(&mut f.expr);
215 }
216 vec![f]
217 }
218
219 fn flat_map_stmt<T: MutVisitor>(stmt: Stmt, vis: &mut T) -> Vec<Stmt> {
220 let kind = match stmt.kind {
221 // Don't wrap toplevel expressions in statements.
222 StmtKind::Expr(mut e) => {
223 noop_visit_expr(&mut e, vis);
224 StmtKind::Expr(e)
225 }
226 StmtKind::Semi(mut e) => {
227 noop_visit_expr(&mut e, vis);
228 StmtKind::Semi(e)
229 }
230 s => s,
231 };
232
233 vec![Stmt { kind, ..stmt }]
234 }
235
236 fn noop_visit_expr<T: MutVisitor>(e: &mut Expr, vis: &mut T) {
5869c6ff 237 use rustc_ast::mut_visit::{noop_visit_expr, visit_thin_attrs};
f035d41b
XL
238 match &mut e.kind {
239 ExprKind::AddrOf(BorrowKind::Raw, ..) => {}
240 ExprKind::Struct(path, fields, expr) => {
241 vis.visit_path(path);
242 fields.flat_map_in_place(|field| flat_map_field(field, vis));
5869c6ff
XL
243 if let StructRest::Base(expr) = expr {
244 vis.visit_expr(expr);
245 }
f035d41b
XL
246 vis.visit_id(&mut e.id);
247 vis.visit_span(&mut e.span);
248 visit_thin_attrs(&mut e.attrs, vis);
249 }
250 _ => noop_visit_expr(e, vis),
251 }
252 }
253
e74abb32
XL
254 impl MutVisitor for BracketsVisitor {
255 fn visit_expr(&mut self, e: &mut P<Expr>) {
5869c6ff
XL
256 match e.kind {
257 ExprKind::ConstBlock(..) => {}
258 _ => noop_visit_expr(e, self),
259 }
60c5eb7d 260 match e.kind {
e74abb32
XL
261 ExprKind::If(..) | ExprKind::Block(..) | ExprKind::Let(..) => {}
262 _ => {
263 let inner = mem::replace(
264 e,
265 P(Expr {
266 id: ast::DUMMY_NODE_ID,
60c5eb7d 267 kind: ExprKind::Err,
e74abb32
XL
268 span: DUMMY_SP,
269 attrs: ThinVec::new(),
f035d41b 270 tokens: None,
e74abb32
XL
271 }),
272 );
60c5eb7d 273 e.kind = ExprKind::Paren(inner);
e74abb32
XL
274 }
275 }
276 }
277
f035d41b
XL
278 fn visit_generic_arg(&mut self, arg: &mut GenericArg) {
279 match arg {
280 // Don't wrap const generic arg as that's invalid syntax.
281 GenericArg::Const(arg) => noop_visit_expr(&mut arg.value, self),
282 _ => noop_visit_generic_arg(arg, self),
e74abb32 283 }
f035d41b
XL
284 }
285
286 fn visit_block(&mut self, block: &mut P<Block>) {
287 self.visit_id(&mut block.id);
288 block
289 .stmts
290 .flat_map_in_place(|stmt| flat_map_stmt(stmt, self));
291 self.visit_span(&mut block.span);
e74abb32
XL
292 }
293
294 // We don't want to look at expressions that might appear in patterns or
295 // types yet. We'll look into comparing those in the future. For now
296 // focus on expressions appearing in other places.
297 fn visit_pat(&mut self, pat: &mut P<Pat>) {
298 let _ = pat;
299 }
300
301 fn visit_ty(&mut self, ty: &mut P<Ty>) {
302 let _ = ty;
303 }
e74abb32
XL
304 }
305
306 let mut folder = BracketsVisitor { failed: false };
f035d41b 307 folder.visit_expr(&mut librustc_expr);
e74abb32
XL
308 if folder.failed {
309 None
310 } else {
f035d41b 311 Some(librustc_expr)
e74abb32
XL
312 }
313}
314
315/// Wrap every expression which is not already wrapped in parens with parens, to
316/// reveal the precedence of the parsed expressions, and produce a stringified
317/// form of the resulting expression.
318fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
319 use syn::fold::*;
320 use syn::*;
321
322 struct ParenthesizeEveryExpr;
323 impl Fold for ParenthesizeEveryExpr {
324 fn fold_expr(&mut self, expr: Expr) -> Expr {
325 match expr {
326 Expr::Group(_) => unreachable!(),
327 Expr::If(..) | Expr::Unsafe(..) | Expr::Block(..) | Expr::Let(..) => {
328 fold_expr(self, expr)
329 }
60c5eb7d 330 _ => Expr::Paren(ExprParen {
e74abb32 331 attrs: Vec::new(),
60c5eb7d 332 expr: Box::new(fold_expr(self, expr)),
e74abb32
XL
333 paren_token: token::Paren::default(),
334 }),
335 }
336 }
337
f035d41b
XL
338 fn fold_generic_argument(&mut self, arg: GenericArgument) -> GenericArgument {
339 match arg {
340 // Don't wrap const generic arg as that's invalid syntax.
341 GenericArgument::Const(a) => GenericArgument::Const(fold_expr(self, a)),
342 _ => fold_generic_argument(self, arg),
343 }
344 }
345
3dfed10e
XL
346 fn fold_generic_method_argument(
347 &mut self,
348 arg: GenericMethodArgument,
349 ) -> GenericMethodArgument {
350 match arg {
351 // Don't wrap const generic arg as that's invalid syntax.
352 GenericMethodArgument::Const(a) => GenericMethodArgument::Const(fold_expr(self, a)),
353 _ => fold_generic_method_argument(self, arg),
354 }
355 }
356
e74abb32
XL
357 fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
358 match stmt {
359 // Don't wrap toplevel expressions in statements.
360 Stmt::Expr(e) => Stmt::Expr(fold_expr(self, e)),
361 Stmt::Semi(e, semi) => Stmt::Semi(fold_expr(self, e), semi),
362 s => s,
363 }
364 }
365
366 // We don't want to look at expressions that might appear in patterns or
367 // types yet. We'll look into comparing those in the future. For now
368 // focus on expressions appearing in other places.
369 fn fold_pat(&mut self, pat: Pat) -> Pat {
370 pat
371 }
372
373 fn fold_type(&mut self, ty: Type) -> Type {
374 ty
375 }
376 }
377
378 let mut folder = ParenthesizeEveryExpr;
379 folder.fold_expr(syn_expr)
380}
381
382/// Walk through a crate collecting all expressions we can find in it.
383fn collect_exprs(file: syn::File) -> Vec<syn::Expr> {
384 use syn::fold::*;
385 use syn::punctuated::Punctuated;
386 use syn::*;
387
388 struct CollectExprs(Vec<Expr>);
389 impl Fold for CollectExprs {
390 fn fold_expr(&mut self, expr: Expr) -> Expr {
f035d41b
XL
391 match expr {
392 Expr::Verbatim(tokens) if tokens.is_empty() => {}
393 _ => self.0.push(expr),
394 }
e74abb32
XL
395
396 Expr::Tuple(ExprTuple {
397 attrs: vec![],
398 elems: Punctuated::new(),
399 paren_token: token::Paren::default(),
400 })
401 }
402 }
403
404 let mut folder = CollectExprs(vec![]);
405 folder.fold_file(file);
406 folder.0
407}