about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/lib.rs1
-rw-r--r--clippy_lints/src/utils/author.rs1386
-rw-r--r--tests/ui/author.stdout10
-rw-r--r--tests/ui/author/blocks.stdout66
-rw-r--r--tests/ui/author/call.stdout10
-rw-r--r--tests/ui/author/if.stdout38
-rw-r--r--tests/ui/author/issue_3849.stdout14
-rw-r--r--tests/ui/author/loop.stdout110
-rw-r--r--tests/ui/author/matches.stdout45
-rw-r--r--tests/ui/author/repeat.stdout5
-rw-r--r--tests/ui/author/struct.stdout55
11 files changed, 680 insertions, 1060 deletions
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 2445a0aeed0..22f34491962 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -2,6 +2,7 @@
 
 #![feature(box_patterns)]
 #![feature(drain_filter)]
+#![feature(format_args_capture)]
 #![feature(in_band_lifetimes)]
 #![feature(iter_zip)]
 #![feature(once_cell)]
diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs
index 38157cc16ef..d20bf341318 100644
--- a/clippy_lints/src/utils/author.rs
+++ b/clippy_lints/src/utils/author.rs
@@ -3,14 +3,14 @@
 
 use clippy_utils::{get_attr, higher};
 use rustc_ast::ast::{LitFloatType, LitKind};
-use rustc_ast::{walk_list, Label, LitIntType};
+use rustc_ast::LitIntType;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir as hir;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
-use rustc_hir::{Arm, Block, Expr, ExprKind, FnRetTy, Lit, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind, TyKind};
+use rustc_hir::{ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::{Ident, Symbol};
+use std::fmt::{Display, Formatter, Write as _};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -53,6 +53,42 @@ declare_clippy_lint! {
 
 declare_lint_pass!(Author => [LINT_AUTHOR]);
 
+/// Writes a line of output with indentation added
+macro_rules! out {
+    ($($t:tt)*) => {
+        println!("    {}", format_args!($($t)*))
+    };
+}
+
+/// The variables passed in are replaced with `&Binding`s where the `value` field is set
+/// to the original value of the variable. The `name` field is set to the name of the variable
+/// (using `stringify!`) and is adjusted to avoid duplicate names.
+/// Note that the `Binding` may be printed directly to output the `name`.
+macro_rules! bind {
+    ($self:ident $(, $name:ident)+) => {
+        $(let $name = & $self.bind(stringify!($name), $name);)+
+    };
+}
+
+/// Transforms the given `Option<T>` varibles into `OptionPat<Binding<T>>`.
+/// This displays as `Some($name)` or `None` when printed. The name of the inner binding
+/// is set to the name of the variable passed to the macro.
+macro_rules! opt_bind {
+    ($self:ident $(, $name:ident)+) => {
+        $(let $name = OptionPat::new($name.map(|o| $self.bind(stringify!($name), o)));)+
+    };
+}
+
+/// Creates a `Binding` that accesses the field of an existing `Binding`
+macro_rules! field {
+    ($binding:ident.$field:ident) => {
+        &Binding {
+            name: $binding.name.to_string() + stringify!(.$field),
+            value: $binding.value.$field,
+        }
+    };
+}
+
 fn prelude() {
     println!("if_chain! {{");
 }
@@ -66,1023 +102,594 @@ fn done() {
 
 impl<'tcx> LateLintPass<'tcx> for Author {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
-        if !has_attr(cx, item.hir_id()) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("item", cx).visit_item(item);
-        done();
+        check_item(cx, item.hir_id());
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
-        if !has_attr(cx, item.hir_id()) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("item", cx).visit_impl_item(item);
-        done();
+        check_item(cx, item.hir_id());
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
-        if !has_attr(cx, item.hir_id()) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("item", cx).visit_trait_item(item);
-        done();
+        check_item(cx, item.hir_id());
     }
 
-    fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx hir::Variant<'_>) {
-        if !has_attr(cx, var.id) {
-            return;
-        }
-        prelude();
-        let parent_hir_id = cx.tcx.hir().get_parent_node(var.id);
-        PrintVisitor::new("var", cx).visit_variant(var, &hir::Generics::empty(), parent_hir_id);
-        done();
-    }
-
-    fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'_>) {
-        if !has_attr(cx, field.hir_id) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("field", cx).visit_field_def(field);
-        done();
+    fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
+        check_node(cx, arm.hir_id, |v| {
+            v.arm(&v.bind("arm", arm));
+        });
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if !has_attr(cx, expr.hir_id) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("expr", cx).visit_expr(expr);
-        done();
-    }
-
-    fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
-        if !has_attr(cx, arm.hir_id) {
-            return;
-        }
-        prelude();
-        PrintVisitor::new("arm", cx).visit_arm(arm);
-        done();
+        check_node(cx, expr.hir_id, |v| {
+            v.expr(&v.bind("expr", expr));
+        });
     }
 
     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
-        if !has_attr(cx, stmt.hir_id) {
-            return;
-        }
         match stmt.kind {
             StmtKind::Expr(e) | StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return,
             _ => {},
         }
+        check_node(cx, stmt.hir_id, |v| {
+            v.stmt(&v.bind("stmt", stmt));
+        });
+    }
+}
+
+fn check_item(cx: &LateContext<'_>, hir_id: HirId) {
+    let hir = cx.tcx.hir();
+    if let Some(body_id) = hir.maybe_body_owned_by(hir_id) {
+        check_node(cx, hir_id, |v| {
+            v.expr(&v.bind("expr", &hir.body(body_id).value));
+        });
+    }
+}
+
+fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) {
+    if has_attr(cx, hir_id) {
         prelude();
-        PrintVisitor::new("stmt", cx).visit_stmt(stmt);
+        f(&PrintVisitor::new(cx));
         done();
     }
+}
 
-    fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'_>) {
-        if !has_attr(cx, item.hir_id()) {
-            return;
+struct Binding<T> {
+    name: String,
+    value: T,
+}
+
+impl<T> Display for Binding<T> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        f.write_str(&self.name)
+    }
+}
+
+struct OptionPat<T> {
+    pub opt: Option<T>,
+}
+
+impl<T> OptionPat<T> {
+    fn new(opt: Option<T>) -> Self {
+        Self { opt }
+    }
+
+    fn if_some(&self, f: impl Fn(&T)) {
+        if let Some(t) = &self.opt {
+            f(t);
+        }
+    }
+}
+
+impl<T: Display> Display for OptionPat<T> {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        match &self.opt {
+            None => f.write_str("None"),
+            Some(node) => write!(f, "Some({node})"),
         }
-        prelude();
-        PrintVisitor::new("item", cx).visit_foreign_item(item);
-        done();
     }
 }
 
+struct PrintVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    /// Fields are the current index that needs to be appended to pattern
+    /// binding names
+    ids: std::cell::Cell<FxHashMap<&'static str, u32>>,
+}
+
+#[allow(clippy::unused_self)]
 impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
-    #[must_use]
-    fn new(s: &'static str, cx: &'a LateContext<'tcx>) -> Self {
+    fn new(cx: &'a LateContext<'tcx>) -> Self {
         Self {
-            ids: FxHashMap::default(),
-            current: s.to_owned(),
             cx,
+            ids: std::cell::Cell::default(),
         }
     }
 
-    fn next(&mut self, s: &'static str) -> String {
-        use std::collections::hash_map::Entry::{Occupied, Vacant};
-        match self.ids.entry(s) {
-            // already there: start numbering from `1`
-            Occupied(mut occ) => {
-                let val = occ.get_mut();
-                *val += 1;
-                format!("{}{}", s, *val)
-            },
-            // not there: insert and return name as given
-            Vacant(vac) => {
-                vac.insert(0);
-                s.to_owned()
+    fn next(&self, s: &'static str) -> String {
+        let mut ids = self.ids.take();
+        let out = match *ids.entry(s).and_modify(|n| *n += 1).or_default() {
+            // first usage of the name, use it as is
+            0 => s.to_string(),
+            // append a number starting with 1
+            n => format!("{s}{n}"),
+        };
+        self.ids.set(ids);
+        out
+    }
+
+    fn bind<T>(&self, name: &'static str, value: T) -> Binding<T> {
+        let name = self.next(name);
+        Binding { name, value }
+    }
+
+    fn option<T: Copy>(&self, option: &Binding<Option<T>>, name: &'static str, f: impl Fn(&Binding<T>)) {
+        match option.value {
+            None => out!("if {option}.is_none();"),
+            Some(value) => {
+                let value = &self.bind(name, value);
+                out!("if let Some({value}) = {option};");
+                f(value);
             },
         }
     }
 
-    fn print_qpath(&mut self, path: &QPath<'_>) {
-        if let QPath::LangItem(lang_item, _) = *path {
-            println!(
-                "    if matches!({}, QPath::LangItem(LangItem::{:?}, _));",
-                self.current, lang_item,
-            );
+    fn slice<T>(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) {
+        if slice.value.is_empty() {
+            out!("if {slice}.is_empty();");
         } else {
-            print!("    if match_qpath({}, &[", self.current);
-            print_path(path, &mut true);
-            println!("]);");
+            out!("if {slice}.len() == {};", slice.value.len());
+            for (i, value) in slice.value.iter().enumerate() {
+                let name = format!("{slice}[{i}]");
+                f(&Binding { name, value });
+            }
         }
     }
 
-    fn print_label(&mut self, label: Option<Label>) {
-        if let Some(label) = label {
-            let label_bind = self.next("label");
+    fn destination(&self, destination: &Binding<hir::Destination>) {
+        self.option(field!(destination.label), "label", |label| {
+            self.ident(field!(label.ident));
+        });
+    }
 
-            println!("    if let Some(ref {}) = {}", label_bind, self.current);
+    fn ident(&self, ident: &Binding<Ident>) {
+        out!("if {ident}.as_str() == {:?};", ident.value.as_str());
+    }
 
-            let label_name_bind = self.next("label_name");
-            let label_name = label.ident.name;
+    fn symbol(&self, symbol: &Binding<Symbol>) {
+        out!("if {symbol}.as_str() == {:?};", symbol.value.as_str());
+    }
 
-            println!(
-                "    if {}.ident.name.as_str() == {:?};",
-                label_name_bind,
-                label_name.as_str()
-            );
+    fn qpath(&self, qpath: &Binding<&QPath<'_>>) {
+        if let QPath::LangItem(lang_item, _) = *qpath.value {
+            out!("if matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _));");
+        } else {
+            out!("if match_qpath({qpath}, &[{}]);", path_to_string(qpath.value));
         }
     }
 
-    fn print_lit_expr(&mut self, lit: &Lit, current: &str) {
-        let lit_pat = self.next("lit");
-
-        println!("Lit(ref {}) = {};", lit_pat, current);
+    fn lit(&self, lit: &Binding<&Lit>) {
+        let kind = |kind| out!("if let LitKind::{kind} = {lit}.node;");
+        macro_rules! kind {
+            ($($t:tt)*) => (kind(format_args!($($t)*)));
+        }
 
-        match lit.node {
-            LitKind::Bool(val) => println!("    if let LitKind::Bool({:?}) = {}.node;", val, lit_pat),
-            LitKind::Char(c) => println!("    if let LitKind::Char({:?}) = {}.node;", c, lit_pat),
-            LitKind::Err(val) => println!("    if let LitKind::Err({}) = {}.node;", val, lit_pat),
-            LitKind::Byte(b) => println!("    if let LitKind::Byte({}) = {}.node;", b, lit_pat),
+        match lit.value.node {
+            LitKind::Bool(val) => kind!("Bool({val:?})"),
+            LitKind::Char(c) => kind!("Char({c:?})"),
+            LitKind::Err(val) => kind!("Err({val})"),
+            LitKind::Byte(b) => kind!("Byte({b})"),
             LitKind::Int(i, suffix) => {
                 let int_ty = match suffix {
-                    LitIntType::Signed(int_ty) => format!("LitIntType::Signed(IntTy::{:?})", int_ty),
-                    LitIntType::Unsigned(uint_ty) => format!("LitIntType::Unsigned(UintTy::{:?})", uint_ty),
+                    LitIntType::Signed(int_ty) => format!("LitIntType::Signed(IntTy::{int_ty:?})"),
+                    LitIntType::Unsigned(uint_ty) => format!("LitIntType::Unsigned(UintTy::{uint_ty:?})"),
                     LitIntType::Unsuffixed => String::from("LitIntType::Unsuffixed"),
                 };
-
-                println!("    if let LitKind::Int({}, {}) = {}.node;", i, int_ty, lit_pat);
+                kind!("Int({i}, {int_ty})");
             },
             LitKind::Float(_, suffix) => {
                 let float_ty = match suffix {
-                    LitFloatType::Suffixed(suffix_ty) => format!("LitFloatType::Suffixed(FloatTy::{:?})", suffix_ty),
+                    LitFloatType::Suffixed(suffix_ty) => format!("LitFloatType::Suffixed(FloatTy::{suffix_ty:?})"),
                     LitFloatType::Unsuffixed => String::from("LitFloatType::Unsuffixed"),
                 };
-
-                println!("    if let LitKind::Float(_, {}) = {}.node;", float_ty, lit_pat);
+                kind!("Float(_, {float_ty})");
             },
             LitKind::ByteStr(ref vec) => {
-                let vec_pat = self.next("vec");
-
-                println!("    if let LitKind::ByteStr(ref {}) = {}.node;", vec_pat, lit_pat);
-                println!("    if let [{:?}] = **{};", vec, vec_pat);
+                bind!(self, vec);
+                kind!("ByteStr(ref {vec})");
+                out!("if let [{:?}] = **{vec};", vec.value);
             },
-            LitKind::Str(ref text, _) => {
-                let str_pat = self.next("s");
-
-                println!("    if let LitKind::Str(ref {}, _) = {}.node;", str_pat, lit_pat);
-                println!("    if {}.as_str() == {:?}", str_pat, &*text.as_str());
+            LitKind::Str(s, _) => {
+                bind!(self, s);
+                kind!("Str({s}, _)");
+                self.symbol(s);
             },
         }
     }
 
-    fn print_match_expr(&mut self, expr: &Expr<'_>, arms: &[Arm<'_>], des: MatchSource, current: &str) {
-        let expr_pat = self.next("expr");
-        let arms_pat = self.next("arms");
-
-        println!(
-            "Match(ref {}, ref {}, MatchSource::{:?}) = {};",
-            expr_pat, arms_pat, des, current
-        );
-
-        self.current = expr_pat;
-        self.visit_expr(expr);
-
-        println!("    if {}.len() == {};", arms_pat, arms.len());
-
-        for (i, arm) in arms.iter().enumerate() {
-            self.current = format!("{}[{}].body", arms_pat, i);
-            self.visit_expr(arm.body);
-
-            if let Some(ref guard) = arm.guard {
-                let guard_pat = self.next("guard");
-
-                println!("    if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i);
-
-                match guard {
-                    hir::Guard::If(if_expr) => {
-                        let if_expr_pat = self.next("expr");
-
-                        println!("    if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat);
-
-                        self.current = if_expr_pat;
-                        self.visit_expr(if_expr);
-                    },
-                    hir::Guard::IfLet(if_let_pat, if_let_expr) => {
-                        let if_let_pat_pat = self.next("pat");
-                        let if_let_expr_pat = self.next("expr");
-
-                        println!(
-                            "    if let Guard::IfLet(ref {}, ref {}) = {};",
-                            if_let_pat_pat, if_let_expr_pat, guard_pat
-                        );
-
-                        self.current = if_let_expr_pat;
-                        self.visit_expr(if_let_expr);
-
-                        self.current = if_let_pat_pat;
-                        self.visit_pat(if_let_pat);
-                    },
-                }
-            }
-            self.current = format!("{}[{}].pat", arms_pat, i);
-            self.visit_pat(arm.pat);
+    fn arm(&self, arm: &Binding<&hir::Arm<'_>>) {
+        self.pat(field!(arm.pat));
+        match arm.value.guard {
+            None => out!("if {arm}.guard.is_none();"),
+            Some(hir::Guard::If(expr)) => {
+                bind!(self, expr);
+                out!("if let Some(Guard::If({expr})) = {arm}.guard;");
+                self.expr(expr);
+            },
+            Some(hir::Guard::IfLet(pat, expr)) => {
+                bind!(self, pat, expr);
+                out!("if let Some(Guard::IfLet({pat}, {expr}) = {arm}.guard;");
+                self.pat(pat);
+                self.expr(expr);
+            },
         }
+        self.expr(field!(arm.body));
     }
 
-    fn check_higher(&mut self, expr: &Expr<'_>) -> bool {
-        if let Some(higher::While { condition, body }) = higher::While::hir(expr) {
-            let condition_pat = self.next("condition");
-            let body_pat = self.next("body");
-
-            println!(
-                "    if let Some(higher::While {{ condition: {}, body: {} }}) = higher::While::hir({})",
-                condition_pat, body_pat, self.current
+    #[allow(clippy::too_many_lines)]
+    fn expr(&self, expr: &Binding<&hir::Expr<'_>>) {
+        if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) {
+            bind!(self, condition, body);
+            out!(
+                "if let Some(higher::While {{ condition: {condition}, body: {body} }}) \
+                = higher::While::hir({expr});"
             );
-
-            self.current = condition_pat;
-            self.visit_expr(condition);
-
-            self.current = body_pat;
-            self.visit_expr(body);
-
-            return true;
+            self.expr(condition);
+            self.expr(body);
+            return;
         }
 
         if let Some(higher::WhileLet {
             let_pat,
             let_expr,
             if_then,
-        }) = higher::WhileLet::hir(expr)
+        }) = higher::WhileLet::hir(expr.value)
         {
-            let let_pat_ = self.next("let_pat");
-            let let_expr_pat = self.next("let_expr");
-            let if_then_pat = self.next("if_then");
-
-            println!(
-                "    if let Some(higher::WhileLet {{ let_pat: {}, let_expr: {}, if_then: {} }}) = higher::WhileLet::hir({})",
-                let_pat_, let_expr_pat, if_then_pat, self.current
+            bind!(self, let_pat, let_expr, if_then);
+            out!(
+                "if let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \
+                = higher::WhileLet::hir({expr});"
             );
-
-            self.current = let_pat_;
-            self.visit_pat(let_pat);
-
-            self.current = let_expr_pat;
-            self.visit_expr(let_expr);
-
-            self.current = if_then_pat;
-            self.visit_expr(if_then);
-
-            return true;
-        }
-
-        if let Some(higher::IfLet {
-            let_pat,
-            let_expr,
-            if_then,
-            if_else,
-        }) = higher::IfLet::hir(self.cx, expr)
-        {
-            let let_pat_ = self.next("let_pat");
-            let let_expr_pat = self.next("let_expr");
-            let if_then_pat = self.next("if_then");
-            let else_pat = self.next("else_expr");
-
-            println!(
-                "    if let Some(higher::IfLet {{ let_pat: {}, let_expr: {}, if_then: {}, if_else: {}}}) = higher::IfLet::hir({})",
-                let_pat_, let_expr_pat, if_then_pat, else_pat, self.current
-            );
-
-            self.current = let_pat_;
-            self.visit_pat(let_pat);
-
-            self.current = let_expr_pat;
-            self.visit_expr(let_expr);
-
-            self.current = if_then_pat;
-            self.visit_expr(if_then);
-
-            if let Some(else_expr) = if_else {
-                self.current = else_pat;
-                self.visit_expr(else_expr);
-            }
-
-            return true;
-        }
-
-        if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) {
-            let cond_pat = self.next("cond");
-            let then_pat = self.next("then");
-            let else_pat = self.next("else_expr");
-
-            println!(
-                "    if let Some(higher::If {{ cond: {}, then: {}, r#else: {}}}) = higher::If::hir({})",
-                cond_pat, then_pat, else_pat, self.current
-            );
-
-            self.current = cond_pat;
-            self.visit_expr(cond);
-
-            self.current = then_pat;
-            self.visit_expr(then);
-
-            if let Some(else_expr) = r#else {
-                self.current = else_pat;
-                self.visit_expr(else_expr);
-            }
-
-            return true;
+            self.pat(let_pat);
+            self.expr(let_expr);
+            self.expr(if_then);
+            return;
         }
 
-        if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr) {
-            let pat_ = self.next("pat");
-            let arg_pat = self.next("arg");
-            let body_pat = self.next("body");
-
-            println!(
-                "    if let Some(higher::ForLoop {{ pat: {}, arg: {}, body: {}, ..}}) = higher::ForLoop::hir({})",
-                pat_, arg_pat, body_pat, self.current
+        if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) {
+            bind!(self, pat, arg, body);
+            out!(
+                "if let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \
+                = higher::ForLoop::hir({expr});"
             );
-
-            self.current = pat_;
-            self.visit_pat(pat);
-
-            self.current = arg_pat;
-            self.visit_expr(arg);
-
-            self.current = body_pat;
-            self.visit_expr(body);
-
-            return true;
-        }
-
-        false
-    }
-}
-
-struct PrintVisitor<'a, 'tcx> {
-    /// Fields are the current index that needs to be appended to pattern
-    /// binding names
-    ids: FxHashMap<&'static str, usize>,
-    /// the name that needs to be destructured
-    current: String,
-    cx: &'a LateContext<'tcx>,
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for PrintVisitor<'a, '_> {
-    type Map = Map<'tcx>;
-
-    #[allow(clippy::too_many_lines)]
-    fn visit_expr(&mut self, expr: &Expr<'_>) {
-        if self.check_higher(expr) {
+            self.pat(pat);
+            self.expr(arg);
+            self.expr(body);
             return;
         }
 
-        print!("    if let ExprKind::");
-        let current = format!("{}.kind", self.current);
+        let kind = |kind| out!("if let ExprKind::{kind} = {expr}.kind;");
+        macro_rules! kind {
+            ($($t:tt)*) => (kind(format_args!($($t)*)));
+        }
 
-        match expr.kind {
+        match expr.value.kind {
             ExprKind::Let(pat, expr, _) => {
-                let let_pat = self.next("pat");
-                let let_expr = self.next("expr");
-
-                println!("Let(ref {}, ref {}, _) = {};", let_pat, let_expr, current);
-
-                self.current = let_expr;
-                self.visit_expr(expr);
-
-                self.current = let_pat;
-                self.visit_pat(pat);
+                bind!(self, pat, expr);
+                kind!("Let({pat}, {expr}, _)");
+                self.pat(pat);
+                self.expr(expr);
             },
             ExprKind::Box(inner) => {
-                let inner_pat = self.next("inner");
-
-                println!("Box(ref {}) = {};", inner_pat, current);
-
-                self.current = inner_pat;
-                self.visit_expr(inner);
+                bind!(self, inner);
+                kind!("Box({inner})");
+                self.expr(inner);
             },
             ExprKind::Array(elements) => {
-                let elements_pat = self.next("elements");
-
-                println!("Array(ref {}) = {};", elements_pat, current);
-
-                println!("    if {}.len() == {};", elements_pat, elements.len());
-
-                for (i, element) in elements.iter().enumerate() {
-                    self.current = format!("{}[{}]", elements_pat, i);
-                    self.visit_expr(element);
-                }
+                bind!(self, elements);
+                kind!("Array({elements})");
+                self.slice(elements, |e| self.expr(e));
             },
             ExprKind::Call(func, args) => {
-                let func_pat = self.next("func");
-                let args_pat = self.next("args");
-
-                println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current);
-
-                self.current = func_pat;
-                self.visit_expr(func);
-
-                println!("    if {}.len() == {};", args_pat, args.len());
-
-                for (i, arg) in args.iter().enumerate() {
-                    self.current = format!("{}[{}]", args_pat, i);
-                    self.visit_expr(arg);
-                }
+                bind!(self, func, args);
+                kind!("Call({func}, {args})");
+                self.expr(func);
+                self.slice(args, |e| self.expr(e));
             },
             ExprKind::MethodCall(method_name, _, args, _) => {
-                let method_name_pat = self.next("method_name");
-                let args_pat = self.next("args");
-
-                println!(
-                    "MethodCall(ref {}, ref {}, _) = {};",
-                    method_name_pat, args_pat, current
-                );
-
-                println!(
-                    "    if {}.ident.name.as_str() == {};",
-                    method_name_pat,
-                    method_name.ident.name.as_str()
-                );
-
-                println!("    if {}.len() == {};", args_pat, args.len());
-
-                for (i, arg) in args.iter().enumerate() {
-                    self.current = format!("{}[{}]", args_pat, i);
-                    self.visit_expr(arg);
-                }
+                bind!(self, method_name, args);
+                kind!("MethodCall({method_name}, _, {args}, _)");
+                self.ident(field!(method_name.ident));
+                self.slice(args, |e| self.expr(e));
             },
             ExprKind::Tup(elements) => {
-                let elements_pat = self.next("elements");
-
-                println!("Tup(ref {}) = {};", elements_pat, current);
-
-                println!("    if {}.len() == {};", elements_pat, elements.len());
-
-                for (i, element) in elements.iter().enumerate() {
-                    self.current = format!("{}[{}]", elements_pat, i);
-                    self.visit_expr(element);
+                bind!(self, elements);
+                kind!("Tup({elements})");
+                self.slice(elements, |e| self.expr(e));
+            },
+            ExprKind::Binary(op, left, right) => {
+                bind!(self, op, left, right);
+                kind!("Binary({op}, {left}, {right})");
+                out!("if BinOpKind::{:?} == {op}.node;", op.value.node);
+                self.expr(left);
+                self.expr(right);
+            },
+            ExprKind::Unary(op, inner) => {
+                bind!(self, inner);
+                kind!("Unary(UnOp::{op:?}, {inner})");
+                self.expr(inner);
+            },
+            ExprKind::Lit(ref lit) => {
+                bind!(self, lit);
+                kind!("Lit(ref {lit})");
+                self.lit(lit);
+            },
+            ExprKind::Cast(expr, cast_ty) => {
+                bind!(self, expr, cast_ty);
+                kind!("Cast({expr}, {cast_ty})");
+                if let TyKind::Path(ref qpath) = cast_ty.value.kind {
+                    bind!(self, qpath);
+                    out!("if let TyKind::Path(ref {qpath}) = {cast_ty}.kind;");
+                    self.qpath(qpath);
                 }
-            },
-            ExprKind::Binary(ref op, left, right) => {
-                let op_pat = self.next("op");
-                let left_pat = self.next("left");
-                let right_pat = self.next("right");
-
-                println!(
-                    "Binary(ref {}, ref {}, ref {}) = {};",
-                    op_pat, left_pat, right_pat, current
-                );
-
-                println!("    if BinOpKind::{:?} == {}.node;", op.node, op_pat);
-
-                self.current = left_pat;
-                self.visit_expr(left);
-
-                self.current = right_pat;
-                self.visit_expr(right);
-            },
-            ExprKind::Unary(ref op, inner) => {
-                let inner_pat = self.next("inner");
-
-                println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current);
-
-                self.current = inner_pat;
-                self.visit_expr(inner);
-            },
-            ExprKind::Lit(ref lit) => self.print_lit_expr(lit, &current),
-            ExprKind::Cast(expr, ty) => {
-                let cast_pat = self.next("expr");
-                let cast_ty = self.next("cast_ty");
-                let qp_label = self.next("qp");
-
-                println!("Cast(ref {}, ref {}) = {};", cast_pat, cast_ty, current);
-
-                if let TyKind::Path(ref qp) = ty.kind {
-                    println!("    if let TyKind::Path(ref {}) = {}.kind;", qp_label, cast_ty);
-
-                    self.current = qp_label;
-                    self.print_qpath(qp);
-                }
-
-                self.current = cast_pat;
-                self.visit_expr(expr);
+                self.expr(expr);
             },
             ExprKind::Type(expr, _ty) => {
-                let cast_pat = self.next("expr");
-
-                println!("Type(ref {}, _) = {};", cast_pat, current);
-
-                self.current = cast_pat;
-                self.visit_expr(expr);
+                bind!(self, expr);
+                kind!("Type({expr}, _)");
+                self.expr(expr);
             },
             ExprKind::Loop(body, label, des, _) => {
-                let body_pat = self.next("body");
-                let label_pat = self.next("label");
-
-                println!(
-                    "Loop(ref {}, ref {}, LoopSource::{:?}) = {};",
-                    body_pat, label_pat, des, current
-                );
-
-                self.current = body_pat;
-                self.visit_block(body);
-
-                self.current = label_pat;
-                self.print_label(label);
-            },
-            ExprKind::If(_, _, _) => {}, // Covered by check_higher
-            ExprKind::Match(match_expr, arms, des) => self.print_match_expr(match_expr, arms, des, &current),
-            ExprKind::Closure(capture_clause, fn_decl, body_id, _, movability) => {
-                let capture_by = format!("CaptureBy::{:?}", capture_clause);
-
-                let movability = if let Some(movability) = movability {
-                    format!("Some(Movability::{:?})", movability)
-                } else {
-                    String::from("None")
-                };
+                bind!(self, body);
+                opt_bind!(self, label);
+                kind!("Loop({body}, {label}, LoopSource::{des:?}, _)");
+                self.block(body);
+                label.if_some(|l| self.ident(field!(l.ident)));
+            },
+            ExprKind::If(cond, then, else_expr) => {
+                bind!(self, cond, then);
+                opt_bind!(self, else_expr);
+                kind!("If({cond}, {then}, {else_expr})");
+                self.expr(cond);
+                self.expr(then);
+                else_expr.if_some(|e| self.expr(e));
+            },
+            ExprKind::Match(scrutinee, arms, des) => {
+                bind!(self, scrutinee, arms);
+                kind!("Match({scrutinee}, {arms}, MatchSource::{des:?})");
+                self.expr(scrutinee);
+                self.slice(arms, |arm| self.arm(arm));
+            },
+            ExprKind::Closure(capture_by, fn_decl, body_id, _, movability) => {
+                let movability = OptionPat::new(movability.map(|m| format!("Movability::{m:?}")));
 
                 let ret_ty = match fn_decl.output {
                     FnRetTy::DefaultReturn(_) => "FnRetTy::DefaultReturn(_)",
                     FnRetTy::Return(_) => "FnRetTy::Return(_ty)",
                 };
 
-                let fn_decl_pat = self.next("fn_decl");
-                let body_id_pat = self.next("body_id");
-
-                println!(
-                    "Closure({}, ref {}, ref {}, _, {}) = {}",
-                    capture_by, fn_decl_pat, body_id_pat, movability, current
-                );
-                println!("    if let {} = {}.output", ret_ty, fn_decl_pat);
-
-                let hir = self.cx.tcx.hir();
-                let body = hir.body(body_id);
-
-                let body_pat = self.next("body");
-
-                println!("    let {} = cx.tcx.hir().body({});", body_pat, body_id_pat);
-
-                self.current = format!("{}.value", body_pat);
-                self.visit_expr(&body.value);
+                bind!(self, fn_decl, body_id);
+                kind!("Closure(CaptureBy::{capture_by:?}, {fn_decl}, {body_id}, _, {movability})");
+                out!("if let {ret_ty} = {fn_decl}.output;");
+                self.body(body_id);
             },
             ExprKind::Yield(sub, source) => {
-                let sub_pat = self.next("sub");
-
-                println!("Yield(ref sub, YieldSource::{:?}) = {};", source, current);
-
-                self.current = sub_pat;
-                self.visit_expr(sub);
+                bind!(self, sub);
+                kind!("Yield(sub, YieldSource::{source:?})");
+                self.expr(sub);
             },
             ExprKind::Block(block, label) => {
-                let block_pat = self.next("block");
-                let label_pat = self.next("label");
-
-                println!("Block(ref {}, ref {}) = {};", block_pat, label_pat, current);
-
-                self.current = block_pat;
-                self.visit_block(block);
-
-                self.current = label_pat;
-                self.print_label(label);
+                bind!(self, block);
+                opt_bind!(self, label);
+                kind!("Block({block}, {label})");
+                self.block(block);
+                label.if_some(|l| self.ident(field!(l.ident)));
             },
             ExprKind::Assign(target, value, _) => {
-                let target_pat = self.next("target");
-                let value_pat = self.next("value");
-
-                println!(
-                    "Assign(ref {}, ref {}, ref _span) = {};",
-                    target_pat, value_pat, current
-                );
-
-                self.current = target_pat;
-                self.visit_expr(target);
-
-                self.current = value_pat;
-                self.visit_expr(value);
-            },
-            ExprKind::AssignOp(ref op, target, value) => {
-                let op_pat = self.next("op");
-                let target_pat = self.next("target");
-                let value_pat = self.next("value");
-
-                println!(
-                    "AssignOp(ref {}, ref {}, ref {}) = {};",
-                    op_pat, target_pat, value_pat, current
-                );
-
-                println!("    if BinOpKind::{:?} == {}.node;", op.node, op_pat);
-
-                self.current = target_pat;
-                self.visit_expr(target);
-
-                self.current = value_pat;
-                self.visit_expr(value);
-            },
-            ExprKind::Field(object, ref field_ident) => {
-                let obj_pat = self.next("object");
-                let field_name_pat = self.next("field_name");
-
-                println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current);
-                println!("    if {}.as_str() == {:?}", field_name_pat, field_ident.as_str());
-
-                self.current = obj_pat;
-                self.visit_expr(object);
+                bind!(self, target, value);
+                kind!("Assign({target}, {value}, _span)");
+                self.expr(target);
+                self.expr(value);
+            },
+            ExprKind::AssignOp(op, target, value) => {
+                bind!(self, op, target, value);
+                kind!("AssignOp({op}, {target}, {value})");
+                out!("if BinOpKind::{:?} == {op}.node;", op.value.node);
+                self.expr(target);
+                self.expr(value);
+            },
+            ExprKind::Field(object, field_name) => {
+                bind!(self, object, field_name);
+                kind!("Field({object}, {field_name})");
+                self.ident(field_name);
+                self.expr(object);
             },
             ExprKind::Index(object, index) => {
-                let object_pat = self.next("object");
-                let index_pat = self.next("index");
-
-                println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current);
-
-                self.current = object_pat;
-                self.visit_expr(object);
-
-                self.current = index_pat;
-                self.visit_expr(index);
+                bind!(self, object, index);
+                kind!("Index({object}, {index})");
+                self.expr(object);
+                self.expr(index);
             },
-            ExprKind::Path(ref path) => {
-                let path_pat = self.next("path");
-
-                println!("Path(ref {}) = {};", path_pat, current);
-
-                self.current = path_pat;
-                self.print_qpath(path);
+            ExprKind::Path(ref qpath) => {
+                bind!(self, qpath);
+                kind!("Path(ref {qpath})");
+                self.qpath(qpath);
             },
             ExprKind::AddrOf(kind, mutability, inner) => {
-                let inner_pat = self.next("inner");
-
-                println!(
-                    "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};",
-                    kind, mutability, inner_pat, current
-                );
-
-                self.current = inner_pat;
-                self.visit_expr(inner);
-            },
-            ExprKind::Break(ref destination, ref opt_value) => {
-                let destination_pat = self.next("destination");
-
-                if let Some(value) = *opt_value {
-                    let value_pat = self.next("value");
-
-                    println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current);
-
-                    self.current = value_pat;
-                    self.visit_expr(value);
-                } else {
-                    println!("Break(ref {}, None) = {};", destination_pat, current);
-                }
-
-                self.current = format!("{}.label", destination_pat);
-                self.print_label(destination.label);
-            },
-            ExprKind::Continue(ref destination) => {
-                let destination_pat = self.next("destination");
-                println!("Continue(ref {}) = {};", destination_pat, current);
-
-                self.current = format!("{}.label", destination_pat);
-                self.print_label(destination.label);
-            },
-            ExprKind::Ret(ref opt_value) => {
-                if let Some(value) = *opt_value {
-                    let value_pat = self.next("value");
-
-                    println!("Ret(Some(ref {})) = {};", value_pat, current);
-
-                    self.current = value_pat;
-                    self.visit_expr(value);
-                } else {
-                    println!("Ret(None) = {};", current);
-                }
+                bind!(self, inner);
+                kind!("AddrOf(BorrowKind::{kind:?}, Mutability::{mutability:?}, {inner})");
+                self.expr(inner);
+            },
+            ExprKind::Break(destination, value) => {
+                bind!(self, destination);
+                opt_bind!(self, value);
+                kind!("Break({destination}, {value})");
+                self.destination(destination);
+                value.if_some(|e| self.expr(e));
+            },
+            ExprKind::Continue(destination) => {
+                bind!(self, destination);
+                kind!("Continue({destination})");
+                self.destination(destination);
+            },
+            ExprKind::Ret(value) => {
+                opt_bind!(self, value);
+                kind!("Ret({value})");
+                value.if_some(|e| self.expr(e));
             },
             ExprKind::InlineAsm(_) => {
-                println!("InlineAsm(_) = {};", current);
-                println!("    // unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
+                kind!("InlineAsm(_)");
+                out!("// unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
             },
             ExprKind::LlvmInlineAsm(_) => {
-                println!("LlvmInlineAsm(_) = {};", current);
-                println!("    // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment");
-            },
-            ExprKind::Struct(path, fields, ref opt_base) => {
-                let path_pat = self.next("path");
-                let fields_pat = self.next("fields");
-
-                if let Some(base) = *opt_base {
-                    let base_pat = self.next("base");
-
-                    println!(
-                        "Struct(ref {}, ref {}, Some(ref {})) = {};",
-                        path_pat, fields_pat, base_pat, current
-                    );
-
-                    self.current = base_pat;
-                    self.visit_expr(base);
-                } else {
-                    println!("Struct(ref {}, ref {}, None) = {};", path_pat, fields_pat, current);
-                }
-
-                self.current = path_pat;
-                self.print_qpath(path);
-
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-
-                for (i, field) in fields.iter().enumerate() {
-                    println!(
-                        "    if {}[{}].ident.name.as_str() == {:?}",
-                        fields_pat,
-                        i,
-                        &*field.ident.name.as_str()
-                    );
-
-                    self.current = format!("{}[{}]", fields_pat, i);
-                    self.visit_expr(field.expr);
-                }
-            },
-            ExprKind::ConstBlock(_) => {
-                let value_pat = self.next("value");
-                println!("Const({}) = {}", value_pat, current);
-                self.current = value_pat;
-            },
+                kind!("LlvmInlineAsm(_)");
+                out!("// unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment");
+            },
+            ExprKind::Struct(qpath, fields, base) => {
+                bind!(self, qpath, fields);
+                opt_bind!(self, base);
+                kind!("Struct({qpath}, {fields}, {base})");
+                self.qpath(qpath);
+                self.slice(fields, |field| {
+                    self.ident(field!(field.ident));
+                    self.expr(field!(field.expr));
+                });
+                base.if_some(|e| self.expr(e));
+            },
+            ExprKind::ConstBlock(_) => kind!("ConstBlock(_)"),
             ExprKind::Repeat(value, length) => {
-                let value_pat = self.next("value");
-                let length_pat = self.next("length");
-
-                println!("Repeat(ref {}, ref {}) = {};", value_pat, length_pat, current);
-
-                self.current = value_pat;
-                self.visit_expr(value);
-
-                let hir = self.cx.tcx.hir();
-                let body = hir.body(length.body);
-
-                self.current = format!("{}.value", length_pat);
-                self.visit_expr(&body.value);
-            },
-            ExprKind::Err => {
-                println!("Err = {}", current);
+                bind!(self, value, length);
+                kind!("Repeat({value}, {length})");
+                self.expr(value);
+                self.body(field!(length.body));
             },
+            ExprKind::Err => kind!("Err"),
             ExprKind::DropTemps(expr) => {
-                let expr_pat = self.next("expr");
-
-                println!("DropTemps(ref {}) = {};", expr_pat, current);
-
-                self.current = expr_pat;
-                self.visit_expr(expr);
+                bind!(self, expr);
+                kind!("DropTemps({expr})");
+                self.expr(expr);
             },
         }
     }
 
-    fn visit_block(&mut self, block: &Block<'_>) {
-        println!("    if {}.stmts.len() == {};", self.current, block.stmts.len());
-
-        let block_name = self.current.clone();
-
-        for (i, stmt) in block.stmts.iter().enumerate() {
-            self.current = format!("{}.stmts[{}]", block_name, i);
-            self.visit_stmt(stmt);
-        }
-
-        if let Some(expr) = block.expr {
-            self.current = self.next("trailing_expr");
-            println!("    if let Some({}) = &{}.expr;", self.current, block_name);
-            self.visit_expr(expr);
-        } else {
-            println!("    if {}.expr.is_none();", block_name);
-        }
+    fn block(&self, block: &Binding<&hir::Block<'_>>) {
+        self.slice(field!(block.stmts), |stmt| self.stmt(stmt));
+        self.option(field!(block.expr), "trailing_expr", |expr| {
+            self.expr(expr);
+        });
     }
 
-    #[allow(clippy::too_many_lines)]
-    fn visit_pat(&mut self, pat: &Pat<'_>) {
-        print!("    if let PatKind::");
-        let current = format!("{}.kind", self.current);
-
-        match pat.kind {
-            PatKind::Wild => println!("Wild = {};", current),
-            PatKind::Binding(anno, .., ident, ref sub) => {
-                let anno_pat = &format!("BindingAnnotation::{:?}", anno);
-                let name_pat = self.next("name");
-
-                if let Some(sub) = *sub {
-                    let sub_pat = self.next("sub");
-
-                    println!(
-                        "Binding({}, _, {}, Some(ref {})) = {};",
-                        anno_pat, name_pat, sub_pat, current
-                    );
-
-                    self.current = sub_pat;
-                    self.visit_pat(sub);
-                } else {
-                    println!("Binding({}, _, {}, None) = {};", anno_pat, name_pat, current);
-                }
-
-                println!("    if {}.as_str() == \"{}\";", name_pat, ident.as_str());
-            },
-            PatKind::Struct(ref path, fields, ignore) => {
-                let path_pat = self.next("path");
-                let fields_pat = self.next("fields");
-                println!(
-                    "Struct(ref {}, ref {}, {}) = {};",
-                    path_pat, fields_pat, ignore, current
-                );
-
-                self.current = path_pat;
-                self.print_qpath(path);
-
-                println!("    if {}.len() == {};", fields_pat, fields.len());
+    fn body(&self, body_id: &Binding<hir::BodyId>) {
+        let expr = &self.cx.tcx.hir().body(body_id.value).value;
+        bind!(self, expr);
+        out!("let {expr} = &cx.tcx.hir().body({body_id}).value;");
+        self.expr(expr);
+    }
 
-                for (i, field) in fields.iter().enumerate() {
-                    println!(
-                        "    if {}[{}].ident.name.as_str() == {:?}",
-                        fields_pat,
-                        i,
-                        &*field.ident.name.as_str()
-                    );
+    fn pat(&self, pat: &Binding<&hir::Pat<'_>>) {
+        let kind = |kind| out!("if let PatKind::{kind} = {pat}.kind;");
+        macro_rules! kind {
+            ($($t:tt)*) => (kind(format_args!($($t)*)));
+        }
 
-                    self.current = format!("{}[{}]", fields_pat, i);
-                    self.visit_pat(field.pat);
-                }
+        match pat.value.kind {
+            PatKind::Wild => kind!("Wild"),
+            PatKind::Binding(anno, .., name, sub) => {
+                bind!(self, name);
+                opt_bind!(self, sub);
+                kind!("Binding(BindingAnnotation::{anno:?}, _, {name}, {sub})");
+                self.ident(name);
+                sub.if_some(|p| self.pat(p));
+            },
+            PatKind::Struct(ref qpath, fields, ignore) => {
+                bind!(self, qpath, fields);
+                kind!("Struct(ref {qpath}, {fields}, {ignore})");
+                self.qpath(qpath);
+                self.slice(fields, |field| {
+                    self.ident(field!(field.ident));
+                    self.pat(field!(field.pat));
+                });
             },
             PatKind::Or(fields) => {
-                let fields_pat = self.next("fields");
-                println!("Or(ref {}) = {};", fields_pat, current);
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-
-                for (i, field) in fields.iter().enumerate() {
-                    self.current = format!("{}[{}]", fields_pat, i);
-                    self.visit_pat(field);
-                }
+                bind!(self, fields);
+                kind!("Or({fields})");
+                self.slice(fields, |pat| self.pat(pat));
             },
-            PatKind::TupleStruct(ref path, fields, skip_pos) => {
-                let path_pat = self.next("path");
-                let fields_pat = self.next("fields");
-
-                println!(
-                    "TupleStruct(ref {}, ref {}, {:?}) = {};",
-                    path_pat, fields_pat, skip_pos, current
-                );
-
-                self.current = path_pat;
-                self.print_qpath(path);
-
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-
-                for (i, field) in fields.iter().enumerate() {
-                    self.current = format!("{}[{}]", fields_pat, i);
-                    self.visit_pat(field);
-                }
+            PatKind::TupleStruct(ref qpath, fields, skip_pos) => {
+                bind!(self, qpath, fields);
+                kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})");
+                self.qpath(qpath);
+                self.slice(fields, |pat| self.pat(pat));
             },
-            PatKind::Path(ref path) => {
-                let path_pat = self.next("path");
-                println!("Path(ref {}) = {};", path_pat, current);
-
-                self.current = path_pat;
-                self.print_qpath(path);
+            PatKind::Path(ref qpath) => {
+                bind!(self, qpath);
+                kind!("Path(ref {qpath})");
+                self.qpath(qpath);
             },
             PatKind::Tuple(fields, skip_pos) => {
-                let fields_pat = self.next("fields");
-                println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current);
-                println!("    if {}.len() == {};", fields_pat, fields.len());
-
-                for (i, field) in fields.iter().enumerate() {
-                    self.current = format!("{}[{}]", fields_pat, i);
-                    self.visit_pat(field);
-                }
+                bind!(self, fields);
+                kind!("Tuple({fields}, {skip_pos:?})");
+                self.slice(fields, |field| self.pat(field));
             },
             PatKind::Box(pat) => {
-                let pat_pat = self.next("pat");
-                println!("Box(ref {}) = {};", pat_pat, current);
-
-                self.current = pat_pat;
-                self.visit_pat(pat);
+                bind!(self, pat);
+                kind!("Box({pat})");
+                self.pat(pat);
             },
             PatKind::Ref(pat, muta) => {
-                let pat_pat = self.next("pat");
-                println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current);
-
-                self.current = pat_pat;
-                self.visit_pat(pat);
+                bind!(self, pat);
+                kind!("Ref({pat}, Mutability::{muta:?})");
+                self.pat(pat);
             },
             PatKind::Lit(lit_expr) => {
-                let lit_expr_pat = self.next("lit_expr");
-                println!("Lit(ref {}) = {}", lit_expr_pat, current);
-
-                self.current = lit_expr_pat;
-                self.visit_expr(lit_expr);
-            },
-            PatKind::Range(ref start, ref end, end_kind) => {
-                let start_pat = self.next("start");
-                let end_pat = self.next("end");
-
-                println!(
-                    "Range(ref {}, ref {}, RangeEnd::{:?}) = {};",
-                    start_pat, end_pat, end_kind, current
-                );
-
-                self.current = start_pat;
-                walk_list!(self, visit_expr, start);
-
-                self.current = end_pat;
-                walk_list!(self, visit_expr, end);
-            },
-            PatKind::Slice(start, ref middle, end) => {
-                let start_pat = self.next("start");
-                let end_pat = self.next("end");
-
-                if let Some(middle) = middle {
-                    let middle_pat = self.next("middle");
-                    println!(
-                        "Slice(ref {}, Some(ref {}), ref {}) = {};",
-                        start_pat, middle_pat, end_pat, current
-                    );
-                    self.current = middle_pat;
-                    self.visit_pat(middle);
-                } else {
-                    println!("Slice(ref {}, None, ref {}) = {};", start_pat, end_pat, current);
-                }
-
-                println!("    if {}.len() == {};", start_pat, start.len());
-
-                for (i, pat) in start.iter().enumerate() {
-                    self.current = format!("{}[{}]", start_pat, i);
-                    self.visit_pat(pat);
-                }
-
-                println!("    if {}.len() == {};", end_pat, end.len());
-
-                for (i, pat) in end.iter().enumerate() {
-                    self.current = format!("{}[{}]", end_pat, i);
-                    self.visit_pat(pat);
-                }
+                bind!(self, lit_expr);
+                kind!("Lit({lit_expr})");
+                self.expr(lit_expr);
+            },
+            PatKind::Range(start, end, end_kind) => {
+                opt_bind!(self, start, end);
+                kind!("Range({start}, {end}, RangeEnd::{end_kind:?})");
+                start.if_some(|e| self.expr(e));
+                end.if_some(|e| self.expr(e));
+            },
+            PatKind::Slice(start, middle, end) => {
+                bind!(self, start, end);
+                opt_bind!(self, middle);
+                kind!("Slice({start}, {middle}, {end})");
+                middle.if_some(|p| self.pat(p));
+                self.slice(start, |pat| self.pat(pat));
+                self.slice(end, |pat| self.pat(pat));
             },
         }
     }
 
-    fn visit_stmt(&mut self, s: &Stmt<'_>) {
-        print!("    if let StmtKind::");
-        let current = format!("{}.kind", self.current);
+    fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) {
+        let kind = |kind| out!("if let StmtKind::{kind} = {stmt}.kind;");
+        macro_rules! kind {
+            ($($t:tt)*) => (kind(format_args!($($t)*)));
+        }
 
-        match s.kind {
-            // A local (let) binding:
+        match stmt.value.kind {
             StmtKind::Local(local) => {
-                let local_pat = self.next("local");
-                println!("Local(ref {}) = {};", local_pat, current);
-
-                if let Some(init) = local.init {
-                    let init_pat = self.next("init");
-                    println!("    if let Some(ref {}) = {}.init;", init_pat, local_pat);
-
-                    self.current = init_pat;
-                    self.visit_expr(init);
-                }
-
-                self.current = format!("{}.pat", local_pat);
-                self.visit_pat(local.pat);
-            },
-            // An item binding:
-            StmtKind::Item(_) => {
-                println!("Item(item_id) = {};", current);
-            },
-
-            // Expr without trailing semi-colon (must have unit type):
+                bind!(self, local);
+                kind!("Local({local})");
+                self.option(field!(local.init), "init", |init| {
+                    self.expr(init);
+                });
+                self.pat(field!(local.pat));
+            },
+            StmtKind::Item(_) => kind!("Item(item_id)"),
             StmtKind::Expr(e) => {
-                let e_pat = self.next("e");
-                println!("Expr(ref {}, _) = {}", e_pat, current);
-
-                self.current = e_pat;
-                self.visit_expr(e);
+                bind!(self, e);
+                kind!("Expr({e})");
+                self.expr(e);
             },
-
-            // Expr with trailing semi-colon (may have any type):
             StmtKind::Semi(e) => {
-                let e_pat = self.next("e");
-                println!("Semi(ref {}, _) = {}", e_pat, current);
-
-                self.current = e_pat;
-                self.visit_expr(e);
+                bind!(self, e);
+                kind!("Semi({e})");
+                self.expr(e);
             },
         }
     }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
 }
 
 fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
@@ -1090,30 +697,29 @@ fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
     get_attr(cx.sess(), attrs, "author").count() > 0
 }
 
-fn print_path(path: &QPath<'_>, first: &mut bool) {
-    match *path {
-        QPath::Resolved(_, path) => {
-            for segment in path.segments {
-                if *first {
-                    *first = false;
-                } else {
-                    print!(", ");
-                }
-                print!("{:?}", segment.ident.as_str());
-            }
-        },
-        QPath::TypeRelative(ty, segment) => match ty.kind {
-            hir::TyKind::Path(ref inner_path) => {
-                print_path(inner_path, first);
-                if *first {
-                    *first = false;
-                } else {
-                    print!(", ");
+fn path_to_string(path: &QPath<'_>) -> String {
+    fn inner(s: &mut String, path: &QPath<'_>) {
+        match *path {
+            QPath::Resolved(_, path) => {
+                for (i, segment) in path.segments.iter().enumerate() {
+                    if i > 0 {
+                        *s += ", ";
+                    }
+                    write!(s, "{:?}", segment.ident.as_str()).unwrap();
                 }
-                print!("{:?}", segment.ident.as_str());
             },
-            ref other => print!("/* unimplemented: {:?}*/", other),
-        },
-        QPath::LangItem(..) => panic!("print_path: called for lang item qpath"),
+            QPath::TypeRelative(ty, segment) => match &ty.kind {
+                hir::TyKind::Path(inner_path) => {
+                    inner(s, inner_path);
+                    *s += ", ";
+                    write!(s, "{:?}", segment.ident.as_str()).unwrap();
+                },
+                other => write!(s, "/* unimplemented: {:?}*/", other).unwrap(),
+            },
+            QPath::LangItem(..) => panic!("path_to_string: called for lang item qpath"),
+        }
     }
+    let mut s = String::new();
+    inner(&mut s, path);
+    s
 }
diff --git a/tests/ui/author.stdout b/tests/ui/author.stdout
index 313597bf534..3125863036b 100644
--- a/tests/ui/author.stdout
+++ b/tests/ui/author.stdout
@@ -1,9 +1,9 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Cast(ref expr, ref cast_ty) = init.kind;
-    if let TyKind::Path(ref qp) = cast_ty.kind;
-    if match_qpath(qp, &["char"]);
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Cast(expr, cast_ty) = init.kind;
+    if let TyKind::Path(ref qpath) = cast_ty.kind;
+    if match_qpath(qpath, &["char"]);
     if let ExprKind::Lit(ref lit) = expr.kind;
     if let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout
index 31c7fced7f7..2fc4a7d1f7f 100644
--- a/tests/ui/author/blocks.stdout
+++ b/tests/ui/author/blocks.stdout
@@ -1,62 +1,62 @@
 if_chain! {
-    if let ExprKind::Block(ref block, ref label) = expr.kind;
+    if let ExprKind::Block(block, None) = expr.kind;
     if block.stmts.len() == 3;
-    if let StmtKind::Local(ref local) = block.stmts[0].kind;
-    if let Some(ref init) = local.init;
+    if let StmtKind::Local(local) = block.stmts[0].kind;
+    if let Some(init) = local.init;
     if let ExprKind::Lit(ref lit) = init.kind;
     if let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
     if name.as_str() == "x";
-    if let StmtKind::Local(ref local1) = block.stmts[1].kind;
-    if let Some(ref init1) = local1.init;
+    if let StmtKind::Local(local1) = block.stmts[1].kind;
+    if let Some(init1) = local1.init;
     if let ExprKind::Lit(ref lit1) = init1.kind;
     if let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local1.pat.kind;
     if name1.as_str() == "_t";
-    if let StmtKind::Semi(ref e, _) = block.stmts[2].kind
-    if let ExprKind::Unary(UnOp::Neg, ref inner) = e.kind;
-    if let ExprKind::Path(ref path) = inner.kind;
-    if match_qpath(path, &["x"]);
+    if let StmtKind::Semi(e) = block.stmts[2].kind;
+    if let ExprKind::Unary(UnOp::Neg, inner) = e.kind;
+    if let ExprKind::Path(ref qpath) = inner.kind;
+    if match_qpath(qpath, &["x"]);
     if block.expr.is_none();
     then {
         // report your lint here
     }
 }
 if_chain! {
-    if let ExprKind::Block(ref block, ref label) = expr.kind;
+    if let ExprKind::Block(block, None) = expr.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Local(ref local) = block.stmts[0].kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Call(ref func, ref args) = init.kind;
-    if let ExprKind::Path(ref path) = func.kind;
-    if match_qpath(path, &["String", "new"]);
-    if args.len() == 0;
+    if let StmtKind::Local(local) = block.stmts[0].kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Call(func, args) = init.kind;
+    if let ExprKind::Path(ref qpath) = func.kind;
+    if match_qpath(qpath, &["String", "new"]);
+    if args.is_empty();
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
     if name.as_str() == "expr";
-    if let Some(trailing_expr) = &block.expr;
-    if let ExprKind::Call(ref func1, ref args1) = trailing_expr.kind;
-    if let ExprKind::Path(ref path1) = func1.kind;
-    if match_qpath(path1, &["drop"]);
+    if let Some(trailing_expr) = block.expr;
+    if let ExprKind::Call(func1, args1) = trailing_expr.kind;
+    if let ExprKind::Path(ref qpath1) = func1.kind;
+    if match_qpath(qpath1, &["drop"]);
     if args1.len() == 1;
-    if let ExprKind::Path(ref path2) = args1[0].kind;
-    if match_qpath(path2, &["expr"]);
+    if let ExprKind::Path(ref qpath2) = args1[0].kind;
+    if match_qpath(qpath2, &["expr"]);
     then {
         // report your lint here
     }
 }
 if_chain! {
-    if let ExprKind::Closure(CaptureBy::Value, ref fn_decl, ref body_id, _, None) = expr.kind
-    if let FnRetTy::DefaultReturn(_) = fn_decl.output
-    let body = cx.tcx.hir().body(body_id);
-    if let ExprKind::Call(ref func, ref args) = body.value.kind;
-    if let ExprKind::Path(ref path) = func.kind;
-    if matches!(path, QPath::LangItem(LangItem::FromGenerator, _));
+    if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind;
+    if let FnRetTy::DefaultReturn(_) = fn_decl.output;
+    let expr1 = &cx.tcx.hir().body(body_id).value;
+    if let ExprKind::Call(func, args) = expr1.kind;
+    if let ExprKind::Path(ref qpath) = func.kind;
+    if matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _));
     if args.len() == 1;
-    if let ExprKind::Closure(CaptureBy::Value, ref fn_decl1, ref body_id1, _, Some(Movability::Static)) = args[0].kind
-    if let FnRetTy::DefaultReturn(_) = fn_decl1.output
-    let body1 = cx.tcx.hir().body(body_id1);
-    if let ExprKind::Block(ref block, ref label) = body1.value.kind;
-    if block.stmts.len() == 0;
+    if let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind;
+    if let FnRetTy::DefaultReturn(_) = fn_decl1.output;
+    let expr2 = &cx.tcx.hir().body(body_id1).value;
+    if let ExprKind::Block(block, None) = expr2.kind;
+    if block.stmts.is_empty();
     if block.expr.is_none();
     then {
         // report your lint here
diff --git a/tests/ui/author/call.stdout b/tests/ui/author/call.stdout
index 07958dbc356..266312d63e5 100644
--- a/tests/ui/author/call.stdout
+++ b/tests/ui/author/call.stdout
@@ -1,9 +1,9 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Call(ref func, ref args) = init.kind;
-    if let ExprKind::Path(ref path) = func.kind;
-    if match_qpath(path, &["{{root}}", "std", "cmp", "min"]);
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Call(func, args) = init.kind;
+    if let ExprKind::Path(ref qpath) = func.kind;
+    if match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]);
     if args.len() == 2;
     if let ExprKind::Lit(ref lit) = args[0].kind;
     if let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node;
diff --git a/tests/ui/author/if.stdout b/tests/ui/author/if.stdout
index 7e72a548011..75ff3faf29a 100644
--- a/tests/ui/author/if.stdout
+++ b/tests/ui/author/if.stdout
@@ -1,23 +1,24 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let Some(higher::If { cond: cond, then: then, r#else: else_expr}) = higher::If::hir(init)
-    if let ExprKind::Lit(ref lit) = cond.kind;
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::If(cond, then, Some(else_expr)) = init.kind;
+    if let ExprKind::DropTemps(expr) = cond.kind;
+    if let ExprKind::Lit(ref lit) = expr.kind;
     if let LitKind::Bool(true) = lit.node;
-    if let ExprKind::Block(ref block, ref label) = then.kind;
+    if let ExprKind::Block(block, None) = then.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Semi(ref e, _) = block.stmts[0].kind
-    if let ExprKind::Binary(ref op, ref left, ref right) = e.kind;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Binary(op, left, right) = e.kind;
     if BinOpKind::Eq == op.node;
     if let ExprKind::Lit(ref lit1) = left.kind;
     if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node;
     if let ExprKind::Lit(ref lit2) = right.kind;
     if let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node;
     if block.expr.is_none();
-    if let ExprKind::Block(ref block1, ref label1) = else_expr.kind;
+    if let ExprKind::Block(block1, None) = else_expr.kind;
     if block1.stmts.len() == 1;
-    if let StmtKind::Semi(ref e1, _) = block1.stmts[0].kind
-    if let ExprKind::Binary(ref op1, ref left1, ref right1) = e1.kind;
+    if let StmtKind::Semi(e1) = block1.stmts[0].kind;
+    if let ExprKind::Binary(op1, left1, right1) = e1.kind;
     if BinOpKind::Eq == op1.node;
     if let ExprKind::Lit(ref lit3) = left1.kind;
     if let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node;
@@ -30,17 +31,18 @@ if_chain! {
     }
 }
 if_chain! {
-    if let Some(higher::IfLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then, if_else: else_expr}) = higher::IfLet::hir(expr)
-    if let PatKind::Lit(ref lit_expr) = let_pat.kind
+    if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind;
+    if let ExprKind::Let(pat, expr1, _) = cond.kind;
+    if let PatKind::Lit(lit_expr) = pat.kind;
     if let ExprKind::Lit(ref lit) = lit_expr.kind;
     if let LitKind::Bool(true) = lit.node;
-    if let ExprKind::Path(ref path) = let_expr.kind;
-    if match_qpath(path, &["a"]);
-    if let ExprKind::Block(ref block, ref label) = if_then.kind;
-    if block.stmts.len() == 0;
+    if let ExprKind::Path(ref qpath) = expr1.kind;
+    if match_qpath(qpath, &["a"]);
+    if let ExprKind::Block(block, None) = then.kind;
+    if block.stmts.is_empty();
     if block.expr.is_none();
-    if let ExprKind::Block(ref block1, ref label1) = else_expr.kind;
-    if block1.stmts.len() == 0;
+    if let ExprKind::Block(block1, None) = else_expr.kind;
+    if block1.stmts.is_empty();
     if block1.expr.is_none();
     then {
         // report your lint here
diff --git a/tests/ui/author/issue_3849.stdout b/tests/ui/author/issue_3849.stdout
index 65f93f3cdc0..bce4bc70273 100644
--- a/tests/ui/author/issue_3849.stdout
+++ b/tests/ui/author/issue_3849.stdout
@@ -1,12 +1,12 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Call(ref func, ref args) = init.kind;
-    if let ExprKind::Path(ref path) = func.kind;
-    if match_qpath(path, &["std", "mem", "transmute"]);
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Call(func, args) = init.kind;
+    if let ExprKind::Path(ref qpath) = func.kind;
+    if match_qpath(qpath, &["std", "mem", "transmute"]);
     if args.len() == 1;
-    if let ExprKind::Path(ref path1) = args[0].kind;
-    if match_qpath(path1, &["ZPTR"]);
+    if let ExprKind::Path(ref qpath1) = args[0].kind;
+    if match_qpath(qpath1, &["ZPTR"]);
     if let PatKind::Wild = local.pat.kind;
     then {
         // report your lint here
diff --git a/tests/ui/author/loop.stdout b/tests/ui/author/loop.stdout
index fa146f03495..5a1f731ac4a 100644
--- a/tests/ui/author/loop.stdout
+++ b/tests/ui/author/loop.stdout
@@ -1,23 +1,23 @@
 if_chain! {
-    if let ExprKind::DropTemps(ref expr) = expr.kind;
-    if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, ..}) = higher::ForLoop::hir(expr)
+    if let ExprKind::DropTemps(expr1) = expr.kind;
+    if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr1);
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = pat.kind;
     if name.as_str() == "y";
-    if let ExprKind::Struct(ref path, ref fields, None) = arg.kind;
-    if matches!(path, QPath::LangItem(LangItem::Range, _));
+    if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+    if matches!(qpath, QPath::LangItem(LangItem::Range, _));
     if fields.len() == 2;
-    if fields[0].ident.name.as_str() == "start"
-    if let ExprKind::Lit(ref lit) = fields[0].kind;
+    if fields[0].ident.as_str() == "start";
+    if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
     if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
-    if fields[1].ident.name.as_str() == "end"
-    if let ExprKind::Lit(ref lit1) = fields[1].kind;
+    if fields[1].ident.as_str() == "end";
+    if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
     if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
-    if let ExprKind::Block(ref block, ref label) = body.kind;
+    if let ExprKind::Block(block, None) = body.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Local(ref local) = block.stmts[0].kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Path(ref path1) = init.kind;
-    if match_qpath(path1, &["y"]);
+    if let StmtKind::Local(local) = block.stmts[0].kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Path(ref qpath1) = init.kind;
+    if match_qpath(qpath1, &["y"]);
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind;
     if name1.as_str() == "z";
     if block.expr.is_none();
@@ -26,85 +26,89 @@ if_chain! {
     }
 }
 if_chain! {
-    if let ExprKind::DropTemps(ref expr) = expr.kind;
-    if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, ..}) = higher::ForLoop::hir(expr)
+    if let ExprKind::DropTemps(expr1) = expr.kind;
+    if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr1);
     if let PatKind::Wild = pat.kind;
-    if let ExprKind::Struct(ref path, ref fields, None) = arg.kind;
-    if matches!(path, QPath::LangItem(LangItem::Range, _));
+    if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+    if matches!(qpath, QPath::LangItem(LangItem::Range, _));
     if fields.len() == 2;
-    if fields[0].ident.name.as_str() == "start"
-    if let ExprKind::Lit(ref lit) = fields[0].kind;
+    if fields[0].ident.as_str() == "start";
+    if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
     if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
-    if fields[1].ident.name.as_str() == "end"
-    if let ExprKind::Lit(ref lit1) = fields[1].kind;
+    if fields[1].ident.as_str() == "end";
+    if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
     if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
-    if let ExprKind::Block(ref block, ref label) = body.kind;
+    if let ExprKind::Block(block, None) = body.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Semi(ref e, _) = block.stmts[0].kind
-    if let ExprKind::Break(ref destination, None) = e.kind;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if destination.label.is_none();
     if block.expr.is_none();
     then {
         // report your lint here
     }
 }
 if_chain! {
-    if let ExprKind::DropTemps(ref expr) = expr.kind;
-    if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, ..}) = higher::ForLoop::hir(expr)
+    if let ExprKind::DropTemps(expr1) = expr.kind;
+    if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr1);
     if let PatKind::Wild = pat.kind;
-    if let ExprKind::Struct(ref path, ref fields, None) = arg.kind;
-    if matches!(path, QPath::LangItem(LangItem::Range, _));
+    if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+    if matches!(qpath, QPath::LangItem(LangItem::Range, _));
     if fields.len() == 2;
-    if fields[0].ident.name.as_str() == "start"
-    if let ExprKind::Lit(ref lit) = fields[0].kind;
+    if fields[0].ident.as_str() == "start";
+    if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
     if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
-    if fields[1].ident.name.as_str() == "end"
-    if let ExprKind::Lit(ref lit1) = fields[1].kind;
+    if fields[1].ident.as_str() == "end";
+    if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
     if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
-    if let ExprKind::Block(ref block, ref label) = body.kind;
+    if let ExprKind::Block(block, None) = body.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Semi(ref e, _) = block.stmts[0].kind
-    if let ExprKind::Break(ref destination, None) = e.kind;
-    if let Some(ref label1) = destination.label
-    if label_name.ident.name.as_str() == "'label";
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if let Some(label) = destination.label;
+    if label.ident.as_str() == "'label";
     if block.expr.is_none();
     then {
         // report your lint here
     }
 }
 if_chain! {
-    if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr)
-    if let ExprKind::Path(ref path) = condition.kind;
-    if match_qpath(path, &["a"]);
-    if let ExprKind::Block(ref block, ref label) = body.kind;
+    if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr);
+    if let ExprKind::Path(ref qpath) = condition.kind;
+    if match_qpath(qpath, &["a"]);
+    if let ExprKind::Block(block, None) = body.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Semi(ref e, _) = block.stmts[0].kind
-    if let ExprKind::Break(ref destination, None) = e.kind;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if destination.label.is_none();
     if block.expr.is_none();
     then {
         // report your lint here
     }
 }
 if_chain! {
-    if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr)
-    if let PatKind::Lit(ref lit_expr) = let_pat.kind
+    if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr);
+    if let PatKind::Lit(lit_expr) = let_pat.kind;
     if let ExprKind::Lit(ref lit) = lit_expr.kind;
     if let LitKind::Bool(true) = lit.node;
-    if let ExprKind::Path(ref path) = let_expr.kind;
-    if match_qpath(path, &["a"]);
-    if let ExprKind::Block(ref block, ref label) = if_then.kind;
+    if let ExprKind::Path(ref qpath) = let_expr.kind;
+    if match_qpath(qpath, &["a"]);
+    if let ExprKind::Block(block, None) = if_then.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Semi(ref e, _) = block.stmts[0].kind
-    if let ExprKind::Break(ref destination, None) = e.kind;
+    if let StmtKind::Semi(e) = block.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if destination.label.is_none();
     if block.expr.is_none();
     then {
         // report your lint here
     }
 }
 if_chain! {
-    if let ExprKind::Loop(ref body, ref label, LoopSource::Loop) = expr.kind;
+    if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind;
     if body.stmts.len() == 1;
-    if let StmtKind::Semi(ref e, _) = body.stmts[0].kind
-    if let ExprKind::Break(ref destination, None) = e.kind;
+    if let StmtKind::Semi(e) = body.stmts[0].kind;
+    if let ExprKind::Break(destination, None) = e.kind;
+    if destination.label.is_none();
     if body.expr.is_none();
     then {
         // report your lint here
diff --git a/tests/ui/author/matches.stdout b/tests/ui/author/matches.stdout
index 3be61fe3772..38444a0094c 100644
--- a/tests/ui/author/matches.stdout
+++ b/tests/ui/author/matches.stdout
@@ -1,32 +1,35 @@
 if_chain! {
-    if let StmtKind::Local(ref local) = stmt.kind;
-    if let Some(ref init) = local.init;
-    if let ExprKind::Match(ref expr, ref arms, MatchSource::Normal) = init.kind;
-    if let ExprKind::Lit(ref lit) = expr.kind;
+    if let StmtKind::Local(local) = stmt.kind;
+    if let Some(init) = local.init;
+    if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind;
+    if let ExprKind::Lit(ref lit) = scrutinee.kind;
     if let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node;
     if arms.len() == 3;
-    if let ExprKind::Lit(ref lit1) = arms[0].body.kind;
-    if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node;
-    if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind
-    if let ExprKind::Lit(ref lit2) = lit_expr.kind;
-    if let LitKind::Int(16, LitIntType::Unsuffixed) = lit2.node;
-    if let ExprKind::Block(ref block, ref label) = arms[1].body.kind;
+    if let PatKind::Lit(lit_expr) = arms[0].pat.kind;
+    if let ExprKind::Lit(ref lit1) = lit_expr.kind;
+    if let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node;
+    if arms[0].guard.is_none();
+    if let ExprKind::Lit(ref lit2) = arms[0].body.kind;
+    if let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node;
+    if let PatKind::Lit(lit_expr1) = arms[1].pat.kind;
+    if let ExprKind::Lit(ref lit3) = lit_expr1.kind;
+    if let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node;
+    if arms[1].guard.is_none();
+    if let ExprKind::Block(block, None) = arms[1].body.kind;
     if block.stmts.len() == 1;
-    if let StmtKind::Local(ref local1) = block.stmts[0].kind;
-    if let Some(ref init1) = local1.init;
-    if let ExprKind::Lit(ref lit3) = init1.kind;
-    if let LitKind::Int(3, LitIntType::Unsuffixed) = lit3.node;
+    if let StmtKind::Local(local1) = block.stmts[0].kind;
+    if let Some(init1) = local1.init;
+    if let ExprKind::Lit(ref lit4) = init1.kind;
+    if let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local1.pat.kind;
     if name.as_str() == "x";
-    if let Some(trailing_expr) = &block.expr;
-    if let ExprKind::Path(ref path) = trailing_expr.kind;
-    if match_qpath(path, &["x"]);
-    if let PatKind::Lit(ref lit_expr1) = arms[1].pat.kind
-    if let ExprKind::Lit(ref lit4) = lit_expr1.kind;
-    if let LitKind::Int(17, LitIntType::Unsuffixed) = lit4.node;
+    if let Some(trailing_expr) = block.expr;
+    if let ExprKind::Path(ref qpath) = trailing_expr.kind;
+    if match_qpath(qpath, &["x"]);
+    if let PatKind::Wild = arms[2].pat.kind;
+    if arms[2].guard.is_none();
     if let ExprKind::Lit(ref lit5) = arms[2].body.kind;
     if let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node;
-    if let PatKind::Wild = arms[2].pat.kind;
     if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind;
     if name1.as_str() == "a";
     then {
diff --git a/tests/ui/author/repeat.stdout b/tests/ui/author/repeat.stdout
index 9ff376373e6..f16350e4b5e 100644
--- a/tests/ui/author/repeat.stdout
+++ b/tests/ui/author/repeat.stdout
@@ -1,8 +1,9 @@
 if_chain! {
-    if let ExprKind::Repeat(ref value, ref length) = expr.kind;
+    if let ExprKind::Repeat(value, length) = expr.kind;
     if let ExprKind::Lit(ref lit) = value.kind;
     if let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node;
-    if let ExprKind::Lit(ref lit1) = length.value.kind;
+    let expr1 = &cx.tcx.hir().body(length.body).value;
+    if let ExprKind::Lit(ref lit1) = expr1.kind;
     if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node;
     then {
         // report your lint here
diff --git a/tests/ui/author/struct.stdout b/tests/ui/author/struct.stdout
index 98055d73779..ded5abd8d33 100644
--- a/tests/ui/author/struct.stdout
+++ b/tests/ui/author/struct.stdout
@@ -1,19 +1,20 @@
 if_chain! {
-    if let ExprKind::Struct(ref path, ref fields, None) = expr.kind;
-    if match_qpath(path, &["Test"]);
+    if let ExprKind::Struct(qpath, fields, None) = expr.kind;
+    if match_qpath(qpath, &["Test"]);
     if fields.len() == 1;
-    if fields[0].ident.name.as_str() == "field"
-    if let Some(higher::If { cond: cond, then: then, r#else: else_expr}) = higher::If::hir(fields[0])
-    if let ExprKind::Lit(ref lit) = cond.kind;
+    if fields[0].ident.as_str() == "field";
+    if let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind;
+    if let ExprKind::DropTemps(expr1) = cond.kind;
+    if let ExprKind::Lit(ref lit) = expr1.kind;
     if let LitKind::Bool(true) = lit.node;
-    if let ExprKind::Block(ref block, ref label) = then.kind;
-    if block.stmts.len() == 0;
-    if let Some(trailing_expr) = &block.expr;
+    if let ExprKind::Block(block, None) = then.kind;
+    if block.stmts.is_empty();
+    if let Some(trailing_expr) = block.expr;
     if let ExprKind::Lit(ref lit1) = trailing_expr.kind;
     if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node;
-    if let ExprKind::Block(ref block1, ref label1) = else_expr.kind;
-    if block1.stmts.len() == 0;
-    if let Some(trailing_expr1) = &block1.expr;
+    if let ExprKind::Block(block1, None) = else_expr.kind;
+    if block1.stmts.is_empty();
+    if let Some(trailing_expr1) = block1.expr;
     if let ExprKind::Lit(ref lit2) = trailing_expr1.kind;
     if let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node;
     then {
@@ -21,40 +22,42 @@ if_chain! {
     }
 }
 if_chain! {
-    if let PatKind::Struct(ref path, ref fields, false) = arm.kind;
-    if match_qpath(path, &["Test"]);
+    if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind;
+    if match_qpath(qpath, &["Test"]);
     if fields.len() == 1;
-    if fields[0].ident.name.as_str() == "field"
-    if let PatKind::Lit(ref lit_expr) = fields[0].kind
+    if fields[0].ident.as_str() == "field";
+    if let PatKind::Lit(lit_expr) = fields[0].pat.kind;
     if let ExprKind::Lit(ref lit) = lit_expr.kind;
     if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
-    if let ExprKind::Block(ref block, ref label) = lit_expr.kind;
-    if block.stmts.len() == 0;
+    if arm.guard.is_none();
+    if let ExprKind::Block(block, None) = arm.body.kind;
+    if block.stmts.is_empty();
     if block.expr.is_none();
     then {
         // report your lint here
     }
 }
 if_chain! {
-    if let PatKind::TupleStruct(ref path, ref fields, None) = arm.kind;
-    if match_qpath(path, &["TestTuple"]);
+    if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind;
+    if match_qpath(qpath, &["TestTuple"]);
     if fields.len() == 1;
-    if let PatKind::Lit(ref lit_expr) = fields[0].kind
+    if let PatKind::Lit(lit_expr) = fields[0].kind;
     if let ExprKind::Lit(ref lit) = lit_expr.kind;
     if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
-    if let ExprKind::Block(ref block, ref label) = lit_expr.kind;
-    if block.stmts.len() == 0;
+    if arm.guard.is_none();
+    if let ExprKind::Block(block, None) = arm.body.kind;
+    if block.stmts.is_empty();
     if block.expr.is_none();
     then {
         // report your lint here
     }
 }
 if_chain! {
-    if let ExprKind::MethodCall(ref method_name, ref args, _) = expr.kind;
-    if method_name.ident.name.as_str() == test;
+    if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind;
+    if method_name.ident.as_str() == "test";
     if args.len() == 1;
-    if let ExprKind::Path(ref path) = args[0].kind;
-    if match_qpath(path, &["test_method_call"]);
+    if let ExprKind::Path(ref qpath) = args[0].kind;
+    if match_qpath(qpath, &["test_method_call"]);
     then {
         // report your lint here
     }