]>
Commit | Line | Data |
---|---|---|
ea8adc8c XL |
1 | // Copyright 2017 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | // ignore-cross-compile | |
12 | ||
13 | ||
14 | // The general idea of this test is to enumerate all "interesting" expressions and check that | |
15 | // `parse(print(e)) == e` for all `e`. Here's what's interesting, for the purposes of this test: | |
16 | // | |
17 | // 1. The test focuses on expression nesting, because interactions between different expression | |
18 | // types are harder to test manually than single expression types in isolation. | |
19 | // | |
20 | // 2. The test only considers expressions of at most two nontrivial nodes. So it will check `x + | |
21 | // x` and `x + (x - x)` but not `(x * x) + (x - x)`. The assumption here is that the correct | |
22 | // handling of an expression might depend on the expression's parent, but doesn't depend on its | |
23 | // siblings or any more distant ancestors. | |
24 | // | |
25 | // 3. The test only checks certain expression kinds. The assumption is that similar expression | |
26 | // types, such as `if` and `while` or `+` and `-`, will be handled identically in the printer | |
27 | // and parser. So if all combinations of exprs involving `if` work correctly, then combinations | |
28 | // using `while`, `if let`, and so on will likely work as well. | |
29 | ||
30 | ||
31 | #![feature(rustc_private)] | |
32 | ||
33 | extern crate syntax; | |
34 | ||
35 | use syntax::ast::*; | |
36 | use syntax::codemap::{Spanned, DUMMY_SP}; | |
37 | use syntax::codemap::FilePathMapping; | |
38 | use syntax::fold::{self, Folder}; | |
39 | use syntax::parse::{self, ParseSess}; | |
40 | use syntax::print::pprust; | |
41 | use syntax::ptr::P; | |
42 | use syntax::util::ThinVec; | |
43 | ||
44 | ||
45 | fn parse_expr(ps: &ParseSess, src: &str) -> P<Expr> { | |
46 | let mut p = parse::new_parser_from_source_str(ps, | |
47 | "<expr>".to_owned(), | |
48 | src.to_owned()); | |
49 | p.parse_expr().unwrap() | |
50 | } | |
51 | ||
52 | ||
53 | // Helper functions for building exprs | |
54 | fn expr(kind: ExprKind) -> P<Expr> { | |
55 | P(Expr { | |
56 | id: DUMMY_NODE_ID, | |
57 | node: kind, | |
58 | span: DUMMY_SP, | |
59 | attrs: ThinVec::new(), | |
60 | }) | |
61 | } | |
62 | ||
63 | fn make_x() -> P<Expr> { | |
64 | let seg = PathSegment { | |
65 | identifier: Ident::from_str("x"), | |
66 | span: DUMMY_SP, | |
67 | parameters: None, | |
68 | }; | |
69 | let path = Path { | |
70 | span: DUMMY_SP, | |
71 | segments: vec![seg], | |
72 | }; | |
73 | expr(ExprKind::Path(None, path)) | |
74 | } | |
75 | ||
76 | /// Iterate over exprs of depth up to `depth`. The goal is to explore all "interesting" | |
77 | /// combinations of expression nesting. For example, we explore combinations using `if`, but not | |
78 | /// `while` or `match`, since those should print and parse in much the same way as `if`. | |
79 | fn iter_exprs(depth: usize, f: &mut FnMut(P<Expr>)) { | |
80 | if depth == 0 { | |
81 | f(make_x()); | |
82 | return; | |
83 | } | |
84 | ||
85 | let mut g = |e| f(expr(e)); | |
86 | ||
87 | for kind in 0 .. 17 { | |
88 | match kind { | |
89 | 0 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Box(e))), | |
90 | 1 => { | |
91 | // Note that for binary expressions, we explore each side separately. The | |
92 | // parenthesization decisions for the LHS and RHS should be independent, and this | |
93 | // way produces `O(n)` results instead of `O(n^2)`. | |
94 | iter_exprs(depth - 1, &mut |e| g(ExprKind::InPlace(e, make_x()))); | |
95 | iter_exprs(depth - 1, &mut |e| g(ExprKind::InPlace(make_x(), e))); | |
96 | }, | |
97 | 2 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Call(e, vec![]))), | |
98 | 3 => { | |
99 | let seg = PathSegment { | |
100 | identifier: Ident::from_str("x"), | |
101 | span: DUMMY_SP, | |
102 | parameters: None, | |
103 | }; | |
104 | ||
105 | iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall( | |
106 | seg.clone(), vec![e, make_x()]))); | |
107 | iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall( | |
108 | seg.clone(), vec![make_x(), e]))); | |
109 | }, | |
110 | 4 => { | |
111 | let op = Spanned { span: DUMMY_SP, node: BinOpKind::Add }; | |
112 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x()))); | |
113 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e))); | |
114 | }, | |
115 | 5 => { | |
116 | let op = Spanned { span: DUMMY_SP, node: BinOpKind::Mul }; | |
117 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x()))); | |
118 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e))); | |
119 | }, | |
120 | 6 => { | |
121 | let op = Spanned { span: DUMMY_SP, node: BinOpKind::Shl }; | |
122 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x()))); | |
123 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e))); | |
124 | }, | |
125 | 7 => { | |
126 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Unary(UnOp::Deref, e))); | |
127 | }, | |
128 | 8 => { | |
129 | let block = P(Block { | |
130 | stmts: Vec::new(), | |
131 | id: DUMMY_NODE_ID, | |
132 | rules: BlockCheckMode::Default, | |
133 | span: DUMMY_SP, | |
134 | }); | |
135 | iter_exprs(depth - 1, &mut |e| g(ExprKind::If(e, block.clone(), None))); | |
136 | }, | |
137 | 9 => { | |
138 | let decl = P(FnDecl { | |
139 | inputs: vec![], | |
140 | output: FunctionRetTy::Default(DUMMY_SP), | |
141 | variadic: false, | |
142 | }); | |
143 | iter_exprs(depth - 1, &mut |e| g( | |
144 | ExprKind::Closure(CaptureBy::Value, decl.clone(), e, DUMMY_SP))); | |
145 | }, | |
146 | 10 => { | |
147 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(e, make_x()))); | |
148 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(make_x(), e))); | |
149 | }, | |
150 | 11 => { | |
151 | let ident = Spanned { span: DUMMY_SP, node: Ident::from_str("f") }; | |
152 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Field(e, ident))); | |
153 | }, | |
154 | 12 => { | |
155 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Range( | |
156 | Some(e), Some(make_x()), RangeLimits::HalfOpen))); | |
157 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Range( | |
158 | Some(make_x()), Some(e), RangeLimits::HalfOpen))); | |
159 | }, | |
160 | 13 => { | |
161 | iter_exprs(depth - 1, &mut |e| g(ExprKind::AddrOf(Mutability::Immutable, e))); | |
162 | }, | |
163 | 14 => { | |
164 | g(ExprKind::Ret(None)); | |
165 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Ret(Some(e)))); | |
166 | }, | |
167 | 15 => { | |
168 | let seg = PathSegment { | |
169 | identifier: Ident::from_str("S"), | |
170 | span: DUMMY_SP, | |
171 | parameters: None, | |
172 | }; | |
173 | let path = Path { | |
174 | span: DUMMY_SP, | |
175 | segments: vec![seg], | |
176 | }; | |
177 | g(ExprKind::Struct(path, vec![], Some(make_x()))); | |
178 | }, | |
179 | 16 => { | |
180 | iter_exprs(depth - 1, &mut |e| g(ExprKind::Try(e))); | |
181 | }, | |
182 | _ => panic!("bad counter value in iter_exprs"), | |
183 | } | |
184 | } | |
185 | } | |
186 | ||
187 | ||
188 | // Folders for manipulating the placement of `Paren` nodes. See below for why this is needed. | |
189 | ||
190 | /// Folder that removes all `ExprKind::Paren` nodes. | |
191 | struct RemoveParens; | |
192 | ||
193 | impl Folder for RemoveParens { | |
194 | fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> { | |
195 | let e = match e.node { | |
196 | ExprKind::Paren(ref inner) => inner.clone(), | |
197 | _ => e.clone(), | |
198 | }; | |
199 | e.map(|e| fold::noop_fold_expr(e, self)) | |
200 | } | |
201 | } | |
202 | ||
203 | ||
204 | /// Folder that inserts `ExprKind::Paren` nodes around every `Expr`. | |
205 | struct AddParens; | |
206 | ||
207 | impl Folder for AddParens { | |
208 | fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> { | |
209 | let e = e.map(|e| fold::noop_fold_expr(e, self)); | |
210 | P(Expr { | |
211 | id: DUMMY_NODE_ID, | |
212 | node: ExprKind::Paren(e), | |
213 | span: DUMMY_SP, | |
214 | attrs: ThinVec::new(), | |
215 | }) | |
216 | } | |
217 | } | |
218 | ||
219 | ||
220 | fn main() { | |
221 | let ps = ParseSess::new(FilePathMapping::empty()); | |
222 | ||
223 | iter_exprs(2, &mut |e| { | |
224 | // If the pretty printer is correct, then `parse(print(e))` should be identical to `e`, | |
225 | // modulo placement of `Paren` nodes. | |
226 | let printed = pprust::expr_to_string(&e); | |
227 | println!("printed: {}", printed); | |
228 | ||
229 | let parsed = parse_expr(&ps, &printed); | |
230 | ||
231 | // We want to know if `parsed` is structurally identical to `e`, ignoring trivial | |
232 | // differences like placement of `Paren`s or the exact ranges of node spans. | |
233 | // Unfortunately, there is no easy way to make this comparison. Instead, we add `Paren`s | |
234 | // everywhere we can, then pretty-print. This should give an unambiguous representation of | |
235 | // each `Expr`, and it bypasses nearly all of the parenthesization logic, so we aren't | |
236 | // relying on the correctness of the very thing we're testing. | |
237 | let e1 = AddParens.fold_expr(RemoveParens.fold_expr(e)); | |
238 | let text1 = pprust::expr_to_string(&e1); | |
239 | let e2 = AddParens.fold_expr(RemoveParens.fold_expr(parsed)); | |
240 | let text2 = pprust::expr_to_string(&e2); | |
241 | assert!(text1 == text2, | |
242 | "exprs are not equal:\n e = {:?}\n parsed = {:?}", | |
243 | text1, text2); | |
244 | }); | |
245 | } |