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
;
23 extern crate rustc_ast_pretty
;
24 extern crate rustc_data_structures
;
25 extern crate rustc_parse
;
26 extern crate rustc_session
;
27 extern crate rustc_span
;
29 use rustc_ast
::mut_visit
::{self, visit_clobber, MutVisitor}
;
30 use rustc_ast
::ptr
::P
;
32 use rustc_ast_pretty
::pprust
;
33 use rustc_data_structures
::thin_vec
::ThinVec
;
34 use rustc_parse
::new_parser_from_source_str
;
35 use rustc_session
::parse
::ParseSess
;
36 use rustc_span
::source_map
::FilePathMapping
;
37 use rustc_span
::source_map
::{FileName, Spanned, DUMMY_SP}
;
38 use rustc_span
::symbol
::Ident
;
40 fn parse_expr(ps
: &ParseSess
, src
: &str) -> Option
<P
<Expr
>> {
41 let src_as_string
= src
.to_string();
44 new_parser_from_source_str(ps
, FileName
::Custom(src_as_string
.clone()), src_as_string
);
45 p
.parse_expr().map_err(|mut e
| e
.cancel()).ok()
48 // Helper functions for building exprs
49 fn expr(kind
: ExprKind
) -> P
<Expr
> {
50 P(Expr { id: DUMMY_NODE_ID, kind, span: DUMMY_SP, attrs: ThinVec::new(), tokens: None }
)
53 fn make_x() -> P
<Expr
> {
54 let seg
= PathSegment
::from_ident(Ident
::from_str("x"));
55 let path
= Path { segments: vec![seg], span: DUMMY_SP, tokens: None }
;
56 expr(ExprKind
::Path(None
, path
))
59 /// Iterate over exprs of depth up to `depth`. The goal is to explore all "interesting"
60 /// combinations of expression nesting. For example, we explore combinations using `if`, but not
61 /// `while` or `match`, since those should print and parse in much the same way as `if`.
62 fn iter_exprs(depth
: usize, f
: &mut dyn FnMut(P
<Expr
>)) {
68 let mut g
= |e
| f(expr(e
));
72 0 => iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Box(e
))),
73 1 => iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Call(e
, vec
![]))),
75 let seg
= PathSegment
::from_ident(Ident
::from_str("x"));
76 iter_exprs(depth
- 1, &mut |e
| {
77 g(ExprKind
::MethodCall(seg
.clone(), vec
![e
, make_x()], DUMMY_SP
))
79 iter_exprs(depth
- 1, &mut |e
| {
80 g(ExprKind
::MethodCall(seg
.clone(), vec
![make_x(), e
], DUMMY_SP
))
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
,
110 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::If(e
, block
.clone(), None
)));
113 let decl
= P(FnDecl { inputs: vec![], output: FnRetTy::Default(DUMMY_SP) }
);
114 iter_exprs(depth
- 1, &mut |e
| {
126 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Assign(e
, make_x(), DUMMY_SP
)));
127 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Assign(make_x(), e
, DUMMY_SP
)));
130 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Field(e
, Ident
::from_str("f"))));
133 iter_exprs(depth
- 1, &mut |e
| {
134 g(ExprKind
::Range(Some(e
), Some(make_x()), RangeLimits
::HalfOpen
))
136 iter_exprs(depth
- 1, &mut |e
| {
137 g(ExprKind
::Range(Some(make_x()), Some(e
), RangeLimits
::HalfOpen
))
141 iter_exprs(depth
- 1, &mut |e
| {
142 g(ExprKind
::AddrOf(BorrowKind
::Ref
, Mutability
::Not
, e
))
146 g(ExprKind
::Ret(None
));
147 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Ret(Some(e
))));
150 let path
= Path
::from_ident(Ident
::from_str("S"));
151 g(ExprKind
::Struct(P(StructExpr
{
155 rest
: StructRest
::Base(make_x()),
159 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Try(e
)));
163 P(Pat { id: DUMMY_NODE_ID, kind: PatKind::Wild, span: DUMMY_SP, tokens: None }
);
164 iter_exprs(depth
- 1, &mut |e
| g(ExprKind
::Let(pat
.clone(), e
)))
166 _
=> panic
!("bad counter value in iter_exprs"),
171 // Folders for manipulating the placement of `Paren` nodes. See below for why this is needed.
173 /// `MutVisitor` that removes all `ExprKind::Paren` nodes.
176 impl MutVisitor
for RemoveParens
{
177 fn visit_expr(&mut self, e
: &mut P
<Expr
>) {
178 match e
.kind
.clone() {
179 ExprKind
::Paren(inner
) => *e
= inner
,
182 mut_visit
::noop_visit_expr(e
, self);
186 /// `MutVisitor` that inserts `ExprKind::Paren` nodes around every `Expr`.
189 impl MutVisitor
for AddParens
{
190 fn visit_expr(&mut self, e
: &mut P
<Expr
>) {
191 mut_visit
::noop_visit_expr(e
, self);
192 visit_clobber(e
, |e
| {
195 kind
: ExprKind
::Paren(e
),
197 attrs
: ThinVec
::new(),
205 rustc_span
::create_default_session_globals_then(|| run());
209 let ps
= ParseSess
::new(FilePathMapping
::empty());
211 iter_exprs(2, &mut |mut e
| {
212 // If the pretty printer is correct, then `parse(print(e))` should be identical to `e`,
213 // modulo placement of `Paren` nodes.
214 let printed
= pprust
::expr_to_string(&e
);
215 println
!("printed: {}", printed
);
217 // Ignore expressions with chained comparisons that fail to parse
218 if let Some(mut parsed
) = parse_expr(&ps
, &printed
) {
219 // We want to know if `parsed` is structurally identical to `e`, ignoring trivial
220 // differences like placement of `Paren`s or the exact ranges of node spans.
221 // Unfortunately, there is no easy way to make this comparison. Instead, we add `Paren`s
222 // everywhere we can, then pretty-print. This should give an unambiguous representation
223 // of each `Expr`, and it bypasses nearly all of the parenthesization logic, so we
224 // aren't relying on the correctness of the very thing we're testing.
225 RemoveParens
.visit_expr(&mut e
);
226 AddParens
.visit_expr(&mut e
);
227 let text1
= pprust
::expr_to_string(&e
);
228 RemoveParens
.visit_expr(&mut parsed
);
229 AddParens
.visit_expr(&mut parsed
);
230 let text2
= pprust
::expr_to_string(&parsed
);
233 "exprs are not equal:\n e = {:?}\n parsed = {:?}",