]>
Commit | Line | Data |
---|---|---|
416331ca | 1 | // run-pass |
ea8adc8c XL |
2 | // ignore-cross-compile |
3 | ||
ea8adc8c | 4 | // The general idea of this test is to enumerate all "interesting" expressions and check that |
dc9dc135 | 5 | // `parse(print(e)) == e` for all `e`. Here's what's interesting, for the purposes of this test: |
ea8adc8c | 6 | // |
dc9dc135 XL |
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. | |
ea8adc8c | 9 | // |
dc9dc135 XL |
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. | |
ea8adc8c | 14 | // |
dc9dc135 XL |
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 | |
ea8adc8c XL |
18 | // using `while`, `if let`, and so on will likely work as well. |
19 | ||
ea8adc8c XL |
20 | #![feature(rustc_private)] |
21 | ||
74b04a01 | 22 | extern crate rustc_ast_pretty; |
b7449926 | 23 | extern crate rustc_data_structures; |
74b04a01 | 24 | extern crate rustc_ast; |
60c5eb7d | 25 | extern crate rustc_parse; |
74b04a01 | 26 | extern crate rustc_session; |
dfeec247 | 27 | extern crate rustc_span; |
ea8adc8c | 28 | |
74b04a01 | 29 | use rustc_ast_pretty::pprust; |
b7449926 | 30 | use rustc_data_structures::thin_vec::ThinVec; |
60c5eb7d | 31 | use rustc_parse::new_parser_from_source_str; |
74b04a01 | 32 | use rustc_session::parse::ParseSess; |
dfeec247 XL |
33 | use rustc_span::source_map::{Spanned, DUMMY_SP, FileName}; |
34 | use rustc_span::source_map::FilePathMapping; | |
f9f354fc | 35 | use rustc_span::symbol::Ident; |
3dfed10e | 36 | use rustc_ast::*; |
74b04a01 XL |
37 | use rustc_ast::mut_visit::{self, MutVisitor, visit_clobber}; |
38 | use rustc_ast::ptr::P; | |
ea8adc8c | 39 | |
e1599b0c | 40 | fn parse_expr(ps: &ParseSess, src: &str) -> Option<P<Expr>> { |
0731742a XL |
41 | let src_as_string = src.to_string(); |
42 | ||
60c5eb7d | 43 | let mut p = new_parser_from_source_str( |
e1599b0c XL |
44 | ps, |
45 | FileName::Custom(src_as_string.clone()), | |
46 | src_as_string, | |
47 | ); | |
48 | p.parse_expr().map_err(|mut e| e.cancel()).ok() | |
ea8adc8c XL |
49 | } |
50 | ||
51 | ||
52 | // Helper functions for building exprs | |
53 | fn expr(kind: ExprKind) -> P<Expr> { | |
54 | P(Expr { | |
55 | id: DUMMY_NODE_ID, | |
e74abb32 | 56 | kind, |
ea8adc8c XL |
57 | span: DUMMY_SP, |
58 | attrs: ThinVec::new(), | |
f9f354fc | 59 | tokens: None |
ea8adc8c XL |
60 | }) |
61 | } | |
62 | ||
63 | fn make_x() -> P<Expr> { | |
83c7162d | 64 | let seg = PathSegment::from_ident(Ident::from_str("x")); |
1b1a35ee | 65 | let path = Path { segments: vec![seg], span: DUMMY_SP, tokens: None }; |
ea8adc8c XL |
66 | expr(ExprKind::Path(None, path)) |
67 | } | |
68 | ||
9fa01778 XL |
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 | |
ea8adc8c | 71 | /// `while` or `match`, since those should print and parse in much the same way as `if`. |
dc9dc135 | 72 | fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) { |
ea8adc8c XL |
73 | if depth == 0 { |
74 | f(make_x()); | |
75 | return; | |
76 | } | |
77 | ||
78 | let mut g = |e| f(expr(e)); | |
79 | ||
dc9dc135 | 80 | for kind in 0..=19 { |
ea8adc8c XL |
81 | match kind { |
82 | 0 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Box(e))), | |
83c7162d XL |
83 | 1 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Call(e, vec![]))), |
84 | 2 => { | |
85 | let seg = PathSegment::from_ident(Ident::from_str("x")); | |
ea8adc8c | 86 | iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall( |
f035d41b | 87 | seg.clone(), vec![e, make_x()], DUMMY_SP))); |
ea8adc8c | 88 | iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall( |
f035d41b | 89 | seg.clone(), vec![make_x(), e], DUMMY_SP))); |
ea8adc8c | 90 | }, |
dc9dc135 XL |
91 | 3..=8 => { |
92 | let op = Spanned { | |
93 | span: DUMMY_SP, | |
94 | node: match kind { | |
95 | 3 => BinOpKind::Add, | |
96 | 4 => BinOpKind::Mul, | |
97 | 5 => BinOpKind::Shl, | |
98 | 6 => BinOpKind::And, | |
99 | 7 => BinOpKind::Or, | |
100 | 8 => BinOpKind::Lt, | |
101 | _ => unreachable!(), | |
102 | } | |
103 | }; | |
ea8adc8c XL |
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))); | |
106 | }, | |
dc9dc135 | 107 | 9 => { |
ea8adc8c XL |
108 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Unary(UnOp::Deref, e))); |
109 | }, | |
dc9dc135 | 110 | 10 => { |
ea8adc8c XL |
111 | let block = P(Block { |
112 | stmts: Vec::new(), | |
113 | id: DUMMY_NODE_ID, | |
114 | rules: BlockCheckMode::Default, | |
115 | span: DUMMY_SP, | |
1b1a35ee | 116 | tokens: None, |
ea8adc8c XL |
117 | }); |
118 | iter_exprs(depth - 1, &mut |e| g(ExprKind::If(e, block.clone(), None))); | |
119 | }, | |
dc9dc135 | 120 | 11 => { |
ea8adc8c XL |
121 | let decl = P(FnDecl { |
122 | inputs: vec![], | |
74b04a01 | 123 | output: FnRetTy::Default(DUMMY_SP), |
ea8adc8c XL |
124 | }); |
125 | iter_exprs(depth - 1, &mut |e| g( | |
2c00a5a8 | 126 | ExprKind::Closure(CaptureBy::Value, |
74b04a01 | 127 | Async::No, |
2c00a5a8 XL |
128 | Movability::Movable, |
129 | decl.clone(), | |
130 | e, | |
131 | DUMMY_SP))); | |
ea8adc8c | 132 | }, |
dc9dc135 | 133 | 12 => { |
dfeec247 XL |
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))); | |
ea8adc8c | 136 | }, |
dc9dc135 | 137 | 13 => { |
83c7162d | 138 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Field(e, Ident::from_str("f")))); |
ea8adc8c | 139 | }, |
dc9dc135 | 140 | 14 => { |
ea8adc8c XL |
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))); | |
145 | }, | |
dc9dc135 | 146 | 15 => { |
60c5eb7d XL |
147 | iter_exprs( |
148 | depth - 1, | |
dfeec247 | 149 | &mut |e| g(ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, e)), |
60c5eb7d | 150 | ); |
ea8adc8c | 151 | }, |
dc9dc135 | 152 | 16 => { |
ea8adc8c XL |
153 | g(ExprKind::Ret(None)); |
154 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Ret(Some(e)))); | |
155 | }, | |
dc9dc135 | 156 | 17 => { |
83c7162d | 157 | let path = Path::from_ident(Ident::from_str("S")); |
6a06907d XL |
158 | g(ExprKind::Struct(P(StructExpr { |
159 | path, fields: vec![], rest: StructRest::Base(make_x()) | |
160 | }))); | |
ea8adc8c | 161 | }, |
dc9dc135 | 162 | 18 => { |
ea8adc8c XL |
163 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Try(e))); |
164 | }, | |
dc9dc135 | 165 | 19 => { |
e1599b0c | 166 | let pat = P(Pat { |
dc9dc135 | 167 | id: DUMMY_NODE_ID, |
e74abb32 | 168 | kind: PatKind::Wild, |
dc9dc135 | 169 | span: DUMMY_SP, |
3dfed10e | 170 | tokens: None, |
e1599b0c XL |
171 | }); |
172 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Let(pat.clone(), e))) | |
dc9dc135 | 173 | }, |
ea8adc8c XL |
174 | _ => panic!("bad counter value in iter_exprs"), |
175 | } | |
176 | } | |
177 | } | |
178 | ||
179 | ||
dc9dc135 | 180 | // Folders for manipulating the placement of `Paren` nodes. See below for why this is needed. |
ea8adc8c | 181 | |
dc9dc135 | 182 | /// `MutVisitor` that removes all `ExprKind::Paren` nodes. |
ea8adc8c XL |
183 | struct RemoveParens; |
184 | ||
9fa01778 XL |
185 | impl MutVisitor for RemoveParens { |
186 | fn visit_expr(&mut self, e: &mut P<Expr>) { | |
e74abb32 | 187 | match e.kind.clone() { |
9fa01778 XL |
188 | ExprKind::Paren(inner) => *e = inner, |
189 | _ => {} | |
ea8adc8c | 190 | }; |
9fa01778 | 191 | mut_visit::noop_visit_expr(e, self); |
ea8adc8c XL |
192 | } |
193 | } | |
194 | ||
195 | ||
dc9dc135 | 196 | /// `MutVisitor` that inserts `ExprKind::Paren` nodes around every `Expr`. |
ea8adc8c XL |
197 | struct AddParens; |
198 | ||
9fa01778 XL |
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| { | |
203 | P(Expr { | |
204 | id: DUMMY_NODE_ID, | |
e74abb32 | 205 | kind: ExprKind::Paren(e), |
9fa01778 XL |
206 | span: DUMMY_SP, |
207 | attrs: ThinVec::new(), | |
f9f354fc | 208 | tokens: None |
9fa01778 XL |
209 | }) |
210 | }); | |
ea8adc8c XL |
211 | } |
212 | } | |
213 | ||
ea8adc8c | 214 | fn main() { |
3dfed10e | 215 | rustc_span::with_default_session_globals(|| run()); |
0531ce1d XL |
216 | } |
217 | ||
218 | fn run() { | |
ea8adc8c XL |
219 | let ps = ParseSess::new(FilePathMapping::empty()); |
220 | ||
9fa01778 | 221 | iter_exprs(2, &mut |mut e| { |
ea8adc8c XL |
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); | |
226 | ||
e1599b0c XL |
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 = {:?}", | |
243 | text1, text2); | |
244 | } | |
ea8adc8c XL |
245 | }); |
246 | } |