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_ast_pretty
;
23 extern crate rustc_data_structures
;
24 extern crate rustc_ast
;
25 extern crate rustc_parse
;
26 extern crate rustc_session
;
27 extern crate rustc_span
;
29 use rustc_ast_pretty
::pprust
;
30 use rustc_data_structures
::thin_vec
::ThinVec
;
31 use rustc_parse
::new_parser_from_source_str
;
32 use rustc_session
::parse
::ParseSess
;
33 use rustc_span
::source_map
::{Spanned, DUMMY_SP, FileName}
;
34 use rustc_span
::source_map
::FilePathMapping
;
35 use rustc_span
::symbol
::Ident
;
37 use rustc_ast
::mut_visit
::{self, MutVisitor, visit_clobber}
;
38 use rustc_ast
::ptr
::P
;
40 fn parse_expr(ps
: &ParseSess
, src
: &str) -> Option
<P
<Expr
>> {
41 let src_as_string
= src
.to_string();
43 let mut p
= new_parser_from_source_str(
45 FileName
::Custom(src_as_string
.clone()),
48 p
.parse_expr().map_err(|mut e
| e
.cancel()).ok()
52 // Helper functions for building exprs
53 fn expr(kind
: ExprKind
) -> P
<Expr
> {
58 attrs
: ThinVec
::new(),
63 fn make_x() -> P
<Expr
> {
64 let seg
= PathSegment
::from_ident(Ident
::from_str("x"));
65 let path
= Path { segments: vec![seg], span: DUMMY_SP, tokens: None }
;
66 expr(ExprKind
::Path(None
, path
))
69 /// Iterate over exprs of depth up to `depth`. The goal is to explore all "interesting"
70 /// combinations of expression nesting. For example, we explore combinations using `if`, but not
71 /// `while` or `match`, since those should print and parse in much the same way as `if`.
72 fn iter_exprs(depth
: usize, f
: &mut dyn FnMut(P
<Expr
>)) {
78 let mut g
= |e
| f(expr(e
));
82 0 => iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Box(e
))),
83 1 => iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Call(e
, vec
![]))),
85 let seg
= PathSegment
::from_ident(Ident
::from_str("x"));
86 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::MethodCall(
87 seg
.clone(), vec
![e
, make_x()], DUMMY_SP
)));
88 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::MethodCall(
89 seg
.clone(), vec
![make_x(), e
], DUMMY_SP
)));
104 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Binary(op
, e
, make_x())));
105 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Binary(op
, make_x(), e
)));
108 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Unary(UnOp
::Deref
, e
)));
111 let block
= P(Block
{
114 rules
: BlockCheckMode
::Default
,
118 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::If(e
, block
.clone(), None
)));
121 let decl
= P(FnDecl
{
123 output
: FnRetTy
::Default(DUMMY_SP
),
125 iter_exprs(depth
- 1, &mut |e
| g(
126 ExprKind
::Closure(CaptureBy
::Value
,
134 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Assign(e
, make_x(), DUMMY_SP
)));
135 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Assign(make_x(), e
, DUMMY_SP
)));
138 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Field(e
, Ident
::from_str("f"))));
141 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Range(
142 Some(e
), Some(make_x()), RangeLimits
::HalfOpen
)));
143 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Range(
144 Some(make_x()), Some(e
), RangeLimits
::HalfOpen
)));
149 &mut |e
| g(ExprKind
::AddrOf(BorrowKind
::Ref
, Mutability
::Not
, e
)),
153 g(ExprKind
::Ret(None
));
154 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Ret(Some(e
))));
157 let path
= Path
::from_ident(Ident
::from_str("S"));
158 g(ExprKind
::Struct(P(StructExpr
{
159 path
, fields
: vec
![], rest
: StructRest
::Base(make_x())
163 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Try(e
)));
172 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Let(pat
.clone(), e
)))
174 _
=> panic
!("bad counter value in iter_exprs"),
180 // Folders for manipulating the placement of `Paren` nodes. See below for why this is needed.
182 /// `MutVisitor` that removes all `ExprKind::Paren` nodes.
185 impl MutVisitor
for RemoveParens
{
186 fn visit_expr(&mut self, e
: &mut P
<Expr
>) {
187 match e
.kind
.clone() {
188 ExprKind
::Paren(inner
) => *e
= inner
,
191 mut_visit
::noop_visit_expr(e
, self);
196 /// `MutVisitor` that inserts `ExprKind::Paren` nodes around every `Expr`.
199 impl MutVisitor
for AddParens
{
200 fn visit_expr(&mut self, e
: &mut P
<Expr
>) {
201 mut_visit
::noop_visit_expr(e
, self);
202 visit_clobber(e
, |e
| {
205 kind
: ExprKind
::Paren(e
),
207 attrs
: ThinVec
::new(),
215 rustc_span
::with_default_session_globals(|| run());
219 let ps
= ParseSess
::new(FilePathMapping
::empty());
221 iter_exprs(2, &mut |mut e
| {
222 // If the pretty printer is correct, then `parse(print(e))` should be identical to `e`,
223 // modulo placement of `Paren` nodes.
224 let printed
= pprust
::expr_to_string(&e
);
225 println
!("printed: {}", printed
);
227 // Ignore expressions with chained comparisons that fail to parse
228 if let Some(mut parsed
) = parse_expr(&ps
, &printed
) {
229 // We want to know if `parsed` is structurally identical to `e`, ignoring trivial
230 // differences like placement of `Paren`s or the exact ranges of node spans.
231 // Unfortunately, there is no easy way to make this comparison. Instead, we add `Paren`s
232 // everywhere we can, then pretty-print. This should give an unambiguous representation
233 // of each `Expr`, and it bypasses nearly all of the parenthesization logic, so we
234 // aren't relying on the correctness of the very thing we're testing.
235 RemoveParens
.visit_expr(&mut e
);
236 AddParens
.visit_expr(&mut e
);
237 let text1
= pprust
::expr_to_string(&e
);
238 RemoveParens
.visit_expr(&mut parsed
);
239 AddParens
.visit_expr(&mut parsed
);
240 let text2
= pprust
::expr_to_string(&parsed
);
241 assert
!(text1
== text2
,
242 "exprs are not equal:\n e = {:?}\n parsed = {:?}",