]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | //! A group of attributes that can be attached to Rust code in order |
2 | //! to generate a clippy lint detecting said code automatically. | |
3 | ||
cdc7bbd5 | 4 | use clippy_utils::get_attr; |
f20569fa XL |
5 | use rustc_ast::ast::{LitFloatType, LitKind}; |
6 | use rustc_ast::walk_list; | |
7 | use rustc_data_structures::fx::FxHashMap; | |
8 | use rustc_hir as hir; | |
9 | use rustc_hir::intravisit::{NestedVisitorMap, Visitor}; | |
10 | use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind, QPath, Stmt, StmtKind, TyKind}; | |
11 | use rustc_lint::{LateContext, LateLintPass, LintContext}; | |
12 | use rustc_middle::hir::map::Map; | |
13 | use rustc_session::{declare_lint_pass, declare_tool_lint}; | |
14 | ||
15 | declare_clippy_lint! { | |
16 | /// **What it does:** Generates clippy code that detects the offending pattern | |
17 | /// | |
18 | /// **Example:** | |
19 | /// ```rust,ignore | |
20 | /// // ./tests/ui/my_lint.rs | |
21 | /// fn foo() { | |
22 | /// // detect the following pattern | |
23 | /// #[clippy::author] | |
24 | /// if x == 42 { | |
25 | /// // but ignore everything from here on | |
26 | /// #![clippy::author = "ignore"] | |
27 | /// } | |
28 | /// () | |
29 | /// } | |
30 | /// ``` | |
31 | /// | |
32 | /// Running `TESTNAME=ui/my_lint cargo uitest` will produce | |
33 | /// a `./tests/ui/new_lint.stdout` file with the generated code: | |
34 | /// | |
35 | /// ```rust,ignore | |
36 | /// // ./tests/ui/new_lint.stdout | |
37 | /// if_chain! { | |
38 | /// if let ExprKind::If(ref cond, ref then, None) = item.kind, | |
39 | /// if let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind, | |
40 | /// if let ExprKind::Path(ref path) = left.kind, | |
41 | /// if let ExprKind::Lit(ref lit) = right.kind, | |
42 | /// if let LitKind::Int(42, _) = lit.node, | |
43 | /// then { | |
44 | /// // report your lint here | |
45 | /// } | |
46 | /// } | |
47 | /// ``` | |
48 | pub LINT_AUTHOR, | |
49 | internal_warn, | |
50 | "helper for writing lints" | |
51 | } | |
52 | ||
53 | declare_lint_pass!(Author => [LINT_AUTHOR]); | |
54 | ||
55 | fn prelude() { | |
56 | println!("if_chain! {{"); | |
57 | } | |
58 | ||
59 | fn done() { | |
60 | println!(" then {{"); | |
61 | println!(" // report your lint here"); | |
62 | println!(" }}"); | |
63 | println!("}}"); | |
64 | } | |
65 | ||
66 | impl<'tcx> LateLintPass<'tcx> for Author { | |
67 | fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { | |
68 | if !has_attr(cx, item.hir_id()) { | |
69 | return; | |
70 | } | |
71 | prelude(); | |
72 | PrintVisitor::new("item").visit_item(item); | |
73 | done(); | |
74 | } | |
75 | ||
76 | fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { | |
77 | if !has_attr(cx, item.hir_id()) { | |
78 | return; | |
79 | } | |
80 | prelude(); | |
81 | PrintVisitor::new("item").visit_impl_item(item); | |
82 | done(); | |
83 | } | |
84 | ||
85 | fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { | |
86 | if !has_attr(cx, item.hir_id()) { | |
87 | return; | |
88 | } | |
89 | prelude(); | |
90 | PrintVisitor::new("item").visit_trait_item(item); | |
91 | done(); | |
92 | } | |
93 | ||
94 | fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx hir::Variant<'_>) { | |
95 | if !has_attr(cx, var.id) { | |
96 | return; | |
97 | } | |
98 | prelude(); | |
99 | let parent_hir_id = cx.tcx.hir().get_parent_node(var.id); | |
100 | PrintVisitor::new("var").visit_variant(var, &hir::Generics::empty(), parent_hir_id); | |
101 | done(); | |
102 | } | |
103 | ||
104 | fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'_>) { | |
105 | if !has_attr(cx, field.hir_id) { | |
106 | return; | |
107 | } | |
108 | prelude(); | |
109 | PrintVisitor::new("field").visit_field_def(field); | |
110 | done(); | |
111 | } | |
112 | ||
113 | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { | |
114 | if !has_attr(cx, expr.hir_id) { | |
115 | return; | |
116 | } | |
117 | prelude(); | |
118 | PrintVisitor::new("expr").visit_expr(expr); | |
119 | done(); | |
120 | } | |
121 | ||
122 | fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { | |
123 | if !has_attr(cx, arm.hir_id) { | |
124 | return; | |
125 | } | |
126 | prelude(); | |
127 | PrintVisitor::new("arm").visit_arm(arm); | |
128 | done(); | |
129 | } | |
130 | ||
131 | fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { | |
132 | if !has_attr(cx, stmt.hir_id) { | |
133 | return; | |
134 | } | |
135 | prelude(); | |
136 | PrintVisitor::new("stmt").visit_stmt(stmt); | |
137 | done(); | |
138 | } | |
139 | ||
140 | fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'_>) { | |
141 | if !has_attr(cx, item.hir_id()) { | |
142 | return; | |
143 | } | |
144 | prelude(); | |
145 | PrintVisitor::new("item").visit_foreign_item(item); | |
146 | done(); | |
147 | } | |
148 | } | |
149 | ||
150 | impl PrintVisitor { | |
151 | #[must_use] | |
152 | fn new(s: &'static str) -> Self { | |
153 | Self { | |
154 | ids: FxHashMap::default(), | |
155 | current: s.to_owned(), | |
156 | } | |
157 | } | |
158 | ||
159 | fn next(&mut self, s: &'static str) -> String { | |
160 | use std::collections::hash_map::Entry::{Occupied, Vacant}; | |
161 | match self.ids.entry(s) { | |
162 | // already there: start numbering from `1` | |
163 | Occupied(mut occ) => { | |
164 | let val = occ.get_mut(); | |
165 | *val += 1; | |
166 | format!("{}{}", s, *val) | |
167 | }, | |
168 | // not there: insert and return name as given | |
169 | Vacant(vac) => { | |
170 | vac.insert(0); | |
171 | s.to_owned() | |
172 | }, | |
173 | } | |
174 | } | |
175 | ||
176 | fn print_qpath(&mut self, path: &QPath<'_>) { | |
177 | if let QPath::LangItem(lang_item, _) = *path { | |
178 | println!( | |
179 | " if matches!({}, QPath::LangItem(LangItem::{:?}, _));", | |
180 | self.current, lang_item, | |
181 | ); | |
182 | } else { | |
183 | print!(" if match_qpath({}, &[", self.current); | |
184 | print_path(path, &mut true); | |
185 | println!("]);"); | |
186 | } | |
187 | } | |
188 | } | |
189 | ||
190 | struct PrintVisitor { | |
191 | /// Fields are the current index that needs to be appended to pattern | |
192 | /// binding names | |
193 | ids: FxHashMap<&'static str, usize>, | |
194 | /// the name that needs to be destructured | |
195 | current: String, | |
196 | } | |
197 | ||
198 | impl<'tcx> Visitor<'tcx> for PrintVisitor { | |
199 | type Map = Map<'tcx>; | |
200 | ||
201 | #[allow(clippy::too_many_lines)] | |
202 | fn visit_expr(&mut self, expr: &Expr<'_>) { | |
203 | print!(" if let ExprKind::"); | |
204 | let current = format!("{}.kind", self.current); | |
205 | match expr.kind { | |
cdc7bbd5 | 206 | ExprKind::Box(inner) => { |
f20569fa XL |
207 | let inner_pat = self.next("inner"); |
208 | println!("Box(ref {}) = {};", inner_pat, current); | |
209 | self.current = inner_pat; | |
210 | self.visit_expr(inner); | |
211 | }, | |
cdc7bbd5 | 212 | ExprKind::Array(elements) => { |
f20569fa XL |
213 | let elements_pat = self.next("elements"); |
214 | println!("Array(ref {}) = {};", elements_pat, current); | |
215 | println!(" if {}.len() == {};", elements_pat, elements.len()); | |
216 | for (i, element) in elements.iter().enumerate() { | |
217 | self.current = format!("{}[{}]", elements_pat, i); | |
218 | self.visit_expr(element); | |
219 | } | |
220 | }, | |
cdc7bbd5 | 221 | ExprKind::Call(func, args) => { |
f20569fa XL |
222 | let func_pat = self.next("func"); |
223 | let args_pat = self.next("args"); | |
224 | println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current); | |
225 | self.current = func_pat; | |
226 | self.visit_expr(func); | |
227 | println!(" if {}.len() == {};", args_pat, args.len()); | |
228 | for (i, arg) in args.iter().enumerate() { | |
229 | self.current = format!("{}[{}]", args_pat, i); | |
230 | self.visit_expr(arg); | |
231 | } | |
232 | }, | |
cdc7bbd5 | 233 | ExprKind::MethodCall(_method_name, ref _generics, _args, ref _fn_span) => { |
f20569fa XL |
234 | println!( |
235 | "MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};", | |
236 | current | |
237 | ); | |
238 | println!(" // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment"); | |
239 | }, | |
cdc7bbd5 | 240 | ExprKind::Tup(elements) => { |
f20569fa XL |
241 | let elements_pat = self.next("elements"); |
242 | println!("Tup(ref {}) = {};", elements_pat, current); | |
243 | println!(" if {}.len() == {};", elements_pat, elements.len()); | |
244 | for (i, element) in elements.iter().enumerate() { | |
245 | self.current = format!("{}[{}]", elements_pat, i); | |
246 | self.visit_expr(element); | |
247 | } | |
248 | }, | |
cdc7bbd5 | 249 | ExprKind::Binary(ref op, left, right) => { |
f20569fa XL |
250 | let op_pat = self.next("op"); |
251 | let left_pat = self.next("left"); | |
252 | let right_pat = self.next("right"); | |
253 | println!( | |
254 | "Binary(ref {}, ref {}, ref {}) = {};", | |
255 | op_pat, left_pat, right_pat, current | |
256 | ); | |
257 | println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat); | |
258 | self.current = left_pat; | |
259 | self.visit_expr(left); | |
260 | self.current = right_pat; | |
261 | self.visit_expr(right); | |
262 | }, | |
cdc7bbd5 | 263 | ExprKind::Unary(ref op, inner) => { |
f20569fa XL |
264 | let inner_pat = self.next("inner"); |
265 | println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current); | |
266 | self.current = inner_pat; | |
267 | self.visit_expr(inner); | |
268 | }, | |
269 | ExprKind::Lit(ref lit) => { | |
270 | let lit_pat = self.next("lit"); | |
271 | println!("Lit(ref {}) = {};", lit_pat, current); | |
272 | match lit.node { | |
273 | LitKind::Bool(val) => println!(" if let LitKind::Bool({:?}) = {}.node;", val, lit_pat), | |
274 | LitKind::Char(c) => println!(" if let LitKind::Char({:?}) = {}.node;", c, lit_pat), | |
275 | LitKind::Err(val) => println!(" if let LitKind::Err({}) = {}.node;", val, lit_pat), | |
276 | LitKind::Byte(b) => println!(" if let LitKind::Byte({}) = {}.node;", b, lit_pat), | |
277 | // FIXME: also check int type | |
278 | LitKind::Int(i, _) => println!(" if let LitKind::Int({}, _) = {}.node;", i, lit_pat), | |
279 | LitKind::Float(_, LitFloatType::Suffixed(_)) => println!( | |
280 | " if let LitKind::Float(_, LitFloatType::Suffixed(_)) = {}.node;", | |
281 | lit_pat | |
282 | ), | |
283 | LitKind::Float(_, LitFloatType::Unsuffixed) => println!( | |
284 | " if let LitKind::Float(_, LitFloatType::Unsuffixed) = {}.node;", | |
285 | lit_pat | |
286 | ), | |
287 | LitKind::ByteStr(ref vec) => { | |
288 | let vec_pat = self.next("vec"); | |
289 | println!(" if let LitKind::ByteStr(ref {}) = {}.node;", vec_pat, lit_pat); | |
290 | println!(" if let [{:?}] = **{};", vec, vec_pat); | |
291 | }, | |
292 | LitKind::Str(ref text, _) => { | |
293 | let str_pat = self.next("s"); | |
294 | println!(" if let LitKind::Str(ref {}, _) = {}.node;", str_pat, lit_pat); | |
295 | println!(" if {}.as_str() == {:?}", str_pat, &*text.as_str()) | |
296 | }, | |
297 | } | |
298 | }, | |
cdc7bbd5 | 299 | ExprKind::Cast(expr, ty) => { |
f20569fa XL |
300 | let cast_pat = self.next("expr"); |
301 | let cast_ty = self.next("cast_ty"); | |
302 | let qp_label = self.next("qp"); | |
303 | ||
304 | println!("Cast(ref {}, ref {}) = {};", cast_pat, cast_ty, current); | |
305 | if let TyKind::Path(ref qp) = ty.kind { | |
306 | println!(" if let TyKind::Path(ref {}) = {}.kind;", qp_label, cast_ty); | |
307 | self.current = qp_label; | |
308 | self.print_qpath(qp); | |
309 | } | |
310 | self.current = cast_pat; | |
311 | self.visit_expr(expr); | |
312 | }, | |
cdc7bbd5 | 313 | ExprKind::Type(expr, _ty) => { |
f20569fa XL |
314 | let cast_pat = self.next("expr"); |
315 | println!("Type(ref {}, _) = {};", cast_pat, current); | |
316 | self.current = cast_pat; | |
317 | self.visit_expr(expr); | |
318 | }, | |
cdc7bbd5 | 319 | ExprKind::Loop(body, _, desugaring, _) => { |
f20569fa XL |
320 | let body_pat = self.next("body"); |
321 | let des = loop_desugaring_name(desugaring); | |
322 | let label_pat = self.next("label"); | |
323 | println!("Loop(ref {}, ref {}, {}) = {};", body_pat, label_pat, des, current); | |
324 | self.current = body_pat; | |
325 | self.visit_block(body); | |
326 | }, | |
cdc7bbd5 | 327 | ExprKind::If(cond, then, ref opt_else) => { |
f20569fa XL |
328 | let cond_pat = self.next("cond"); |
329 | let then_pat = self.next("then"); | |
cdc7bbd5 | 330 | if let Some(else_) = *opt_else { |
f20569fa XL |
331 | let else_pat = self.next("else_"); |
332 | println!( | |
333 | "If(ref {}, ref {}, Some(ref {})) = {};", | |
334 | cond_pat, then_pat, else_pat, current | |
335 | ); | |
336 | self.current = else_pat; | |
337 | self.visit_expr(else_); | |
338 | } else { | |
339 | println!("If(ref {}, ref {}, None) = {};", cond_pat, then_pat, current); | |
340 | } | |
341 | self.current = cond_pat; | |
342 | self.visit_expr(cond); | |
343 | self.current = then_pat; | |
344 | self.visit_expr(then); | |
345 | }, | |
cdc7bbd5 | 346 | ExprKind::Match(expr, arms, desugaring) => { |
f20569fa XL |
347 | let des = desugaring_name(desugaring); |
348 | let expr_pat = self.next("expr"); | |
349 | let arms_pat = self.next("arms"); | |
350 | println!("Match(ref {}, ref {}, {}) = {};", expr_pat, arms_pat, des, current); | |
351 | self.current = expr_pat; | |
352 | self.visit_expr(expr); | |
353 | println!(" if {}.len() == {};", arms_pat, arms.len()); | |
354 | for (i, arm) in arms.iter().enumerate() { | |
355 | self.current = format!("{}[{}].body", arms_pat, i); | |
cdc7bbd5 | 356 | self.visit_expr(arm.body); |
f20569fa XL |
357 | if let Some(ref guard) = arm.guard { |
358 | let guard_pat = self.next("guard"); | |
359 | println!(" if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i); | |
360 | match guard { | |
cdc7bbd5 | 361 | hir::Guard::If(if_expr) => { |
f20569fa XL |
362 | let if_expr_pat = self.next("expr"); |
363 | println!(" if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat); | |
364 | self.current = if_expr_pat; | |
365 | self.visit_expr(if_expr); | |
366 | }, | |
cdc7bbd5 | 367 | hir::Guard::IfLet(if_let_pat, if_let_expr) => { |
f20569fa XL |
368 | let if_let_pat_pat = self.next("pat"); |
369 | let if_let_expr_pat = self.next("expr"); | |
370 | println!( | |
371 | " if let Guard::IfLet(ref {}, ref {}) = {};", | |
372 | if_let_pat_pat, if_let_expr_pat, guard_pat | |
373 | ); | |
374 | self.current = if_let_expr_pat; | |
375 | self.visit_expr(if_let_expr); | |
376 | self.current = if_let_pat_pat; | |
377 | self.visit_pat(if_let_pat); | |
378 | }, | |
379 | } | |
380 | } | |
381 | self.current = format!("{}[{}].pat", arms_pat, i); | |
cdc7bbd5 | 382 | self.visit_pat(arm.pat); |
f20569fa XL |
383 | } |
384 | }, | |
cdc7bbd5 | 385 | ExprKind::Closure(ref _capture_clause, _func, _, _, _) => { |
f20569fa XL |
386 | println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current); |
387 | println!(" // unimplemented: `ExprKind::Closure` is not further destructured at the moment"); | |
388 | }, | |
cdc7bbd5 | 389 | ExprKind::Yield(sub, _) => { |
f20569fa XL |
390 | let sub_pat = self.next("sub"); |
391 | println!("Yield(ref sub) = {};", current); | |
392 | self.current = sub_pat; | |
393 | self.visit_expr(sub); | |
394 | }, | |
cdc7bbd5 | 395 | ExprKind::Block(block, _) => { |
f20569fa XL |
396 | let block_pat = self.next("block"); |
397 | println!("Block(ref {}) = {};", block_pat, current); | |
398 | self.current = block_pat; | |
399 | self.visit_block(block); | |
400 | }, | |
cdc7bbd5 | 401 | ExprKind::Assign(target, value, _) => { |
f20569fa XL |
402 | let target_pat = self.next("target"); |
403 | let value_pat = self.next("value"); | |
404 | println!( | |
405 | "Assign(ref {}, ref {}, ref _span) = {};", | |
406 | target_pat, value_pat, current | |
407 | ); | |
408 | self.current = target_pat; | |
409 | self.visit_expr(target); | |
410 | self.current = value_pat; | |
411 | self.visit_expr(value); | |
412 | }, | |
cdc7bbd5 | 413 | ExprKind::AssignOp(ref op, target, value) => { |
f20569fa XL |
414 | let op_pat = self.next("op"); |
415 | let target_pat = self.next("target"); | |
416 | let value_pat = self.next("value"); | |
417 | println!( | |
418 | "AssignOp(ref {}, ref {}, ref {}) = {};", | |
419 | op_pat, target_pat, value_pat, current | |
420 | ); | |
421 | println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat); | |
422 | self.current = target_pat; | |
423 | self.visit_expr(target); | |
424 | self.current = value_pat; | |
425 | self.visit_expr(value); | |
426 | }, | |
cdc7bbd5 | 427 | ExprKind::Field(object, ref field_ident) => { |
f20569fa XL |
428 | let obj_pat = self.next("object"); |
429 | let field_name_pat = self.next("field_name"); | |
430 | println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current); | |
431 | println!(" if {}.as_str() == {:?}", field_name_pat, field_ident.as_str()); | |
432 | self.current = obj_pat; | |
433 | self.visit_expr(object); | |
434 | }, | |
cdc7bbd5 | 435 | ExprKind::Index(object, index) => { |
f20569fa XL |
436 | let object_pat = self.next("object"); |
437 | let index_pat = self.next("index"); | |
438 | println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current); | |
439 | self.current = object_pat; | |
440 | self.visit_expr(object); | |
441 | self.current = index_pat; | |
442 | self.visit_expr(index); | |
443 | }, | |
444 | ExprKind::Path(ref path) => { | |
445 | let path_pat = self.next("path"); | |
446 | println!("Path(ref {}) = {};", path_pat, current); | |
447 | self.current = path_pat; | |
448 | self.print_qpath(path); | |
449 | }, | |
cdc7bbd5 | 450 | ExprKind::AddrOf(kind, mutability, inner) => { |
f20569fa XL |
451 | let inner_pat = self.next("inner"); |
452 | println!( | |
453 | "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};", | |
454 | kind, mutability, inner_pat, current | |
455 | ); | |
456 | self.current = inner_pat; | |
457 | self.visit_expr(inner); | |
458 | }, | |
459 | ExprKind::Break(ref _destination, ref opt_value) => { | |
460 | let destination_pat = self.next("destination"); | |
cdc7bbd5 | 461 | if let Some(value) = *opt_value { |
f20569fa XL |
462 | let value_pat = self.next("value"); |
463 | println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current); | |
464 | self.current = value_pat; | |
465 | self.visit_expr(value); | |
466 | } else { | |
467 | println!("Break(ref {}, None) = {};", destination_pat, current); | |
468 | } | |
469 | // FIXME: implement label printing | |
470 | }, | |
471 | ExprKind::Continue(ref _destination) => { | |
472 | let destination_pat = self.next("destination"); | |
473 | println!("Again(ref {}) = {};", destination_pat, current); | |
474 | // FIXME: implement label printing | |
475 | }, | |
476 | ExprKind::Ret(ref opt_value) => { | |
cdc7bbd5 | 477 | if let Some(value) = *opt_value { |
f20569fa XL |
478 | let value_pat = self.next("value"); |
479 | println!("Ret(Some(ref {})) = {};", value_pat, current); | |
480 | self.current = value_pat; | |
481 | self.visit_expr(value); | |
482 | } else { | |
483 | println!("Ret(None) = {};", current); | |
484 | } | |
485 | }, | |
486 | ExprKind::InlineAsm(_) => { | |
487 | println!("InlineAsm(_) = {};", current); | |
488 | println!(" // unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment"); | |
489 | }, | |
490 | ExprKind::LlvmInlineAsm(_) => { | |
491 | println!("LlvmInlineAsm(_) = {};", current); | |
492 | println!(" // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment"); | |
493 | }, | |
cdc7bbd5 | 494 | ExprKind::Struct(path, fields, ref opt_base) => { |
f20569fa XL |
495 | let path_pat = self.next("path"); |
496 | let fields_pat = self.next("fields"); | |
cdc7bbd5 | 497 | if let Some(base) = *opt_base { |
f20569fa XL |
498 | let base_pat = self.next("base"); |
499 | println!( | |
500 | "Struct(ref {}, ref {}, Some(ref {})) = {};", | |
501 | path_pat, fields_pat, base_pat, current | |
502 | ); | |
503 | self.current = base_pat; | |
504 | self.visit_expr(base); | |
505 | } else { | |
506 | println!("Struct(ref {}, ref {}, None) = {};", path_pat, fields_pat, current); | |
507 | } | |
508 | self.current = path_pat; | |
509 | self.print_qpath(path); | |
510 | println!(" if {}.len() == {};", fields_pat, fields.len()); | |
511 | println!(" // unimplemented: field checks"); | |
512 | }, | |
513 | ExprKind::ConstBlock(_) => { | |
514 | let value_pat = self.next("value"); | |
515 | println!("Const({})", value_pat); | |
516 | self.current = value_pat; | |
517 | }, | |
518 | // FIXME: compute length (needs type info) | |
cdc7bbd5 | 519 | ExprKind::Repeat(value, _) => { |
f20569fa XL |
520 | let value_pat = self.next("value"); |
521 | println!("Repeat(ref {}, _) = {};", value_pat, current); | |
522 | println!("// unimplemented: repeat count check"); | |
523 | self.current = value_pat; | |
524 | self.visit_expr(value); | |
525 | }, | |
526 | ExprKind::Err => { | |
527 | println!("Err = {}", current); | |
528 | }, | |
cdc7bbd5 | 529 | ExprKind::DropTemps(expr) => { |
f20569fa XL |
530 | let expr_pat = self.next("expr"); |
531 | println!("DropTemps(ref {}) = {};", expr_pat, current); | |
532 | self.current = expr_pat; | |
533 | self.visit_expr(expr); | |
534 | }, | |
535 | } | |
536 | } | |
537 | ||
538 | fn visit_block(&mut self, block: &Block<'_>) { | |
539 | let trailing_pat = self.next("trailing_expr"); | |
540 | println!(" if let Some({}) = &{}.expr;", trailing_pat, self.current); | |
541 | println!(" if {}.stmts.len() == {};", self.current, block.stmts.len()); | |
542 | let current = self.current.clone(); | |
543 | for (i, stmt) in block.stmts.iter().enumerate() { | |
544 | self.current = format!("{}.stmts[{}]", current, i); | |
545 | self.visit_stmt(stmt); | |
546 | } | |
547 | } | |
548 | ||
549 | #[allow(clippy::too_many_lines)] | |
550 | fn visit_pat(&mut self, pat: &Pat<'_>) { | |
551 | print!(" if let PatKind::"); | |
552 | let current = format!("{}.kind", self.current); | |
553 | match pat.kind { | |
554 | PatKind::Wild => println!("Wild = {};", current), | |
555 | PatKind::Binding(anno, .., ident, ref sub) => { | |
556 | let anno_pat = match anno { | |
557 | BindingAnnotation::Unannotated => "BindingAnnotation::Unannotated", | |
558 | BindingAnnotation::Mutable => "BindingAnnotation::Mutable", | |
559 | BindingAnnotation::Ref => "BindingAnnotation::Ref", | |
560 | BindingAnnotation::RefMut => "BindingAnnotation::RefMut", | |
561 | }; | |
562 | let name_pat = self.next("name"); | |
cdc7bbd5 | 563 | if let Some(sub) = *sub { |
f20569fa XL |
564 | let sub_pat = self.next("sub"); |
565 | println!( | |
566 | "Binding({}, _, {}, Some(ref {})) = {};", | |
567 | anno_pat, name_pat, sub_pat, current | |
568 | ); | |
569 | self.current = sub_pat; | |
570 | self.visit_pat(sub); | |
571 | } else { | |
572 | println!("Binding({}, _, {}, None) = {};", anno_pat, name_pat, current); | |
573 | } | |
574 | println!(" if {}.as_str() == \"{}\";", name_pat, ident.as_str()); | |
575 | }, | |
cdc7bbd5 | 576 | PatKind::Struct(ref path, fields, ignore) => { |
f20569fa XL |
577 | let path_pat = self.next("path"); |
578 | let fields_pat = self.next("fields"); | |
579 | println!( | |
580 | "Struct(ref {}, ref {}, {}) = {};", | |
581 | path_pat, fields_pat, ignore, current | |
582 | ); | |
583 | self.current = path_pat; | |
584 | self.print_qpath(path); | |
585 | println!(" if {}.len() == {};", fields_pat, fields.len()); | |
586 | println!(" // unimplemented: field checks"); | |
587 | }, | |
cdc7bbd5 | 588 | PatKind::Or(fields) => { |
f20569fa XL |
589 | let fields_pat = self.next("fields"); |
590 | println!("Or(ref {}) = {};", fields_pat, current); | |
591 | println!(" if {}.len() == {};", fields_pat, fields.len()); | |
592 | println!(" // unimplemented: field checks"); | |
593 | }, | |
cdc7bbd5 | 594 | PatKind::TupleStruct(ref path, fields, skip_pos) => { |
f20569fa XL |
595 | let path_pat = self.next("path"); |
596 | let fields_pat = self.next("fields"); | |
597 | println!( | |
598 | "TupleStruct(ref {}, ref {}, {:?}) = {};", | |
599 | path_pat, fields_pat, skip_pos, current | |
600 | ); | |
601 | self.current = path_pat; | |
602 | self.print_qpath(path); | |
603 | println!(" if {}.len() == {};", fields_pat, fields.len()); | |
604 | println!(" // unimplemented: field checks"); | |
605 | }, | |
606 | PatKind::Path(ref path) => { | |
607 | let path_pat = self.next("path"); | |
608 | println!("Path(ref {}) = {};", path_pat, current); | |
609 | self.current = path_pat; | |
610 | self.print_qpath(path); | |
611 | }, | |
cdc7bbd5 | 612 | PatKind::Tuple(fields, skip_pos) => { |
f20569fa XL |
613 | let fields_pat = self.next("fields"); |
614 | println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current); | |
615 | println!(" if {}.len() == {};", fields_pat, fields.len()); | |
616 | println!(" // unimplemented: field checks"); | |
617 | }, | |
cdc7bbd5 | 618 | PatKind::Box(pat) => { |
f20569fa XL |
619 | let pat_pat = self.next("pat"); |
620 | println!("Box(ref {}) = {};", pat_pat, current); | |
621 | self.current = pat_pat; | |
622 | self.visit_pat(pat); | |
623 | }, | |
cdc7bbd5 | 624 | PatKind::Ref(pat, muta) => { |
f20569fa XL |
625 | let pat_pat = self.next("pat"); |
626 | println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current); | |
627 | self.current = pat_pat; | |
628 | self.visit_pat(pat); | |
629 | }, | |
cdc7bbd5 | 630 | PatKind::Lit(lit_expr) => { |
f20569fa XL |
631 | let lit_expr_pat = self.next("lit_expr"); |
632 | println!("Lit(ref {}) = {}", lit_expr_pat, current); | |
633 | self.current = lit_expr_pat; | |
634 | self.visit_expr(lit_expr); | |
635 | }, | |
636 | PatKind::Range(ref start, ref end, end_kind) => { | |
637 | let start_pat = self.next("start"); | |
638 | let end_pat = self.next("end"); | |
639 | println!( | |
640 | "Range(ref {}, ref {}, RangeEnd::{:?}) = {};", | |
641 | start_pat, end_pat, end_kind, current | |
642 | ); | |
643 | self.current = start_pat; | |
644 | walk_list!(self, visit_expr, start); | |
645 | self.current = end_pat; | |
646 | walk_list!(self, visit_expr, end); | |
647 | }, | |
cdc7bbd5 | 648 | PatKind::Slice(start, ref middle, end) => { |
f20569fa XL |
649 | let start_pat = self.next("start"); |
650 | let end_pat = self.next("end"); | |
cdc7bbd5 | 651 | if let Some(middle) = middle { |
f20569fa XL |
652 | let middle_pat = self.next("middle"); |
653 | println!( | |
654 | "Slice(ref {}, Some(ref {}), ref {}) = {};", | |
655 | start_pat, middle_pat, end_pat, current | |
656 | ); | |
657 | self.current = middle_pat; | |
658 | self.visit_pat(middle); | |
659 | } else { | |
660 | println!("Slice(ref {}, None, ref {}) = {};", start_pat, end_pat, current); | |
661 | } | |
662 | println!(" if {}.len() == {};", start_pat, start.len()); | |
663 | for (i, pat) in start.iter().enumerate() { | |
664 | self.current = format!("{}[{}]", start_pat, i); | |
665 | self.visit_pat(pat); | |
666 | } | |
667 | println!(" if {}.len() == {};", end_pat, end.len()); | |
668 | for (i, pat) in end.iter().enumerate() { | |
669 | self.current = format!("{}[{}]", end_pat, i); | |
670 | self.visit_pat(pat); | |
671 | } | |
672 | }, | |
673 | } | |
674 | } | |
675 | ||
676 | fn visit_stmt(&mut self, s: &Stmt<'_>) { | |
677 | print!(" if let StmtKind::"); | |
678 | let current = format!("{}.kind", self.current); | |
679 | match s.kind { | |
680 | // A local (let) binding: | |
cdc7bbd5 | 681 | StmtKind::Local(local) => { |
f20569fa XL |
682 | let local_pat = self.next("local"); |
683 | println!("Local(ref {}) = {};", local_pat, current); | |
cdc7bbd5 | 684 | if let Some(init) = local.init { |
f20569fa XL |
685 | let init_pat = self.next("init"); |
686 | println!(" if let Some(ref {}) = {}.init;", init_pat, local_pat); | |
687 | self.current = init_pat; | |
688 | self.visit_expr(init); | |
689 | } | |
690 | self.current = format!("{}.pat", local_pat); | |
cdc7bbd5 | 691 | self.visit_pat(local.pat); |
f20569fa XL |
692 | }, |
693 | // An item binding: | |
694 | StmtKind::Item(_) => { | |
695 | println!("Item(item_id) = {};", current); | |
696 | }, | |
697 | ||
698 | // Expr without trailing semi-colon (must have unit type): | |
cdc7bbd5 | 699 | StmtKind::Expr(e) => { |
f20569fa XL |
700 | let e_pat = self.next("e"); |
701 | println!("Expr(ref {}, _) = {}", e_pat, current); | |
702 | self.current = e_pat; | |
703 | self.visit_expr(e); | |
704 | }, | |
705 | ||
706 | // Expr with trailing semi-colon (may have any type): | |
cdc7bbd5 | 707 | StmtKind::Semi(e) => { |
f20569fa XL |
708 | let e_pat = self.next("e"); |
709 | println!("Semi(ref {}, _) = {}", e_pat, current); | |
710 | self.current = e_pat; | |
711 | self.visit_expr(e); | |
712 | }, | |
713 | } | |
714 | } | |
715 | ||
716 | fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { | |
717 | NestedVisitorMap::None | |
718 | } | |
719 | } | |
720 | ||
721 | fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { | |
722 | let attrs = cx.tcx.hir().attrs(hir_id); | |
723 | get_attr(cx.sess(), attrs, "author").count() > 0 | |
724 | } | |
725 | ||
726 | #[must_use] | |
727 | fn desugaring_name(des: hir::MatchSource) -> String { | |
728 | match des { | |
729 | hir::MatchSource::ForLoopDesugar => "MatchSource::ForLoopDesugar".to_string(), | |
730 | hir::MatchSource::TryDesugar => "MatchSource::TryDesugar".to_string(), | |
731 | hir::MatchSource::WhileDesugar => "MatchSource::WhileDesugar".to_string(), | |
732 | hir::MatchSource::WhileLetDesugar => "MatchSource::WhileLetDesugar".to_string(), | |
733 | hir::MatchSource::Normal => "MatchSource::Normal".to_string(), | |
734 | hir::MatchSource::IfLetDesugar { contains_else_clause } => format!( | |
735 | "MatchSource::IfLetDesugar {{ contains_else_clause: {} }}", | |
736 | contains_else_clause | |
737 | ), | |
738 | hir::MatchSource::IfLetGuardDesugar => "MatchSource::IfLetGuardDesugar".to_string(), | |
739 | hir::MatchSource::AwaitDesugar => "MatchSource::AwaitDesugar".to_string(), | |
740 | } | |
741 | } | |
742 | ||
743 | #[must_use] | |
744 | fn loop_desugaring_name(des: hir::LoopSource) -> &'static str { | |
745 | match des { | |
746 | hir::LoopSource::ForLoop => "LoopSource::ForLoop", | |
747 | hir::LoopSource::Loop => "LoopSource::Loop", | |
748 | hir::LoopSource::While => "LoopSource::While", | |
749 | hir::LoopSource::WhileLet => "LoopSource::WhileLet", | |
750 | } | |
751 | } | |
752 | ||
753 | fn print_path(path: &QPath<'_>, first: &mut bool) { | |
754 | match *path { | |
cdc7bbd5 | 755 | QPath::Resolved(_, path) => { |
f20569fa XL |
756 | for segment in path.segments { |
757 | if *first { | |
758 | *first = false; | |
759 | } else { | |
760 | print!(", "); | |
761 | } | |
762 | print!("{:?}", segment.ident.as_str()); | |
763 | } | |
764 | }, | |
cdc7bbd5 | 765 | QPath::TypeRelative(ty, segment) => match ty.kind { |
f20569fa XL |
766 | hir::TyKind::Path(ref inner_path) => { |
767 | print_path(inner_path, first); | |
768 | if *first { | |
769 | *first = false; | |
770 | } else { | |
771 | print!(", "); | |
772 | } | |
773 | print!("{:?}", segment.ident.as_str()); | |
774 | }, | |
775 | ref other => print!("/* unimplemented: {:?}*/", other), | |
776 | }, | |
777 | QPath::LangItem(..) => panic!("print_path: called for lang item qpath"), | |
778 | } | |
779 | } |