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