2 // ignore-cross-compile
4 // The general idea of this test is to enumerate all "interesting" expressions and check that
5 // `parse(print(e)) == e` for all `e`. Here's what's interesting, for the purposes of this test:
7 // 1. The test focuses on expression nesting, because interactions between different expression
8 // types are harder to test manually than single expression types in isolation.
10 // 2. The test only considers expressions of at most two nontrivial nodes. So it will check `x +
11 // x` and `x + (x - x)` but not `(x * x) + (x - x)`. The assumption here is that the correct
12 // handling of an expression might depend on the expression's parent, but doesn't depend on its
13 // siblings or any more distant ancestors.
15 // 3. The test only checks certain expression kinds. The assumption is that similar expression
16 // types, such as `if` and `while` or `+` and `-`, will be handled identically in the printer
17 // and parser. So if all combinations of exprs involving `if` work correctly, then combinations
18 // using `while`, `if let`, and so on will likely work as well.
20 #![feature(rustc_private)]
22 extern crate rustc_data_structures
;
25 use rustc_data_structures
::thin_vec
::ThinVec
;
27 use syntax
::source_map
::{Spanned, DUMMY_SP, FileName}
;
28 use syntax
::source_map
::FilePathMapping
;
29 use syntax
::mut_visit
::{self, MutVisitor, visit_clobber}
;
30 use syntax
::parse
::{self, ParseSess}
;
31 use syntax
::print
::pprust
;
35 fn parse_expr(ps
: &ParseSess
, src
: &str) -> P
<Expr
> {
36 let src_as_string
= src
.to_string();
38 let mut p
= parse
::new_parser_from_source_str(ps
,
39 FileName
::Custom(src_as_string
.clone()),
41 p
.parse_expr().unwrap()
45 // Helper functions for building exprs
46 fn expr(kind
: ExprKind
) -> P
<Expr
> {
51 attrs
: ThinVec
::new(),
55 fn make_x() -> P
<Expr
> {
56 let seg
= PathSegment
::from_ident(Ident
::from_str("x"));
57 let path
= Path { segments: vec![seg], span: DUMMY_SP }
;
58 expr(ExprKind
::Path(None
, path
))
61 /// Iterate over exprs of depth up to `depth`. The goal is to explore all "interesting"
62 /// combinations of expression nesting. For example, we explore combinations using `if`, but not
63 /// `while` or `match`, since those should print and parse in much the same way as `if`.
64 fn iter_exprs(depth
: usize, f
: &mut dyn FnMut(P
<Expr
>)) {
70 let mut g
= |e
| f(expr(e
));
74 0 => iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Box(e
))),
75 1 => iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Call(e
, vec
![]))),
77 let seg
= PathSegment
::from_ident(Ident
::from_str("x"));
78 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::MethodCall(
79 seg
.clone(), vec
![e
, make_x()])));
80 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::MethodCall(
81 seg
.clone(), vec
![make_x(), e
])));
96 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Binary(op
, e
, make_x())));
97 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Binary(op
, make_x(), e
)));
100 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Unary(UnOp
::Deref
, e
)));
103 let block
= P(Block
{
106 rules
: BlockCheckMode
::Default
,
109 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::If(e
, block
.clone(), None
)));
112 let decl
= P(FnDecl
{
114 output
: FunctionRetTy
::Default(DUMMY_SP
),
117 iter_exprs(depth
- 1, &mut |e
| g(
118 ExprKind
::Closure(CaptureBy
::Value
,
126 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Assign(e
, make_x())));
127 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Assign(make_x(), e
)));
130 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Field(e
, Ident
::from_str("f"))));
133 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Range(
134 Some(e
), Some(make_x()), RangeLimits
::HalfOpen
)));
135 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Range(
136 Some(make_x()), Some(e
), RangeLimits
::HalfOpen
)));
139 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::AddrOf(Mutability
::Immutable
, e
)));
142 g(ExprKind
::Ret(None
));
143 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Ret(Some(e
))));
146 let path
= Path
::from_ident(Ident
::from_str("S"));
147 g(ExprKind
::Struct(path
, vec
![], Some(make_x())));
150 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Try(e
)));
153 let ps
= vec
![P(Pat
{
158 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Let(ps
.clone(), e
)))
160 _
=> panic
!("bad counter value in iter_exprs"),
166 // Folders for manipulating the placement of `Paren` nodes. See below for why this is needed.
168 /// `MutVisitor` that removes all `ExprKind::Paren` nodes.
171 impl MutVisitor
for RemoveParens
{
172 fn visit_expr(&mut self, e
: &mut P
<Expr
>) {
173 match e
.node
.clone() {
174 ExprKind
::Paren(inner
) => *e
= inner
,
177 mut_visit
::noop_visit_expr(e
, self);
182 /// `MutVisitor` that inserts `ExprKind::Paren` nodes around every `Expr`.
185 impl MutVisitor
for AddParens
{
186 fn visit_expr(&mut self, e
: &mut P
<Expr
>) {
187 mut_visit
::noop_visit_expr(e
, self);
188 visit_clobber(e
, |e
| {
191 node
: ExprKind
::Paren(e
),
193 attrs
: ThinVec
::new(),
200 syntax
::with_default_globals(|| run());
204 let ps
= ParseSess
::new(FilePathMapping
::empty());
206 iter_exprs(2, &mut |mut e
| {
207 // If the pretty printer is correct, then `parse(print(e))` should be identical to `e`,
208 // modulo placement of `Paren` nodes.
209 let printed
= pprust
::expr_to_string(&e
);
210 println
!("printed: {}", printed
);
212 let mut parsed
= parse_expr(&ps
, &printed
);
214 // We want to know if `parsed` is structurally identical to `e`, ignoring trivial
215 // differences like placement of `Paren`s or the exact ranges of node spans.
216 // Unfortunately, there is no easy way to make this comparison. Instead, we add `Paren`s
217 // everywhere we can, then pretty-print. This should give an unambiguous representation of
218 // each `Expr`, and it bypasses nearly all of the parenthesization logic, so we aren't
219 // relying on the correctness of the very thing we're testing.
220 RemoveParens
.visit_expr(&mut e
);
221 AddParens
.visit_expr(&mut e
);
222 let text1
= pprust
::expr_to_string(&e
);
223 RemoveParens
.visit_expr(&mut parsed
);
224 AddParens
.visit_expr(&mut parsed
);
225 let text2
= pprust
::expr_to_string(&parsed
);
226 assert
!(text1
== text2
,
227 "exprs are not equal:\n e = {:?}\n parsed = {:?}",