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