1 #![cfg(not(syn_disable_nightly_tests))]
3 #![recursion_limit = "1024"]
4 #![feature(rustc_private)]
6 clippy
::explicit_deref_methods
,
8 clippy
::match_wildcard_for_single_variants
,
12 //! The tests in this module do the following:
14 //! 1. Parse a given expression in both `syn` and `librustc`.
15 //! 2. Fold over the expression adding brackets around each subexpression (with
16 //! some complications - see the `syn_brackets` and `librustc_brackets`
18 //! 3. Serialize the `syn` expression back into a string, and re-parse it with
20 //! 4. Respan all of the expressions, replacing the spans with the default
22 //! 5. Compare the expressions with one another, if they are not equal fail.
24 extern crate rustc_ast
;
25 extern crate rustc_data_structures
;
26 extern crate rustc_span
;
28 use crate::common
::eq
::SpanlessEq
;
29 use crate::common
::parse
;
31 use rayon
::iter
::{IntoParallelIterator, ParallelIterator}
;
34 use rustc_ast
::ptr
::P
;
35 use rustc_span
::edition
::Edition
;
38 use std
::sync
::atomic
::{AtomicUsize, Ordering}
;
39 use walkdir
::{DirEntry, WalkDir}
;
49 /// Test some pre-set expressions chosen by us.
51 fn test_simple_precedence() {
52 const EXPRS
: &[&str] = &[
55 "{ for i in r { } *some_ptr += 1; }",
56 "{ loop { break 5; } }",
57 "{ if true { () }.mthd() }",
58 "{ for i in unsafe { 20 } { } }",
64 let expr
= if let Some(expr
) = parse
::syn_expr(input
) {
71 let pf
= match test_expressions(Edition
::Edition2018
, vec
![expr
]) {
79 errorf
!("=== {}: {}\n", input
, pf
);
83 panic
!("Failed {} tests", failed
);
87 /// Test expressions from rustc, like in `test_round_trip`.
89 fn test_rustc_precedence() {
92 let abort_after
= common
::abort_after();
94 panic
!("Skipping all precedence tests");
97 let passed
= AtomicUsize
::new(0);
98 let failed
= AtomicUsize
::new(0);
100 // 2018 edition is hard
101 let edition_regex
= Regex
::new(r
"\b(async|try)[!(]").unwrap();
103 WalkDir
::new("tests/rust")
104 .sort_by(|a
, b
| a
.file_name().cmp(b
.file_name()))
106 .filter_entry(repo
::base_dir_filter
)
107 .collect
::<Result
<Vec
<DirEntry
>, walkdir
::Error
>>()
111 let path
= entry
.path();
116 let content
= fs
::read_to_string(path
).unwrap();
117 let content
= edition_regex
.replace_all(&content
, "_$0");
119 let (l_passed
, l_failed
) = match syn
::parse_file(&content
) {
121 let edition
= repo
::edition(path
).parse().unwrap();
122 let exprs
= collect_exprs(file
);
123 test_expressions(edition
, exprs
)
126 errorf
!("syn failed to parse\n{:?}\n", msg
);
132 "=== {}: {} passed | {} failed\n",
138 passed
.fetch_add(l_passed
, Ordering
::Relaxed
);
139 let prev_failed
= failed
.fetch_add(l_failed
, Ordering
::Relaxed
);
141 if prev_failed
+ l_failed
>= abort_after
{
146 let passed
= passed
.load(Ordering
::Relaxed
);
147 let failed
= failed
.load(Ordering
::Relaxed
);
149 errorf
!("\n===== Precedence Test Results =====\n");
150 errorf
!("{} passed | {} failed\n", passed
, failed
);
153 panic
!("{} failures", failed
);
157 fn test_expressions(edition
: Edition
, exprs
: Vec
<syn
::Expr
>) -> (usize, usize) {
161 rustc_span
::create_session_if_not_set_then(edition
, |_
| {
163 let raw
= quote
!(#expr).to_string();
165 let librustc_ast
= if let Some(e
) = librustc_parse_and_rewrite(&raw
) {
169 errorf
!("\nFAIL - librustc failed to parse raw\n");
173 let syn_expr
= syn_brackets(expr
);
174 let syn_ast
= if let Some(e
) = parse
::librustc_expr("e
!(#syn_expr).to_string()) {
178 errorf
!("\nFAIL - librustc failed to parse bracketed\n");
182 if SpanlessEq
::eq(&syn_ast
, &librustc_ast
) {
186 errorf
!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast
, librustc_ast
);
194 fn librustc_parse_and_rewrite(input
: &str) -> Option
<P
<ast
::Expr
>> {
195 parse
::librustc_expr(input
).and_then(librustc_brackets
)
198 /// Wrap every expression which is not already wrapped in parens with parens, to
199 /// reveal the precedence of the parsed expressions, and produce a stringified
200 /// form of the resulting expression.
202 /// This method operates on librustc objects.
203 fn librustc_brackets(mut librustc_expr
: P
<ast
::Expr
>) -> Option
<P
<ast
::Expr
>> {
204 use rustc_ast
::ast
::{
205 Attribute
, Block
, BorrowKind
, Expr
, ExprField
, ExprKind
, GenericArg
, Local
, LocalKind
, Pat
,
206 Stmt
, StmtKind
, StructExpr
, StructRest
, Ty
,
208 use rustc_ast
::mut_visit
::{noop_visit_generic_arg, noop_visit_local, MutVisitor}
;
209 use rustc_data_structures
::map_in_place
::MapInPlace
;
210 use rustc_data_structures
::thin_vec
::ThinVec
;
211 use rustc_span
::DUMMY_SP
;
213 use std
::ops
::DerefMut
;
215 struct BracketsVisitor
{
219 fn flat_map_field
<T
: MutVisitor
>(mut f
: ExprField
, vis
: &mut T
) -> Vec
<ExprField
> {
221 noop_visit_expr(&mut f
.expr
, vis
);
223 vis
.visit_expr(&mut f
.expr
);
228 fn flat_map_stmt
<T
: MutVisitor
>(stmt
: Stmt
, vis
: &mut T
) -> Vec
<Stmt
> {
229 let kind
= match stmt
.kind
{
230 // Don't wrap toplevel expressions in statements.
231 StmtKind
::Expr(mut e
) => {
232 noop_visit_expr(&mut e
, vis
);
235 StmtKind
::Semi(mut e
) => {
236 noop_visit_expr(&mut e
, vis
);
242 vec
![Stmt { kind, ..stmt }
]
245 fn noop_visit_expr
<T
: MutVisitor
>(e
: &mut Expr
, vis
: &mut T
) {
246 use rustc_ast
::mut_visit
::{noop_visit_expr, visit_thin_attrs}
;
248 ExprKind
::AddrOf(BorrowKind
::Raw
, ..) => {}
249 ExprKind
::Struct(expr
) => {
255 } = expr
.deref_mut();
256 vis
.visit_qself(qself
);
257 vis
.visit_path(path
);
258 fields
.flat_map_in_place(|field
| flat_map_field(field
, vis
));
259 if let StructRest
::Base(rest
) = rest
{
260 vis
.visit_expr(rest
);
262 vis
.visit_id(&mut e
.id
);
263 vis
.visit_span(&mut e
.span
);
264 visit_thin_attrs(&mut e
.attrs
, vis
);
266 _
=> noop_visit_expr(e
, vis
),
270 impl MutVisitor
for BracketsVisitor
{
271 fn visit_expr(&mut self, e
: &mut P
<Expr
>) {
273 ExprKind
::ConstBlock(..) => {}
274 _
=> noop_visit_expr(e
, self),
277 ExprKind
::If(..) | ExprKind
::Block(..) | ExprKind
::Let(..) => {}
279 let inner
= mem
::replace(
282 id
: ast
::DUMMY_NODE_ID
,
285 attrs
: ThinVec
::new(),
289 e
.kind
= ExprKind
::Paren(inner
);
294 fn visit_generic_arg(&mut self, arg
: &mut GenericArg
) {
296 // Don't wrap unbraced const generic arg as that's invalid syntax.
297 GenericArg
::Const(anon_const
) => {
298 if let ExprKind
::Block(..) = &mut anon_const
.value
.kind
{
299 noop_visit_expr(&mut anon_const
.value
, self);
302 _
=> noop_visit_generic_arg(arg
, self),
306 fn visit_block(&mut self, block
: &mut P
<Block
>) {
307 self.visit_id(&mut block
.id
);
310 .flat_map_in_place(|stmt
| flat_map_stmt(stmt
, self));
311 self.visit_span(&mut block
.span
);
314 fn visit_local(&mut self, local
: &mut P
<Local
>) {
316 LocalKind
::InitElse(..) => {}
317 _
=> noop_visit_local(local
, self),
321 // We don't want to look at expressions that might appear in patterns or
322 // types yet. We'll look into comparing those in the future. For now
323 // focus on expressions appearing in other places.
324 fn visit_pat(&mut self, pat
: &mut P
<Pat
>) {
328 fn visit_ty(&mut self, ty
: &mut P
<Ty
>) {
332 fn visit_attribute(&mut self, attr
: &mut Attribute
) {
337 let mut folder
= BracketsVisitor { failed: false }
;
338 folder
.visit_expr(&mut librustc_expr
);
346 /// Wrap every expression which is not already wrapped in parens with parens, to
347 /// reveal the precedence of the parsed expressions, and produce a stringified
348 /// form of the resulting expression.
349 fn syn_brackets(syn_expr
: syn
::Expr
) -> syn
::Expr
{
350 use syn
::fold
::{fold_expr, fold_generic_argument, fold_generic_method_argument, Fold}
;
351 use syn
::{token, Expr, ExprParen, GenericArgument, GenericMethodArgument, Pat, Stmt, Type}
;
353 struct ParenthesizeEveryExpr
;
354 impl Fold
for ParenthesizeEveryExpr
{
355 fn fold_expr(&mut self, expr
: Expr
) -> Expr
{
357 Expr
::Group(_
) => unreachable
!(),
358 Expr
::If(..) | Expr
::Unsafe(..) | Expr
::Block(..) | Expr
::Let(..) => {
359 fold_expr(self, expr
)
361 _
=> Expr
::Paren(ExprParen
{
363 expr
: Box
::new(fold_expr(self, expr
)),
364 paren_token
: token
::Paren
::default(),
369 fn fold_generic_argument(&mut self, arg
: GenericArgument
) -> GenericArgument
{
371 GenericArgument
::Const(arg
) => GenericArgument
::Const(match arg
{
372 Expr
::Block(_
) => fold_expr(self, arg
),
373 // Don't wrap unbraced const generic arg as that's invalid syntax.
376 _
=> fold_generic_argument(self, arg
),
380 fn fold_generic_method_argument(
382 arg
: GenericMethodArgument
,
383 ) -> GenericMethodArgument
{
385 GenericMethodArgument
::Const(arg
) => GenericMethodArgument
::Const(match arg
{
386 Expr
::Block(_
) => fold_expr(self, arg
),
387 // Don't wrap unbraced const generic arg as that's invalid syntax.
390 _
=> fold_generic_method_argument(self, arg
),
394 fn fold_stmt(&mut self, stmt
: Stmt
) -> Stmt
{
396 // Don't wrap toplevel expressions in statements.
397 Stmt
::Expr(e
) => Stmt
::Expr(fold_expr(self, e
)),
398 Stmt
::Semi(e
, semi
) => {
399 if let Expr
::Verbatim(_
) = e
{
402 Stmt
::Semi(fold_expr(self, e
), semi
)
409 // We don't want to look at expressions that might appear in patterns or
410 // types yet. We'll look into comparing those in the future. For now
411 // focus on expressions appearing in other places.
412 fn fold_pat(&mut self, pat
: Pat
) -> Pat
{
416 fn fold_type(&mut self, ty
: Type
) -> Type
{
421 let mut folder
= ParenthesizeEveryExpr
;
422 folder
.fold_expr(syn_expr
)
425 /// Walk through a crate collecting all expressions we can find in it.
426 fn collect_exprs(file
: syn
::File
) -> Vec
<syn
::Expr
> {
428 use syn
::punctuated
::Punctuated
;
429 use syn
::{token, Expr, ExprTuple, Path}
;
431 struct CollectExprs(Vec
<Expr
>);
432 impl Fold
for CollectExprs
{
433 fn fold_expr(&mut self, expr
: Expr
) -> Expr
{
435 Expr
::Verbatim(_
) => {}
436 _
=> self.0.push(expr
),
439 Expr
::Tuple(ExprTuple
{
441 elems
: Punctuated
::new(),
442 paren_token
: token
::Paren
::default(),
446 fn fold_path(&mut self, path
: Path
) -> Path
{
447 // Skip traversing into const generic path arguments
452 let mut folder
= CollectExprs(vec
![]);
453 folder
.fold_file(file
);