about summary refs log tree commit diff
path: root/src/tools/clippy/clippy_lints
diff options
context:
space:
mode:
authorflip1995 <philipp.krones@embecosm.com>2021-02-11 15:04:38 +0100
committerflip1995 <philipp.krones@embecosm.com>2021-02-11 15:04:38 +0100
commitd2e2f64ef7bd98c8ce2c9ce77db27ea1617892ea (patch)
treea2147c3fa0e7a643165c22eb8a53a680d8da736a /src/tools/clippy/clippy_lints
parent2918062d1d94b65dfd53d265e957d86fcb8bfdbd (diff)
parent70c0f90453701e7d6d9b99aaa1fc6a765937b736 (diff)
downloadrust-d2e2f64ef7bd98c8ce2c9ce77db27ea1617892ea.tar.gz
rust-d2e2f64ef7bd98c8ce2c9ce77db27ea1617892ea.zip
Merge commit '70c0f90453701e7d6d9b99aaa1fc6a765937b736' into clippyup
Diffstat (limited to 'src/tools/clippy/clippy_lints')
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs75
-rw-r--r--src/tools/clippy/clippy_lints/src/collapsible_match.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_method.rs52
-rw-r--r--src/tools/clippy/clippy_lints/src/eval_order_dependence.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/functions.rs35
-rw-r--r--src/tools/clippy/clippy_lints/src/let_if_seq.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/let_underscore.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/loops.rs285
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_use.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_ok_or.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/matches.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs52
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs141
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_mut.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_question_mark.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/ranges.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/regex.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs66
-rw-r--r--src/tools/clippy/clippy_lints/src/to_string_in_display.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_self.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/conf.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/higher.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs57
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/usage.rs34
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/visitors.rs41
-rw-r--r--src/tools/clippy/clippy_lints/src/vec.rs2
31 files changed, 719 insertions, 464 deletions
diff --git a/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs b/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs
index 0d294761af5..cc2869ab495 100644
--- a/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo_common_metadata.rs
@@ -5,7 +5,7 @@ use std::path::PathBuf;
 use crate::utils::{run_lints, span_lint};
 use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::DUMMY_SP;
 
 declare_clippy_lint! {
@@ -51,6 +51,21 @@ declare_clippy_lint! {
     "common metadata is defined in `Cargo.toml`"
 }
 
+#[derive(Copy, Clone, Debug)]
+pub struct CargoCommonMetadata {
+    ignore_publish: bool,
+}
+
+impl CargoCommonMetadata {
+    pub fn new(ignore_publish: bool) -> Self {
+        Self { ignore_publish }
+    }
+}
+
+impl_lint_pass!(CargoCommonMetadata => [
+    CARGO_COMMON_METADATA
+]);
+
 fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, field: &str) {
     let message = format!("package `{}` is missing `{}` metadata", package.name, field);
     span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message);
@@ -69,8 +84,6 @@ fn is_empty_vec(value: &[String]) -> bool {
     value.iter().all(String::is_empty)
 }
 
-declare_lint_pass!(CargoCommonMetadata => [CARGO_COMMON_METADATA]);
-
 impl LateLintPass<'_> for CargoCommonMetadata {
     fn check_crate(&mut self, cx: &LateContext<'_>, _: &Crate<'_>) {
         if !run_lints(cx, &[CARGO_COMMON_METADATA], CRATE_HIR_ID) {
@@ -80,32 +93,36 @@ impl LateLintPass<'_> for CargoCommonMetadata {
         let metadata = unwrap_cargo_metadata!(cx, CARGO_COMMON_METADATA, false);
 
         for package in metadata.packages {
-            if is_empty_vec(&package.authors) {
-                missing_warning(cx, &package, "package.authors");
-            }
-
-            if is_empty_str(&package.description) {
-                missing_warning(cx, &package, "package.description");
-            }
-
-            if is_empty_str(&package.license) && is_empty_path(&package.license_file) {
-                missing_warning(cx, &package, "either package.license or package.license_file");
-            }
-
-            if is_empty_str(&package.repository) {
-                missing_warning(cx, &package, "package.repository");
-            }
-
-            if is_empty_path(&package.readme) {
-                missing_warning(cx, &package, "package.readme");
-            }
-
-            if is_empty_vec(&package.keywords) {
-                missing_warning(cx, &package, "package.keywords");
-            }
-
-            if is_empty_vec(&package.categories) {
-                missing_warning(cx, &package, "package.categories");
+            // only run the lint if publish is `None` (`publish = true` or skipped entirely)
+            // or if the vector isn't empty (`publish = ["something"]`)
+            if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || self.ignore_publish {
+                if is_empty_vec(&package.authors) {
+                    missing_warning(cx, &package, "package.authors");
+                }
+
+                if is_empty_str(&package.description) {
+                    missing_warning(cx, &package, "package.description");
+                }
+
+                if is_empty_str(&package.license) && is_empty_path(&package.license_file) {
+                    missing_warning(cx, &package, "either package.license or package.license_file");
+                }
+
+                if is_empty_str(&package.repository) {
+                    missing_warning(cx, &package, "package.repository");
+                }
+
+                if is_empty_path(&package.readme) {
+                    missing_warning(cx, &package, "package.readme");
+                }
+
+                if is_empty_vec(&package.keywords) {
+                    missing_warning(cx, &package, "package.keywords");
+                }
+
+                if is_empty_vec(&package.categories) {
+                    missing_warning(cx, &package, "package.categories");
+                }
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/collapsible_match.rs
index b83aae0e571..67282cb7900 100644
--- a/src/tools/clippy/clippy_lints/src/collapsible_match.rs
+++ b/src/tools/clippy/clippy_lints/src/collapsible_match.rs
@@ -1,10 +1,10 @@
 use crate::utils::visitors::LocalUsedVisitor;
-use crate::utils::{span_lint_and_then, SpanlessEq};
+use crate::utils::{path_to_local, span_lint_and_then, SpanlessEq};
 use if_chain::if_chain;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{DefIdTree, TyCtxt};
+use rustc_middle::ty::{DefIdTree, TyCtxt, TypeckResults};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::{MultiSpan, Span};
 
@@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
     }
 }
 
-fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) {
+fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext<'tcx>) {
     if_chain! {
         let expr = strip_singleton_blocks(arm.body);
         if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind;
@@ -72,7 +72,7 @@ fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) {
         if arms_inner.iter().all(|arm| arm.guard.is_none());
         // match expression must be a local binding
         // match <local> { .. }
-        if let Some(binding_id) = addr_adjusted_binding(expr_in, cx);
+        if let Some(binding_id) = path_to_local(strip_ref_operators(expr_in, cx.typeck_results()));
         // one of the branches must be "wild-like"
         if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(arm_inner, cx.tcx));
         let (wild_inner_arm, non_wild_inner_arm) =
@@ -84,14 +84,13 @@ fn check_arm(arm: &Arm<'_>, wild_outer_arm: &Arm<'_>, cx: &LateContext<'_>) {
         // the "wild-like" branches must be equal
         if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body);
         // the binding must not be used in the if guard
+        let mut used_visitor = LocalUsedVisitor::new(cx, binding_id);
         if match arm.guard {
             None => true,
-            Some(Guard::If(expr) | Guard::IfLet(_, expr)) => {
-                !LocalUsedVisitor::new(binding_id).check_expr(expr)
-            }
+            Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !used_visitor.check_expr(expr),
         };
         // ...or anywhere in the inner match
-        if !arms_inner.iter().any(|arm| LocalUsedVisitor::new(binding_id).check_arm(arm));
+        if !arms_inner.iter().any(|arm| used_visitor.check_arm(arm));
         then {
             span_lint_and_then(
                 cx,
@@ -175,19 +174,15 @@ fn is_none_ctor(res: Res, tcx: TyCtxt<'_>) -> bool {
     false
 }
 
-/// Retrieves a binding ID with optional `&` and/or `*` operators removed. (e.g. `&**foo`)
-/// Returns `None` if a non-reference type is de-referenced.
-/// For example, if `Vec` is de-referenced to a slice, `None` is returned.
-fn addr_adjusted_binding(mut expr: &Expr<'_>, cx: &LateContext<'_>) -> Option<HirId> {
+/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
+/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
+fn strip_ref_operators<'hir>(mut expr: &'hir Expr<'hir>, typeck_results: &TypeckResults<'_>) -> &'hir Expr<'hir> {
     loop {
         match expr.kind {
             ExprKind::AddrOf(_, _, e) => expr = e,
-            ExprKind::Path(QPath::Resolved(None, path)) => match path.res {
-                Res::Local(binding_id) => break Some(binding_id),
-                _ => break None,
-            },
-            ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
-            _ => break None,
+            ExprKind::Unary(UnOp::Deref, e) if typeck_results.expr_ty(e).is_ref() => expr = e,
+            _ => break,
         }
     }
+    expr
 }
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_method.rs b/src/tools/clippy/clippy_lints/src/disallowed_method.rs
index 581c3242e37..56dc6d18a58 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_method.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_method.rs
@@ -1,29 +1,47 @@
-use crate::utils::span_lint;
+use crate::utils::{fn_def_id, span_lint};
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::Expr;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Symbol;
 
 declare_clippy_lint! {
-    /// **What it does:** Lints for specific trait methods defined in clippy.toml
+    /// **What it does:** Denies the configured methods and functions in clippy.toml
     ///
     /// **Why is this bad?** Some methods are undesirable in certain contexts,
-    /// and it would be beneficial to lint for them as needed.
+    /// and it's beneficial to lint for them as needed.
     ///
-    /// **Known problems:** None.
+    /// **Known problems:** Currently, you must write each function as a
+    /// fully-qualified path. This lint doesn't support aliases or reexported
+    /// names; be aware that many types in `std` are actually reexports.
+    ///
+    /// For example, if you want to disallow `Duration::as_secs`, your clippy.toml
+    /// configuration would look like
+    /// `disallowed-methods = ["core::time::Duration::as_secs"]` and not
+    /// `disallowed-methods = ["std::time::Duration::as_secs"]` as you might expect.
     ///
     /// **Example:**
     ///
+    /// An example clippy.toml configuration:
+    /// ```toml
+    /// # clippy.toml
+    /// disallowed-methods = ["alloc::vec::Vec::leak", "std::time::Instant::now"]
+    /// ```
+    ///
     /// ```rust,ignore
-    /// // example code where clippy issues a warning
-    /// foo.bad_method(); // Foo::bad_method is disallowed in the configuration
+    /// // Example code where clippy issues a warning
+    /// let xs = vec![1, 2, 3, 4];
+    /// xs.leak(); // Vec::leak is disallowed in the config.
+    ///
+    /// let _now = Instant::now(); // Instant::now is disallowed in the config.
     /// ```
+    ///
     /// Use instead:
     /// ```rust,ignore
-    /// // example code which does not raise clippy warning
-    /// goodStruct.bad_method(); // GoodStruct::bad_method is not disallowed
+    /// // Example code which does not raise clippy warning
+    /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
+    /// xs.push(123); // Vec::push is _not_ disallowed in the config.
     /// ```
     pub DISALLOWED_METHOD,
     nursery,
@@ -50,14 +68,12 @@ impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
 
 impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::MethodCall(_path, _, _args, _) = &expr.kind {
-            let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
-
-            let method_call = cx.get_def_path(def_id);
-            if self.disallowed.contains(&method_call) {
-                let method = method_call
-                    .iter()
-                    .map(|s| s.to_ident_string())
+        if let Some(def_id) = fn_def_id(cx, expr) {
+            let func_path = cx.get_def_path(def_id);
+            if self.disallowed.contains(&func_path) {
+                let func_path_string = func_path
+                    .into_iter()
+                    .map(Symbol::to_ident_string)
                     .collect::<Vec<_>>()
                     .join("::");
 
@@ -65,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
                     cx,
                     DISALLOWED_METHOD,
                     expr.span,
-                    &format!("use of a disallowed method `{}`", method),
+                    &format!("use of a disallowed method `{}`", func_path_string),
                 );
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
index bc2b2904698..83cee11c3a8 100644
--- a/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
+++ b/src/tools/clippy/clippy_lints/src/eval_order_dependence.rs
@@ -1,7 +1,6 @@
-use crate::utils::{get_parent_expr, span_lint, span_lint_and_note};
-use if_chain::if_chain;
+use crate::utils::{get_parent_expr, path_to_local, path_to_local_id, span_lint, span_lint_and_note};
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{def, BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, QPath, Stmt, StmtKind};
+use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty;
@@ -72,20 +71,14 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence {
         // Find a write to a local variable.
         match expr.kind {
             ExprKind::Assign(ref lhs, ..) | ExprKind::AssignOp(_, ref lhs, _) => {
-                if let ExprKind::Path(ref qpath) = lhs.kind {
-                    if let QPath::Resolved(_, ref path) = *qpath {
-                        if path.segments.len() == 1 {
-                            if let def::Res::Local(var) = cx.qpath_res(qpath, lhs.hir_id) {
-                                let mut visitor = ReadVisitor {
-                                    cx,
-                                    var,
-                                    write_expr: expr,
-                                    last_expr: expr,
-                                };
-                                check_for_unsequenced_reads(&mut visitor);
-                            }
-                        }
-                    }
+                if let Some(var) = path_to_local(lhs) {
+                    let mut visitor = ReadVisitor {
+                        cx,
+                        var,
+                        write_expr: expr,
+                        last_expr: expr,
+                    };
+                    check_for_unsequenced_reads(&mut visitor);
                 }
             },
             _ => {},
@@ -304,27 +297,20 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
             return;
         }
 
-        match expr.kind {
-            ExprKind::Path(ref qpath) => {
-                if_chain! {
-                    if let QPath::Resolved(None, ref path) = *qpath;
-                    if path.segments.len() == 1;
-                    if let def::Res::Local(local_id) = self.cx.qpath_res(qpath, expr.hir_id);
-                    if local_id == self.var;
-                    // Check that this is a read, not a write.
-                    if !is_in_assignment_position(self.cx, expr);
-                    then {
-                        span_lint_and_note(
-                            self.cx,
-                            EVAL_ORDER_DEPENDENCE,
-                            expr.span,
-                            "unsequenced read of a variable",
-                            Some(self.write_expr.span),
-                            "whether read occurs before this write depends on evaluation order"
-                        );
-                    }
-                }
+        if path_to_local_id(expr, self.var) {
+            // Check that this is a read, not a write.
+            if !is_in_assignment_position(self.cx, expr) {
+                span_lint_and_note(
+                    self.cx,
+                    EVAL_ORDER_DEPENDENCE,
+                    expr.span,
+                    "unsequenced read of a variable",
+                    Some(self.write_expr.span),
+                    "whether read occurs before this write depends on evaluation order",
+                );
             }
+        }
+        match expr.kind {
             // We're about to descend a closure. Since we don't know when (or
             // if) the closure will be evaluated, any reads in it might not
             // occur here (or ever). Like above, bail to avoid false positives.
diff --git a/src/tools/clippy/clippy_lints/src/functions.rs b/src/tools/clippy/clippy_lints/src/functions.rs
index 71a146cc298..94200a15420 100644
--- a/src/tools/clippy/clippy_lints/src/functions.rs
+++ b/src/tools/clippy/clippy_lints/src/functions.rs
@@ -1,7 +1,7 @@
 use crate::utils::{
     attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats,
-    last_path_segment, match_def_path, must_use_attr, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help,
-    span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
+    last_path_segment, match_def_path, must_use_attr, path_to_local, return_ty, snippet, snippet_opt, span_lint,
+    span_lint_and_help, span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
@@ -9,7 +9,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::intravisit;
-use rustc_hir::{def::Res, def_id::DefId};
+use rustc_hir::{def::Res, def_id::DefId, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::map::Map;
 use rustc_middle::lint::in_external_macro;
@@ -658,16 +658,14 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
 
 impl<'a, 'tcx> DerefVisitor<'a, 'tcx> {
     fn check_arg(&self, ptr: &hir::Expr<'_>) {
-        if let hir::ExprKind::Path(ref qpath) = ptr.kind {
-            if let Res::Local(id) = self.cx.qpath_res(qpath, ptr.hir_id) {
-                if self.ptrs.contains(&id) {
-                    span_lint(
-                        self.cx,
-                        NOT_UNSAFE_PTR_ARG_DEREF,
-                        ptr.span,
-                        "this public function dereferences a raw pointer but is not marked `unsafe`",
-                    );
-                }
+        if let Some(id) = path_to_local(ptr) {
+            if self.ptrs.contains(&id) {
+                span_lint(
+                    self.cx,
+                    NOT_UNSAFE_PTR_ARG_DEREF,
+                    ptr.span,
+                    "this public function dereferences a raw pointer but is not marked `unsafe`",
+                );
             }
         }
     }
@@ -698,7 +696,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
                             arg.span,
                             &mut tys,
                         )
-                        && is_mutated_static(self.cx, arg)
+                        && is_mutated_static(arg)
                     {
                         self.mutates_static = true;
                         return;
@@ -707,7 +705,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
                 }
             },
             Assign(ref target, ..) | AssignOp(_, ref target, _) | AddrOf(_, hir::Mutability::Mut, ref target) => {
-                self.mutates_static |= is_mutated_static(self.cx, target)
+                self.mutates_static |= is_mutated_static(target)
             },
             _ => {},
         }
@@ -718,12 +716,13 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for StaticMutVisitor<'a, 'tcx> {
     }
 }
 
-fn is_mutated_static(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
+fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
     use hir::ExprKind::{Field, Index, Path};
 
     match e.kind {
-        Path(ref qpath) => !matches!(cx.qpath_res(qpath, e.hir_id), Res::Local(_)),
-        Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(cx, inner),
+        Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)),
+        Path(_) => true,
+        Field(ref inner, _) | Index(ref inner, _) => is_mutated_static(inner),
         _ => false,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
index 5886c2360e3..5863eef8a26 100644
--- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs
+++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
@@ -1,8 +1,7 @@
-use crate::utils::{snippet, span_lint_and_then, visitors::LocalUsedVisitor};
+use crate::utils::{path_to_local_id, snippet, span_lint_and_then, visitors::LocalUsedVisitor};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::def::Res;
 use rustc_hir::BindingAnnotation;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -64,10 +63,11 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
                 if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
                 if let hir::StmtKind::Expr(ref if_) = expr.kind;
                 if let hir::ExprKind::If(ref cond, ref then, ref else_) = if_.kind;
-                if !LocalUsedVisitor::new(canonical_id).check_expr(cond);
+                let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id);
+                if !used_visitor.check_expr(cond);
                 if let hir::ExprKind::Block(ref then, _) = then.kind;
                 if let Some(value) = check_assign(cx, canonical_id, &*then);
-                if !LocalUsedVisitor::new(canonical_id).check_expr(value);
+                if !used_visitor.check_expr(value);
                 then {
                     let span = stmt.span.to(if_.span);
 
@@ -144,11 +144,9 @@ fn check_assign<'tcx>(
         if let Some(expr) = block.stmts.iter().last();
         if let hir::StmtKind::Semi(ref expr) = expr.kind;
         if let hir::ExprKind::Assign(ref var, ref value, _) = expr.kind;
-        if let hir::ExprKind::Path(ref qpath) = var.kind;
-        if let Res::Local(local_id) = cx.qpath_res(qpath, var.hir_id);
-        if decl == local_id;
+        if path_to_local_id(var, decl);
         then {
-            let mut v = LocalUsedVisitor::new(decl);
+            let mut v = LocalUsedVisitor::new(cx, decl);
 
             if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| v.check_stmt(stmt)) {
                 return None;
diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs
index 6a5a77f8690..7e96dfcc7da 100644
--- a/src/tools/clippy/clippy_lints/src/let_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs
@@ -5,7 +5,7 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::{implements_trait, is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};
+use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for `let _ = <expr>`
@@ -125,15 +125,6 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
 
                     GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
                 });
-                let implements_drop = cx.tcx.lang_items().drop_trait().map_or(false, |drop_trait|
-                    init_ty.walk().any(|inner| match inner.unpack() {
-                        GenericArgKind::Type(inner_ty) => {
-                            implements_trait(cx, inner_ty, drop_trait, &[])
-                        },
-
-                        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
-                    })
-                );
                 if contains_sync_guard {
                     span_lint_and_help(
                         cx,
@@ -144,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
                         "consider using an underscore-prefixed named \
                             binding or dropping explicitly with `std::mem::drop`"
                     )
-                } else if implements_drop {
+                } else if init_ty.needs_drop(cx.tcx, cx.param_env) {
                     span_lint_and_help(
                         cx,
                         LET_UNDERSCORE_DROP,
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 5a40c00bd67..d96911fac1a 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -310,6 +310,7 @@ mod regex;
 mod repeat_once;
 mod returns;
 mod self_assignment;
+mod semicolon_if_nothing_returned;
 mod serde_api;
 mod shadow;
 mod single_component_path_imports;
@@ -687,6 +688,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &loops::FOR_KV_MAP,
         &loops::FOR_LOOPS_OVER_FALLIBLES,
         &loops::ITER_NEXT_LOOP,
+        &loops::MANUAL_FLATTEN,
         &loops::MANUAL_MEMCPY,
         &loops::MUT_RANGE_BOUND,
         &loops::NEEDLESS_COLLECT,
@@ -732,6 +734,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &mem_replace::MEM_REPLACE_WITH_DEFAULT,
         &mem_replace::MEM_REPLACE_WITH_UNINIT,
         &methods::BIND_INSTEAD_OF_MAP,
+        &methods::BYTES_NTH,
         &methods::CHARS_LAST_CMP,
         &methods::CHARS_NEXT_CMP,
         &methods::CLONE_DOUBLE_REF,
@@ -741,6 +744,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &methods::EXPECT_USED,
         &methods::FILETYPE_IS_FILE,
         &methods::FILTER_MAP,
+        &methods::FILTER_MAP_IDENTITY,
         &methods::FILTER_MAP_NEXT,
         &methods::FILTER_NEXT,
         &methods::FLAT_MAP_IDENTITY,
@@ -875,6 +879,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &returns::LET_AND_RETURN,
         &returns::NEEDLESS_RETURN,
         &self_assignment::SELF_ASSIGNMENT,
+        &semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
         &serde_api::SERDE_API_MISUSE,
         &shadow::SHADOW_REUSE,
         &shadow::SHADOW_SAME,
@@ -1179,7 +1184,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| box redundant_else::RedundantElse);
     store.register_late_pass(|| box create_dir::CreateDir);
     store.register_early_pass(|| box needless_arbitrary_self_type::NeedlessArbitrarySelfType);
-    store.register_late_pass(|| box cargo_common_metadata::CargoCommonMetadata);
+    let cargo_ignore_publish = conf.cargo_ignore_publish;
+    store.register_late_pass(move || box cargo_common_metadata::CargoCommonMetadata::new(cargo_ignore_publish));
     store.register_late_pass(|| box multiple_crate_versions::MultipleCrateVersions);
     store.register_late_pass(|| box wildcard_dependencies::WildcardDependencies);
     let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
@@ -1236,6 +1242,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box manual_unwrap_or::ManualUnwrapOr);
     store.register_late_pass(|| box manual_ok_or::ManualOkOr);
     store.register_late_pass(|| box float_equality_without_abs::FloatEqualityWithoutAbs);
+    store.register_late_pass(|| box semicolon_if_nothing_returned::SemicolonIfNothingReturned);
     store.register_late_pass(|| box async_yields_async::AsyncYieldsAsync);
     let disallowed_methods = conf.disallowed_methods.iter().cloned().collect::<FxHashSet<_>>();
     store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));
@@ -1290,6 +1297,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&panic_unimplemented::UNIMPLEMENTED),
         LintId::of(&panic_unimplemented::UNREACHABLE),
         LintId::of(&pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
+        LintId::of(&semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
         LintId::of(&shadow::SHADOW_REUSE),
         LintId::of(&shadow::SHADOW_SAME),
         LintId::of(&strings::STRING_ADD),
@@ -1491,6 +1499,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&loops::FOR_KV_MAP),
         LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES),
         LintId::of(&loops::ITER_NEXT_LOOP),
+        LintId::of(&loops::MANUAL_FLATTEN),
         LintId::of(&loops::MANUAL_MEMCPY),
         LintId::of(&loops::MUT_RANGE_BOUND),
         LintId::of(&loops::NEEDLESS_COLLECT),
@@ -1524,11 +1533,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
         LintId::of(&mem_replace::MEM_REPLACE_WITH_UNINIT),
         LintId::of(&methods::BIND_INSTEAD_OF_MAP),
+        LintId::of(&methods::BYTES_NTH),
         LintId::of(&methods::CHARS_LAST_CMP),
         LintId::of(&methods::CHARS_NEXT_CMP),
         LintId::of(&methods::CLONE_DOUBLE_REF),
         LintId::of(&methods::CLONE_ON_COPY),
         LintId::of(&methods::EXPECT_FUN_CALL),
+        LintId::of(&methods::FILTER_MAP_IDENTITY),
         LintId::of(&methods::FILTER_NEXT),
         LintId::of(&methods::FLAT_MAP_IDENTITY),
         LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
@@ -1620,7 +1631,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&reference::DEREF_ADDROF),
         LintId::of(&reference::REF_IN_DEREF),
         LintId::of(&regex::INVALID_REGEX),
-        LintId::of(&regex::TRIVIAL_REGEX),
         LintId::of(&repeat_once::REPEAT_ONCE),
         LintId::of(&returns::LET_AND_RETURN),
         LintId::of(&returns::NEEDLESS_RETURN),
@@ -1741,6 +1751,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&matches::SINGLE_MATCH),
         LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
         LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
+        LintId::of(&methods::BYTES_NTH),
         LintId::of(&methods::CHARS_LAST_CMP),
         LintId::of(&methods::CHARS_NEXT_CMP),
         LintId::of(&methods::FROM_ITER_INSTEAD_OF_COLLECT),
@@ -1783,7 +1794,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&ranges::MANUAL_RANGE_CONTAINS),
         LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
         LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
-        LintId::of(&regex::TRIVIAL_REGEX),
         LintId::of(&returns::LET_AND_RETURN),
         LintId::of(&returns::NEEDLESS_RETURN),
         LintId::of(&single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
@@ -1822,6 +1832,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES),
         LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
         LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
+        LintId::of(&loops::MANUAL_FLATTEN),
         LintId::of(&loops::MUT_RANGE_BOUND),
         LintId::of(&loops::SINGLE_ELEMENT_LOOP),
         LintId::of(&loops::WHILE_LET_LOOP),
@@ -1835,6 +1846,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&matches::WILDCARD_IN_OR_PATTERNS),
         LintId::of(&methods::BIND_INSTEAD_OF_MAP),
         LintId::of(&methods::CLONE_ON_COPY),
+        LintId::of(&methods::FILTER_MAP_IDENTITY),
         LintId::of(&methods::FILTER_NEXT),
         LintId::of(&methods::FLAT_MAP_IDENTITY),
         LintId::of(&methods::INSPECT_FOR_EACH),
@@ -2011,6 +2023,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&needless_borrow::NEEDLESS_BORROW),
         LintId::of(&path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
         LintId::of(&redundant_pub_crate::REDUNDANT_PUB_CRATE),
+        LintId::of(&regex::TRIVIAL_REGEX),
         LintId::of(&strings::STRING_LIT_AS_BYTES),
         LintId::of(&transmute::USELESS_TRANSMUTE),
         LintId::of(&use_self::USE_SELF),
diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs
index 5211ca7da32..eb185377e20 100644
--- a/src/tools/clippy/clippy_lints/src/loops.rs
+++ b/src/tools/clippy/clippy_lints/src/loops.rs
@@ -1,14 +1,13 @@
 use crate::consts::constant;
-use crate::utils::paths;
 use crate::utils::sugg::Sugg;
-use crate::utils::usage::{is_unused, mutated_variables};
+use crate::utils::usage::mutated_variables;
 use crate::utils::visitors::LocalUsedVisitor;
 use crate::utils::{
     contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
-    indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item,
-    last_path_segment, match_trait_method, match_type, match_var, multispan_sugg, single_segment_path, snippet,
-    snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg,
-    span_lint_and_then, sugg, SpanlessEq,
+    indent_of, is_in_panic_handler, is_integer_const, is_no_std_crate, is_ok_ctor, is_refutable, is_some_ctor,
+    is_type_diagnostic_item, last_path_segment, match_trait_method, match_type, multispan_sugg, path_to_local,
+    path_to_local_id, paths, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite,
+    span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast::ast;
@@ -494,8 +493,40 @@ declare_clippy_lint! {
     "there is no reason to have a single element loop"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Check for unnecessary `if let` usage in a for loop
+    /// where only the `Some` or `Ok` variant of the iterator element is used.
+    ///
+    /// **Why is this bad?** It is verbose and can be simplified
+    /// by first calling the `flatten` method on the `Iterator`.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// let x = vec![Some(1), Some(2), Some(3)];
+    /// for n in x {
+    ///     if let Some(n) = n {
+    ///         println!("{}", n);
+    ///     }
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let x = vec![Some(1), Some(2), Some(3)];
+    /// for n in x.into_iter().flatten() {
+    ///     println!("{}", n);
+    /// }
+    /// ```
+    pub MANUAL_FLATTEN,
+    complexity,
+    "for loops over `Option`s or `Result`s with a single expression can be simplified"
+}
+
 declare_lint_pass!(Loops => [
     MANUAL_MEMCPY,
+    MANUAL_FLATTEN,
     NEEDLESS_RANGE_LOOP,
     EXPLICIT_ITER_LOOP,
     EXPLICIT_INTO_ITER_LOOP,
@@ -517,14 +548,14 @@ declare_lint_pass!(Loops => [
 impl<'tcx> LateLintPass<'tcx> for Loops {
     #[allow(clippy::too_many_lines)]
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let Some((pat, arg, body)) = higher::for_loop(expr) {
+        if let Some((pat, arg, body, span)) = higher::for_loop(expr) {
             // we don't want to check expanded macros
             // this check is not at the top of the function
             // since higher::for_loop expressions are marked as expansions
             if body.span.from_expansion() {
                 return;
             }
-            check_for_loop(cx, pat, arg, body, expr);
+            check_for_loop(cx, pat, arg, body, expr, span);
         }
 
         // we don't want to check expanded macros
@@ -707,7 +738,7 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult
 fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
     let stmts = block.stmts.iter().map(stmt_to_expr);
     let expr = once(block.expr.as_deref());
-    let mut iter = stmts.chain(expr).filter_map(|e| e);
+    let mut iter = stmts.chain(expr).flatten();
     never_loop_expr_seq(&mut iter, main_loop_id)
 }
 
@@ -819,6 +850,7 @@ fn check_for_loop<'tcx>(
     arg: &'tcx Expr<'_>,
     body: &'tcx Expr<'_>,
     expr: &'tcx Expr<'_>,
+    span: Span,
 ) {
     let is_manual_memcpy_triggered = detect_manual_memcpy(cx, pat, arg, body, expr);
     if !is_manual_memcpy_triggered {
@@ -830,6 +862,7 @@ fn check_for_loop<'tcx>(
     check_for_mut_range_bound(cx, arg, body);
     check_for_single_element_loop(cx, pat, arg, body, expr);
     detect_same_item_push(cx, pat, arg, body, expr);
+    check_manual_flatten(cx, pat, arg, body, span);
 }
 
 // this function assumes the given expression is a `for` loop.
@@ -843,21 +876,6 @@ fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span {
     }
 }
 
-fn same_var<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, var: HirId) -> bool {
-    if_chain! {
-        if let ExprKind::Path(qpath) = &expr.kind;
-        if let QPath::Resolved(None, path) = qpath;
-        if path.segments.len() == 1;
-        if let Res::Local(local_id) = cx.qpath_res(qpath, expr.hir_id);
-        then {
-            // our variable!
-            local_id == var
-        } else {
-            false
-        }
-    }
-}
-
 /// a wrapper of `Sugg`. Besides what `Sugg` do, this removes unnecessary `0`;
 /// and also, it avoids subtracting a variable from the same one by replacing it with `0`.
 /// it exists for the convenience of the overloaded operators while normal functions can do the
@@ -1010,14 +1028,9 @@ fn get_details_from_idx<'tcx>(
     idx: &Expr<'_>,
     starts: &[Start<'tcx>],
 ) -> Option<(StartKind<'tcx>, Offset)> {
-    fn get_start<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option<StartKind<'tcx>> {
-        starts.iter().find_map(|start| {
-            if same_var(cx, e, start.id) {
-                Some(start.kind)
-            } else {
-                None
-            }
-        })
+    fn get_start<'tcx>(e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option<StartKind<'tcx>> {
+        let id = path_to_local(e)?;
+        starts.iter().find(|start| start.id == id).map(|start| start.kind)
     }
 
     fn get_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]) -> Option<Sugg<'static>> {
@@ -1026,7 +1039,7 @@ fn get_details_from_idx<'tcx>(
                 ast::LitKind::Int(x, _ty) => Some(Sugg::NonParen(x.to_string().into())),
                 _ => None,
             },
-            ExprKind::Path(..) if get_start(cx, e, starts).is_none() => Some(Sugg::hir(cx, e, "???")),
+            ExprKind::Path(..) if get_start(e, starts).is_none() => Some(Sugg::hir(cx, e, "???")),
             _ => None,
         }
     }
@@ -1034,18 +1047,18 @@ fn get_details_from_idx<'tcx>(
     match idx.kind {
         ExprKind::Binary(op, lhs, rhs) => match op.node {
             BinOpKind::Add => {
-                let offset_opt = get_start(cx, lhs, starts)
+                let offset_opt = get_start(lhs, starts)
                     .and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, o)))
-                    .or_else(|| get_start(cx, rhs, starts).and_then(|s| get_offset(cx, lhs, starts).map(|o| (s, o))));
+                    .or_else(|| get_start(rhs, starts).and_then(|s| get_offset(cx, lhs, starts).map(|o| (s, o))));
 
                 offset_opt.map(|(s, o)| (s, Offset::positive(o)))
             },
             BinOpKind::Sub => {
-                get_start(cx, lhs, starts).and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o))))
+                get_start(lhs, starts).and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, Offset::negative(o))))
             },
             _ => None,
         },
-        ExprKind::Path(..) => get_start(cx, idx, starts).map(|s| (s, Offset::empty())),
+        ExprKind::Path(..) => get_start(idx, starts).map(|s| (s, Offset::empty())),
         _ => None,
     }
 }
@@ -1062,11 +1075,10 @@ fn get_assignment<'tcx>(e: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, &'tcx
 /// The returned iterator yields `None` if no assignment expressions are there,
 /// filtering out the increments of the given whitelisted loop counters;
 /// because its job is to make sure there's nothing other than assignments and the increments.
-fn get_assignments<'a: 'c, 'tcx: 'c, 'c>(
-    cx: &'a LateContext<'tcx>,
+fn get_assignments<'a, 'tcx>(
     Block { stmts, expr, .. }: &'tcx Block<'tcx>,
-    loop_counters: &'c [Start<'tcx>],
-) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> + 'c {
+    loop_counters: &'a [Start<'tcx>],
+) -> impl Iterator<Item = Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)>> + 'a {
     // As the `filter` and `map` below do different things, I think putting together
     // just increases complexity. (cc #3188 and #4193)
     stmts
@@ -1078,12 +1090,14 @@ fn get_assignments<'a: 'c, 'tcx: 'c, 'c>(
         .chain((*expr).into_iter())
         .filter(move |e| {
             if let ExprKind::AssignOp(_, place, _) = e.kind {
-                !loop_counters
-                    .iter()
-                    // skip the first item which should be `StartKind::Range`
-                    // this makes it possible to use the slice with `StartKind::Range` in the same iterator loop.
-                    .skip(1)
-                    .any(|counter| same_var(cx, place, counter.id))
+                path_to_local(place).map_or(false, |id| {
+                    !loop_counters
+                        .iter()
+                        // skip the first item which should be `StartKind::Range`
+                        // this makes it possible to use the slice with `StartKind::Range` in the same iterator loop.
+                        .skip(1)
+                        .any(|counter| counter.id == id)
+                })
             } else {
                 true
             }
@@ -1140,7 +1154,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
             if method.ident.name == sym!(len);
             if len_args.len() == 1;
             if let Some(arg) = len_args.get(0);
-            if var_def_id(cx, arg) == var_def_id(cx, base);
+            if path_to_local(arg) == path_to_local(base);
             then {
                 if sugg.as_str() == end_str {
                     sugg::EMPTY.into()
@@ -1245,7 +1259,7 @@ fn detect_manual_memcpy<'tcx>(
                 if let Some(loop_counters) = get_loop_counters(cx, block, expr) {
                     starts.extend(loop_counters);
                 }
-                iter_a = Some(get_assignments(cx, block, &starts));
+                iter_a = Some(get_assignments(block, &starts));
             } else {
                 iter_b = Some(get_assignment(body));
             }
@@ -1267,7 +1281,7 @@ fn detect_manual_memcpy<'tcx>(
                             if let Some((start_right, offset_right)) = get_details_from_idx(cx, &idx_right, &starts);
 
                             // Source and destination must be different
-                            if var_def_id(cx, base_left) != var_def_id(cx, base_right);
+                            if path_to_local(base_left) != path_to_local(base_right);
                             then {
                                 Some((IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left },
                                     IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right }))
@@ -1879,8 +1893,8 @@ fn check_for_loop_over_map_kv<'tcx>(
             let arg_span = arg.span;
             let (new_pat_span, kind, ty, mutbl) = match *cx.typeck_results().expr_ty(arg).kind() {
                 ty::Ref(_, ty, mutbl) => match (&pat[0].kind, &pat[1].kind) {
-                    (key, _) if pat_is_wild(key, body) => (pat[1].span, "value", ty, mutbl),
-                    (_, value) if pat_is_wild(value, body) => (pat[0].span, "key", ty, Mutability::Not),
+                    (key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value", ty, mutbl),
+                    (_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key", ty, Mutability::Not),
                     _ => return,
                 },
                 _ => return,
@@ -1953,6 +1967,77 @@ fn check_for_single_element_loop<'tcx>(
     }
 }
 
+/// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
+/// iterator element is used.
+fn check_manual_flatten<'tcx>(
+    cx: &LateContext<'tcx>,
+    pat: &'tcx Pat<'_>,
+    arg: &'tcx Expr<'_>,
+    body: &'tcx Expr<'_>,
+    span: Span,
+) {
+    if let ExprKind::Block(ref block, _) = body.kind {
+        // Ensure the `if let` statement is the only expression or statement in the for-loop
+        let inner_expr = if block.stmts.len() == 1 && block.expr.is_none() {
+            let match_stmt = &block.stmts[0];
+            if let StmtKind::Semi(inner_expr) = match_stmt.kind {
+                Some(inner_expr)
+            } else {
+                None
+            }
+        } else if block.stmts.is_empty() {
+            block.expr
+        } else {
+            None
+        };
+
+        if_chain! {
+            if let Some(inner_expr) = inner_expr;
+            if let ExprKind::Match(
+                ref match_expr, ref match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false }
+            ) = inner_expr.kind;
+            // Ensure match_expr in `if let` statement is the same as the pat from the for-loop
+            if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
+            if path_to_local_id(match_expr, pat_hir_id);
+            // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
+            if let PatKind::TupleStruct(QPath::Resolved(None, path), _, _) = match_arms[0].pat.kind;
+            let some_ctor = is_some_ctor(cx, path.res);
+            let ok_ctor = is_ok_ctor(cx, path.res);
+            if some_ctor || ok_ctor;
+            let if_let_type = if some_ctor { "Some" } else { "Ok" };
+
+            then {
+                // Prepare the error message
+                let msg = format!("unnecessary `if let` since only the `{}` variant of the iterator element is used", if_let_type);
+
+                // Prepare the help message
+                let mut applicability = Applicability::MaybeIncorrect;
+                let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
+
+                span_lint_and_then(
+                    cx,
+                    MANUAL_FLATTEN,
+                    span,
+                    &msg,
+                    |diag| {
+                        let sugg = format!("{}.flatten()", arg_snippet);
+                        diag.span_suggestion(
+                            arg.span,
+                            "try",
+                            sugg,
+                            Applicability::MaybeIncorrect,
+                        );
+                        diag.span_help(
+                            inner_expr.span,
+                            "...and remove the `if let` statement in the for loop",
+                        );
+                    }
+                );
+            }
+        }
+    }
+}
+
 struct MutatePairDelegate<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     hir_id_low: Option<HirId>,
@@ -2024,20 +2109,11 @@ fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) {
 
 fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> {
     if_chain! {
-        if let ExprKind::Path(ref qpath) = bound.kind;
-        if let QPath::Resolved(None, _) = *qpath;
+        if let Some(hir_id) = path_to_local(bound);
+        if let Node::Binding(pat) = cx.tcx.hir().get(hir_id);
+        if let PatKind::Binding(BindingAnnotation::Mutable, ..) = pat.kind;
         then {
-            let res = cx.qpath_res(qpath, bound.hir_id);
-            if let Res::Local(hir_id) = res {
-                let node_str = cx.tcx.hir().get(hir_id);
-                if_chain! {
-                    if let Node::Binding(pat) = node_str;
-                    if let PatKind::Binding(BindingAnnotation::Mutable, ..) = pat.kind;
-                    then {
-                        return Some(hir_id);
-                    }
-                }
-            }
+            return Some(hir_id);
         }
     }
     None
@@ -2069,10 +2145,12 @@ fn check_for_mutation<'tcx>(
 }
 
 /// Returns `true` if the pattern is a `PatWild` or an ident prefixed with `_`.
-fn pat_is_wild<'tcx>(pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
+fn pat_is_wild<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx PatKind<'_>, body: &'tcx Expr<'_>) -> bool {
     match *pat {
         PatKind::Wild => true,
-        PatKind::Binding(.., ident, None) if ident.as_str().starts_with('_') => is_unused(&ident, body),
+        PatKind::Binding(_, id, ident, None) if ident.as_str().starts_with('_') => {
+            !LocalUsedVisitor::new(cx, id).check_expr(body)
+        },
         _ => false,
     }
 }
@@ -2108,9 +2186,9 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
             if let QPath::Resolved(None, ref seqvar) = *seqpath;
             if seqvar.segments.len() == 1;
             then {
-                let index_used_directly = same_var(self.cx, idx, self.var);
+                let index_used_directly = path_to_local_id(idx, self.var);
                 let indexed_indirectly = {
-                    let mut used_visitor = LocalUsedVisitor::new(self.var);
+                    let mut used_visitor = LocalUsedVisitor::new(self.cx, self.var);
                     walk_expr(&mut used_visitor, idx);
                     used_visitor.used
                 };
@@ -2179,17 +2257,14 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
 
         if_chain! {
             // directly using a variable
-            if let ExprKind::Path(ref qpath) = expr.kind;
-            if let QPath::Resolved(None, ref path) = *qpath;
-            if path.segments.len() == 1;
+            if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind;
+            if let Res::Local(local_id) = path.res;
             then {
-                if let Res::Local(local_id) = self.cx.qpath_res(qpath, expr.hir_id) {
-                    if local_id == self.var {
-                        self.nonindex = true;
-                    } else {
-                        // not the correct variable, but still a variable
-                        self.referenced.insert(path.segments[0].ident.name);
-                    }
+                if local_id == self.var {
+                    self.nonindex = true;
+                } else {
+                    // not the correct variable, but still a variable
+                    self.referenced.insert(path.segments[0].ident.name);
                 }
             }
         }
@@ -2247,7 +2322,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
 }
 
 fn is_used_inside<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, container: &'tcx Expr<'_>) -> bool {
-    let def_id = match var_def_id(cx, expr) {
+    let def_id = match path_to_local(expr) {
         Some(id) => id,
         None => return false,
     };
@@ -2260,12 +2335,11 @@ fn is_used_inside<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, container:
 }
 
 fn is_iterator_used_after_while_let<'tcx>(cx: &LateContext<'tcx>, iter_expr: &'tcx Expr<'_>) -> bool {
-    let def_id = match var_def_id(cx, iter_expr) {
+    let def_id = match path_to_local(iter_expr) {
         Some(id) => id,
         None => return false,
     };
     let mut visitor = VarUsedAfterLoopVisitor {
-        cx,
         def_id,
         iter_expr_id: iter_expr.hir_id,
         past_while_let: false,
@@ -2277,20 +2351,19 @@ fn is_iterator_used_after_while_let<'tcx>(cx: &LateContext<'tcx>, iter_expr: &'t
     visitor.var_used_after_while_let
 }
 
-struct VarUsedAfterLoopVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
+struct VarUsedAfterLoopVisitor {
     def_id: HirId,
     iter_expr_id: HirId,
     past_while_let: bool,
     var_used_after_while_let: bool,
 }
 
-impl<'a, 'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor {
     type Map = Map<'tcx>;
 
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
         if self.past_while_let {
-            if Some(self.def_id) == var_def_id(self.cx, expr) {
+            if path_to_local_id(expr, self.def_id) {
                 self.var_used_after_while_let = true;
             }
         } else if self.iter_expr_id == expr.hir_id {
@@ -2412,7 +2485,7 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
         }
 
         // If node is a variable
-        if let Some(def_id) = var_def_id(self.cx, expr) {
+        if let Some(def_id) = path_to_local(expr) {
             if let Some(parent) = get_parent_expr(self.cx, expr) {
                 let state = self.states.entry(def_id).or_insert(IncrementVisitorVarState::Initial);
                 if *state == IncrementVisitorVarState::IncrOnce {
@@ -2539,7 +2612,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
         }
 
         // If node is the desired variable, see how it's used
-        if var_def_id(self.cx, expr) == Some(self.var_id) {
+        if path_to_local_id(expr, self.var_id) {
             if self.past_loop {
                 self.state = InitializeVisitorState::DontWarn;
                 return;
@@ -2586,16 +2659,6 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
     }
 }
 
-fn var_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<HirId> {
-    if let ExprKind::Path(ref qpath) = expr.kind {
-        let path_res = cx.qpath_res(qpath, expr.hir_id);
-        if let Res::Local(hir_id) = path_res {
-            return Some(hir_id);
-        }
-    }
-    None
-}
-
 fn is_loop(expr: &Expr<'_>) -> bool {
     matches!(expr.kind, ExprKind::Loop(..))
 }
@@ -2618,8 +2681,8 @@ fn is_nested(cx: &LateContext<'_>, match_expr: &Expr<'_>, iter_expr: &Expr<'_>)
 
 fn is_loop_nested(cx: &LateContext<'_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'_>) -> bool {
     let mut id = loop_expr.hir_id;
-    let iter_name = if let Some(name) = path_name(iter_expr) {
-        name
+    let iter_id = if let Some(id) = path_to_local(iter_expr) {
+        id
     } else {
         return true;
     };
@@ -2637,7 +2700,7 @@ fn is_loop_nested(cx: &LateContext<'_>, loop_expr: &Expr<'_>, iter_expr: &Expr<'
             Some(Node::Block(block)) => {
                 let mut block_visitor = LoopNestVisitor {
                     hir_id: id,
-                    iterator: iter_name,
+                    iterator: iter_id,
                     nesting: Unknown,
                 };
                 walk_block(&mut block_visitor, block);
@@ -2665,7 +2728,7 @@ use self::Nesting::{LookFurther, RuledOut, Unknown};
 
 struct LoopNestVisitor {
     hir_id: HirId,
-    iterator: Symbol,
+    iterator: HirId,
     nesting: Nesting,
 }
 
@@ -2690,7 +2753,7 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
         }
         match expr.kind {
             ExprKind::Assign(ref path, _, _) | ExprKind::AssignOp(_, ref path, _) => {
-                if match_var(path, self.iterator) {
+                if path_to_local_id(path, self.iterator) {
                     self.nesting = RuledOut;
                 }
             },
@@ -2702,8 +2765,8 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
         if self.nesting != Unknown {
             return;
         }
-        if let PatKind::Binding(.., span_name, _) = pat.kind {
-            if self.iterator == span_name.name {
+        if let PatKind::Binding(_, id, ..) = pat.kind {
+            if id == self.iterator {
                 self.nesting = RuledOut;
                 return;
             }
@@ -2716,16 +2779,6 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
     }
 }
 
-fn path_name(e: &Expr<'_>) -> Option<Symbol> {
-    if let ExprKind::Path(QPath::Resolved(_, ref path)) = e.kind {
-        let segments = &path.segments;
-        if segments.len() == 1 {
-            return Some(segments[0].ident.name);
-        }
-    };
-    None
-}
-
 fn check_infinite_loop<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) {
     if constant(cx, cx.typeck_results(), cond).is_some() {
         // A pure constant condition (e.g., `while false`) is not linted.
@@ -3087,7 +3140,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
     type Map = Map<'tcx>;
 
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if same_var(self.cx, expr, self.id) {
+        if path_to_local_id(expr, self.id) {
             self.count += 1;
         } else {
             walk_expr(self, expr);
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index bb52888883a..40f04bd677d 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -160,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
             let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name));
 
             if let Some(idx) = found_idx {
-                let _ = self.mac_refs.remove(idx);
+                self.mac_refs.remove(idx);
                 let seg = import.split("::").collect::<Vec<_>>();
 
                 match seg.as_slice() {
diff --git a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
index 8c77e155b70..efb05b8ffdf 100644
--- a/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_ok_or.rs
@@ -1,9 +1,10 @@
 use crate::utils::{
-    indent_of, is_type_diagnostic_item, match_qpath, paths, reindent_multiline, snippet_opt, span_lint_and_sugg,
+    indent_of, is_type_diagnostic_item, match_qpath, path_to_local_id, paths, reindent_multiline, snippet_opt,
+    span_lint_and_sugg,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{def, Expr, ExprKind, PatKind, QPath};
+use rustc_hir::{Expr, ExprKind, PatKind};
 use rustc_lint::LintContext;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
@@ -90,8 +91,6 @@ fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
         if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
         if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
         if match_qpath(ok_path, &paths::RESULT_OK);
-        if let ExprKind::Path(QPath::Resolved(_, ok_arg_path)) = ok_arg.kind;
-        if let def::Res::Local(ok_arg_path_id) = ok_arg_path.res;
-        then { param_id == ok_arg_path_id } else { false }
+        then { path_to_local_id(ok_arg, param_id) } else { false }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
index 9e2c6c7f231..b452225b5db 100644
--- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
@@ -1,9 +1,9 @@
 use crate::consts::constant_simple;
 use crate::utils;
-use crate::utils::sugg;
+use crate::utils::{path_to_local_id, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{def, Arm, Expr, ExprKind, Pat, PatKind, QPath};
+use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind};
 use rustc_lint::LintContext;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
@@ -83,9 +83,7 @@ fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
             if utils::match_qpath(unwrap_qpath, &utils::paths::OPTION_SOME)
                 || utils::match_qpath(unwrap_qpath, &utils::paths::RESULT_OK);
             if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
-            if let ExprKind::Path(QPath::Resolved(_, body_path)) = unwrap_arm.body.kind;
-            if let def::Res::Local(body_path_hir_id) = body_path.res;
-            if body_path_hir_id == binding_hir_id;
+            if path_to_local_id(unwrap_arm.body, binding_hir_id);
             if !utils::usage::contains_return_break_continue_macro(or_arm.body);
             then {
                 Some(or_arm)
diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs
index ba7b9bd0424..e33001b16bc 100644
--- a/src/tools/clippy/clippy_lints/src/matches.rs
+++ b/src/tools/clippy/clippy_lints/src/matches.rs
@@ -1,11 +1,12 @@
 use crate::consts::{constant, miri_to_const, Constant};
 use crate::utils::sugg::Sugg;
-use crate::utils::usage::is_unused;
+use crate::utils::visitors::LocalUsedVisitor;
 use crate::utils::{
-    expr_block, get_arg_name, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of,
-    is_refutable, is_type_diagnostic_item, is_wild, match_qpath, match_type, match_var, meets_msrv, multispan_sugg,
+    expr_block, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
+    is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local_id,
     peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt,
     snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
+    strip_pat_refs,
 };
 use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
 use if_chain::if_chain;
@@ -616,9 +617,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
             if let PatKind::TupleStruct(
                 QPath::Resolved(None, ref variant_name), ref args, _) = arms[0].pat.kind;
             if args.len() == 1;
-            if let Some(arg) = get_arg_name(&args[0]);
+            if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
             let body = remove_blocks(&arms[0].body);
-            if match_var(body, arg);
+            if path_to_local_id(body, arg);
 
             then {
                 let mut applicability = Applicability::MachineApplicable;
@@ -910,7 +911,7 @@ fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms
     }
 }
 
-fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
+fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
     let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
     if is_type_diagnostic_item(cx, ex_ty, sym::result_type) {
         for arm in arms {
@@ -922,8 +923,10 @@ fn check_wild_err_arm(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
                     if !matching_wild {
                         // Looking for unused bindings (i.e.: `_e`)
                         inner.iter().for_each(|pat| {
-                            if let PatKind::Binding(.., ident, None) = &pat.kind {
-                                if ident.as_str().starts_with('_') && is_unused(ident, arm.body) {
+                            if let PatKind::Binding(_, id, ident, None) = pat.kind {
+                                if ident.as_str().starts_with('_')
+                                    && !LocalUsedVisitor::new(cx, id).check_expr(arm.body)
+                                {
                                     ident_bind_name = (&ident.name.as_str()).to_string();
                                     matching_wild = true;
                                 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs
new file mode 100644
index 00000000000..defc50ede22
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs
@@ -0,0 +1,39 @@
+use crate::utils::{is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::BYTES_NTH;
+
+pub(super) fn lints<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>]) {
+    if_chain! {
+        if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind;
+        let ty = cx.typeck_results().expr_ty(&iter_args[0]).peel_refs();
+        let caller_type = if is_type_diagnostic_item(cx, ty, sym::string_type) {
+            Some("String")
+        } else if ty.is_str() {
+            Some("str")
+        } else {
+            None
+        };
+        if let Some(caller_type) = caller_type;
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+            span_lint_and_sugg(
+                cx,
+                BYTES_NTH,
+                expr.span,
+                &format!("called `.byte().nth()` on a `{}`", caller_type),
+                "try",
+                format!(
+                    "{}.as_bytes().get({})",
+                    snippet_with_applicability(cx, iter_args[0].span, "..", &mut applicability),
+                    snippet_with_applicability(cx, args[1].span, "..", &mut applicability)
+                ),
+                applicability,
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
new file mode 100644
index 00000000000..9e646360a40
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs
@@ -0,0 +1,52 @@
+use crate::utils::{match_qpath, match_trait_method, path_to_local_id, paths, span_lint_and_sugg};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_span::source_map::Span;
+
+use super::FILTER_MAP_IDENTITY;
+
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &hir::Expr<'_>,
+    filter_map_args: &[hir::Expr<'_>],
+    filter_map_span: Span,
+) {
+    if match_trait_method(cx, expr, &paths::ITERATOR) {
+        let arg_node = &filter_map_args[1].kind;
+
+        let apply_lint = |message: &str| {
+            span_lint_and_sugg(
+                cx,
+                FILTER_MAP_IDENTITY,
+                filter_map_span.with_hi(expr.span.hi()),
+                message,
+                "try",
+                "flatten()".to_string(),
+                Applicability::MachineApplicable,
+            );
+        };
+
+        if_chain! {
+            if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node;
+            let body = cx.tcx.hir().body(*body_id);
+
+            if let hir::PatKind::Binding(_, binding_id, ..) = body.params[0].pat.kind;
+            if path_to_local_id(&body.value, binding_id);
+            then {
+                apply_lint("called `filter_map(|x| x)` on an `Iterator`");
+            }
+        }
+
+        if_chain! {
+            if let hir::ExprKind::Path(ref qpath) = arg_node;
+
+            if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
+
+            then {
+                apply_lint("called `filter_map(std::convert::identity)` on an `Iterator`");
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 0918843294d..433f513b1a8 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -1,4 +1,6 @@
 mod bind_instead_of_map;
+mod bytes_nth;
+mod filter_map_identity;
 mod inefficient_to_string;
 mod inspect_for_each;
 mod manual_saturating_arithmetic;
@@ -15,8 +17,7 @@ use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::def::Res;
-use rustc_hir::{Expr, ExprKind, PatKind, QPath, TraitItem, TraitItemKind, UnOp};
+use rustc_hir::{Expr, ExprKind, PatKind, TraitItem, TraitItemKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, TraitRef, Ty, TyS};
@@ -30,12 +31,12 @@ use crate::consts::{constant, Constant};
 use crate::utils::eager_or_lazy::is_lazyness_candidate;
 use crate::utils::usage::mutated_variables;
 use crate::utils::{
-    contains_return, contains_ty, get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher,
-    implements_trait, in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
-    match_def_path, match_qpath, match_trait_method, match_type, match_var, meets_msrv, method_calls,
-    method_chain_args, paths, remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability,
-    snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
-    walk_ptrs_ty_depth, SpanlessEq,
+    contains_return, contains_ty, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
+    in_macro, is_copy, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment, match_def_path,
+    match_qpath, match_trait_method, match_type, meets_msrv, method_calls, method_chain_args, path_to_local_id, paths,
+    remove_blocks, return_ty, single_segment_path, snippet, snippet_with_applicability, snippet_with_macro_callsite,
+    span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, strip_pat_refs, sugg, walk_ptrs_ty_depth,
+    SpanlessEq,
 };
 
 declare_clippy_lint! {
@@ -1467,6 +1468,51 @@ declare_clippy_lint! {
     "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
 }
 
+declare_clippy_lint! {
+    /// **What it does:** Checks for usage of `filter_map(|x| x)`.
+    ///
+    /// **Why is this bad?** Readability, this can be written more concisely by using `flatten`.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// # let iter = vec![Some(1)].into_iter();
+    /// iter.filter_map(|x| x);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # let iter = vec![Some(1)].into_iter();
+    /// iter.flatten();
+    /// ```
+    pub FILTER_MAP_IDENTITY,
+    complexity,
+    "call to `filter_map` where `flatten` is sufficient"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for the use of `.bytes().nth()`.
+    ///
+    /// **Why is this bad?** `.as_bytes().get()` is more efficient and more
+    /// readable.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// // Bad
+    /// let _ = "Hello".bytes().nth(3);
+    ///
+    /// // Good
+    /// let _ = "Hello".as_bytes().get(3);
+    /// ```
+    pub BYTES_NTH,
+    style,
+    "replace `.bytes().nth()` with `.as_bytes().get()`"
+}
+
 pub struct Methods {
     msrv: Option<RustcVersion>,
 }
@@ -1504,6 +1550,7 @@ impl_lint_pass!(Methods => [
     FILTER_NEXT,
     SKIP_WHILE_NEXT,
     FILTER_MAP,
+    FILTER_MAP_IDENTITY,
     MANUAL_FILTER_MAP,
     MANUAL_FIND_MAP,
     FILTER_MAP_NEXT,
@@ -1513,6 +1560,7 @@ impl_lint_pass!(Methods => [
     ITER_NEXT_SLICE,
     ITER_NTH,
     ITER_NTH_ZERO,
+    BYTES_NTH,
     ITER_SKIP_NEXT,
     GET_UNWRAP,
     STRING_EXTEND_CHARS,
@@ -1590,6 +1638,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             ["extend", ..] => lint_extend(cx, expr, arg_lists[0]),
             ["nth", "iter"] => lint_iter_nth(cx, expr, &arg_lists, false),
             ["nth", "iter_mut"] => lint_iter_nth(cx, expr, &arg_lists, true),
+            ["nth", "bytes"] => bytes_nth::lints(cx, expr, &arg_lists[1]),
             ["nth", ..] => lint_iter_nth_zero(cx, expr, arg_lists[0]),
             ["step_by", ..] => lint_step_by(cx, expr, arg_lists[0]),
             ["next", "skip"] => lint_iter_skip_next(cx, expr, arg_lists[1]),
@@ -1597,7 +1646,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             ["as_ref"] => lint_asref(cx, expr, "as_ref", arg_lists[0]),
             ["as_mut"] => lint_asref(cx, expr, "as_mut", arg_lists[0]),
             ["fold", ..] => lint_unnecessary_fold(cx, expr, arg_lists[0], method_spans[0]),
-            ["filter_map", ..] => unnecessary_filter_map::lint(cx, expr, arg_lists[0]),
+            ["filter_map", ..] => {
+                unnecessary_filter_map::lint(cx, expr, arg_lists[0]);
+                filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]);
+            },
             ["count", "map"] => lint_suspicious_map(cx, expr),
             ["assume_init"] => lint_maybe_uninit(cx, &arg_lists[0][0], expr),
             ["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => {
@@ -2183,7 +2235,10 @@ fn lint_expect_fun_call(
         span_replace_word,
         &format!("use of `{}` followed by a function call", name),
         "try this",
-        format!("unwrap_or_else({} {{ panic!(\"{{}}\", {}) }})", closure_args, arg_root_snippet),
+        format!(
+            "unwrap_or_else({} {{ panic!(\"{{}}\", {}) }})",
+            closure_args, arg_root_snippet
+        ),
         applicability,
     );
 }
@@ -2396,11 +2451,12 @@ fn lint_unnecessary_fold(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args:
             if bin_op.node == op;
 
             // Extract the names of the two arguments to the closure
-            if let Some(first_arg_ident) = get_arg_name(&closure_body.params[0].pat);
-            if let Some(second_arg_ident) = get_arg_name(&closure_body.params[1].pat);
+            if let [param_a, param_b] = closure_body.params;
+            if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(&param_a.pat).kind;
+            if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(&param_b.pat).kind;
 
-            if match_var(&*left_expr, first_arg_ident);
-            if replacement_has_args || match_var(&*right_expr, second_arg_ident);
+            if path_to_local_id(left_expr, first_arg_id);
+            if replacement_has_args || path_to_local_id(right_expr, second_arg_id);
 
             then {
                 let mut applicability = Applicability::MachineApplicable;
@@ -3068,10 +3124,8 @@ fn lint_filter_map<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_f
             };
             // let the filter closure arg and the map closure arg be equal
             if_chain! {
-                if let ExprKind::Path(QPath::Resolved(None, a_path)) = a_path.kind;
-                if let ExprKind::Path(QPath::Resolved(None, b_path)) = b.kind;
-                if a_path.res == Res::Local(filter_param_id);
-                if b_path.res == Res::Local(map_param_id);
+                if path_to_local_id(a_path, filter_param_id);
+                if path_to_local_id(b, map_param_id);
                 if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b));
                 then {
                     return true;
@@ -3255,8 +3309,9 @@ fn lint_search_is_some<'tcx>(
                 then {
                     if let hir::PatKind::Ref(..) = closure_arg.pat.kind {
                         Some(search_snippet.replacen('&', "", 1))
-                    } else if let Some(name) = get_arg_name(&closure_arg.pat) {
-                        Some(search_snippet.replace(&format!("*{}", name), &name.as_str()))
+                    } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(&closure_arg.pat).kind {
+                        let name = &*ident.name.as_str();
+                        Some(search_snippet.replace(&format!("*{}", name), name))
                     } else {
                         None
                     }
@@ -3688,9 +3743,7 @@ fn lint_option_as_ref_deref<'tcx>(
                 hir::ExprKind::MethodCall(_, _, args, _) => {
                     if_chain! {
                         if args.len() == 1;
-                        if let hir::ExprKind::Path(qpath) = &args[0].kind;
-                        if let hir::def::Res::Local(local_id) = cx.qpath_res(qpath, args[0].hir_id);
-                        if closure_body.params[0].pat.hir_id == local_id;
+                        if path_to_local_id(&args[0], closure_body.params[0].pat.hir_id);
                         let adj = cx
                             .typeck_results()
                             .expr_adjustments(&args[0])
@@ -3710,10 +3763,8 @@ fn lint_option_as_ref_deref<'tcx>(
                     if_chain! {
                         if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner1) = inner.kind;
                         if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner2) = inner1.kind;
-                        if let hir::ExprKind::Path(ref qpath) = inner2.kind;
-                        if let hir::def::Res::Local(local_id) = cx.qpath_res(qpath, inner2.hir_id);
                         then {
-                            closure_body.params[0].pat.hir_id == local_id
+                            path_to_local_id(inner2, closure_body.params[0].pat.hir_id)
                         } else {
                             false
                         }
@@ -4094,20 +4145,54 @@ fn lint_from_iter(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<
         if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]);
         then {
             // `expr` implements `FromIterator` trait
-            let iter_expr = snippet(cx, args[0].span, "..");
+            let iter_expr = sugg::Sugg::hir(cx, &args[0], "..").maybe_par();
+            let turbofish = extract_turbofish(cx, expr, ty);
+            let sugg = format!("{}.collect::<{}>()", iter_expr, turbofish);
             span_lint_and_sugg(
                 cx,
                 FROM_ITER_INSTEAD_OF_COLLECT,
                 expr.span,
                 "usage of `FromIterator::from_iter`",
                 "use `.collect()` instead of `::from_iter()`",
-                format!("{}.collect()", iter_expr),
+                sugg,
                 Applicability::MaybeIncorrect,
             );
         }
     }
 }
 
+fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String {
+    if_chain! {
+        let call_site = expr.span.source_callsite();
+        if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site);
+        let snippet_split = snippet.split("::").collect::<Vec<_>>();
+        if let Some((_, elements)) = snippet_split.split_last();
+
+        then {
+            // is there a type specifier? (i.e.: like `<u32>` in `collections::BTreeSet::<u32>::`)
+            if let Some(type_specifier) = snippet_split.iter().find(|e| e.starts_with('<') && e.ends_with('>')) {
+                // remove the type specifier from the path elements
+                let without_ts = elements.iter().filter_map(|e| {
+                    if e == type_specifier { None } else { Some((*e).to_string()) }
+                }).collect::<Vec<_>>();
+                // join and add the type specifier at the end (i.e.: `collections::BTreeSet<u32>`)
+                format!("{}{}", without_ts.join("::"), type_specifier)
+            } else {
+                // type is not explicitly specified so wildcards are needed
+                // i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>`
+                let ty_str = ty.to_string();
+                let start = ty_str.find('<').unwrap_or(0);
+                let end = ty_str.find('>').unwrap_or_else(|| ty_str.len());
+                let nb_wildcard = ty_str[start..end].split(',').count();
+                let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1));
+                format!("{}<{}>", elements.join("::"), wildcards)
+            }
+        } else {
+            ty.to_string()
+        }
+    }
+}
+
 fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
     expected.constness == actual.constness
         && expected.unsafety == actual.unsafety
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
index d98e6160d30..5691fcb88e9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
@@ -1,8 +1,6 @@
-use crate::utils::paths;
 use crate::utils::usage::mutated_variables;
-use crate::utils::{match_qpath, match_trait_method, span_lint};
+use crate::utils::{match_qpath, match_trait_method, path_to_local_id, paths, span_lint};
 use rustc_hir as hir;
-use rustc_hir::def::Res;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
@@ -59,14 +57,8 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
                 if let hir::ExprKind::Path(ref path) = func.kind;
                 then {
                     if match_qpath(path, &paths::OPTION_SOME) {
-                        if_chain! {
-                            if let hir::ExprKind::Path(path) = &args[0].kind;
-                            if let Res::Local(ref local) = cx.qpath_res(path, args[0].hir_id);
-                            then {
-                                if arg_id == *local {
-                                    return (false, false)
-                                }
-                            }
+                        if path_to_local_id(&args[0], arg_id) {
+                            return (false, false)
                         }
                         return (true, false);
                     }
diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs
index 2f3cdb894f0..d7239b328bb 100644
--- a/src/tools/clippy/clippy_lints/src/mut_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs
@@ -52,7 +52,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
             return;
         }
 
-        if let Some((_, arg, body)) = higher::for_loop(expr) {
+        if let Some((_, arg, body, _)) = higher::for_loop(expr) {
             // A `for` loop lowers to:
             // ```rust
             // match ::std::iter::Iterator::next(&mut iter) {
diff --git a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
index 9e9b79ee1cf..fe8d4d07abc 100644
--- a/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_question_mark.rs
@@ -1,8 +1,6 @@
 use rustc_errors::Applicability;
-use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::ty::DefIdTree;
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
@@ -160,7 +158,7 @@ fn is_some_or_ok_call<'a>(
         // Check outer expression matches CALL_IDENT(ARGUMENT) format
         if let ExprKind::Call(path, args) = &expr.kind;
         if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind;
-        if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res);
+        if utils::is_some_ctor(cx, path.res) || utils::is_ok_ctor(cx, path.res);
 
         // Extract inner expression from ARGUMENT
         if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;
@@ -208,25 +206,3 @@ fn is_some_or_ok_call<'a>(
 fn has_implicit_error_from(cx: &LateContext<'_>, entire_expr: &Expr<'_>, inner_result_expr: &Expr<'_>) -> bool {
     return cx.typeck_results().expr_ty(entire_expr) != cx.typeck_results().expr_ty(inner_result_expr);
 }
-
-fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool {
-    if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() {
-        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
-            if let Some(variant_id) = cx.tcx.parent(id) {
-                return variant_id == ok_id;
-            }
-        }
-    }
-    false
-}
-
-fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
-    if let Some(some_id) = cx.tcx.lang_items().option_some_variant() {
-        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
-            if let Some(variant_id) = cx.tcx.parent(id) {
-                return variant_id == some_id;
-            }
-        }
-    }
-    false
-}
diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs
index 3e454eecd97..59503817c0f 100644
--- a/src/tools/clippy/clippy_lints/src/ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/ranges.rs
@@ -442,7 +442,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
         let mut cur_expr = expr;
         while let Some(parent_expr) = get_parent_expr(cx, cur_expr) {
             match higher::for_loop(parent_expr) {
-                Some((_, args, _)) if args.hir_id == expr.hir_id => return true,
+                Some((_, args, _, _)) if args.hir_id == expr.hir_id => return true,
                 _ => cur_expr = parent_expr,
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs
index d06ab143482..1edea613148 100644
--- a/src/tools/clippy/clippy_lints/src/regex.rs
+++ b/src/tools/clippy/clippy_lints/src/regex.rs
@@ -35,14 +35,16 @@ declare_clippy_lint! {
     /// `str::starts_with`, `str::ends_with` or `std::contains` or other `str`
     /// methods.
     ///
-    /// **Known problems:** None.
+    /// **Known problems:** If the same regex is going to be applied to multiple
+    /// inputs, the precomputations done by `Regex` construction can give
+    /// significantly better performance than any of the `str`-based methods.
     ///
     /// **Example:**
     /// ```ignore
     /// Regex::new("^foobar")
     /// ```
     pub TRIVIAL_REGEX,
-    style,
+    nursery,
     "trivial regular expressions"
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
new file mode 100644
index 00000000000..839c995e525
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
@@ -0,0 +1,66 @@
+use crate::utils::{in_macro, snippet_with_macro_callsite, span_lint_and_sugg, sugg};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Block, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:** Looks for blocks of expressions and fires if the last expression returns `()`
+    /// but is not followed by a semicolon.
+    ///
+    /// **Why is this bad?** The semicolon might be optional but when
+    /// extending the block with new code, it doesn't require a change in previous last line.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// fn main() {
+    ///     println!("Hello world")
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn main() {
+    ///     println!("Hello world");
+    /// }
+    /// ```
+    pub SEMICOLON_IF_NOTHING_RETURNED,
+    restriction,
+    "add a semicolon if nothing is returned"
+}
+
+declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED]);
+
+impl LateLintPass<'_> for SemicolonIfNothingReturned {
+    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
+        if_chain! {
+            if !in_macro(block.span);
+            if let Some(expr) = block.expr;
+            let t_expr = cx.typeck_results().expr_ty(expr);
+            if t_expr.is_unit();
+            if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
+            if !snippet.ends_with('}');
+            then {
+                // filter out the desugared `for` loop
+                if let ExprKind::DropTemps(..) = &expr.kind {
+                    return;
+                }
+
+                let sugg = sugg::Sugg::hir_with_macro_callsite(cx, &expr, "..");
+                let suggestion = format!("{0};", sugg);
+                span_lint_and_sugg(
+                    cx,
+                    SEMICOLON_IF_NOTHING_RETURNED,
+                    expr.span.source_callsite(),
+                    "consider adding a `;` to the last statement for consistent formatting",
+                    "add a `;` here",
+                    suggestion,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
index fa508df865e..fdd105e6246 100644
--- a/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
+++ b/src/tools/clippy/clippy_lints/src/to_string_in_display.rs
@@ -1,6 +1,5 @@
-use crate::utils::{match_def_path, match_trait_method, paths, span_lint};
+use crate::utils::{match_def_path, match_trait_method, path_to_local_id, paths, span_lint};
 use if_chain::if_chain;
-use rustc_hir::def::Res;
 use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
@@ -89,14 +88,12 @@ impl LateLintPass<'_> for ToStringInDisplay {
 
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         if_chain! {
+            if self.in_display_impl;
+            if let Some(self_hir_id) = self.self_hir_id;
             if let ExprKind::MethodCall(ref path, _, args, _) = expr.kind;
             if path.ident.name == sym!(to_string);
             if match_trait_method(cx, expr, &paths::TO_STRING);
-            if self.in_display_impl;
-            if let ExprKind::Path(ref qpath) = args[0].kind;
-            if let Res::Local(hir_id) = cx.qpath_res(qpath, args[0].hir_id);
-            if let Some(self_hir_id) = self.self_hir_id;
-            if hir_id == self_hir_id;
+            if path_to_local_id(&args[0], self_hir_id);
             then {
                 span_lint(
                     cx,
diff --git a/src/tools/clippy/clippy_lints/src/unused_self.rs b/src/tools/clippy/clippy_lints/src/unused_self.rs
index 5349c4f7eb8..9d61bd0cc2f 100644
--- a/src/tools/clippy/clippy_lints/src/unused_self.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_self.rs
@@ -1,12 +1,10 @@
 use if_chain::if_chain;
-use rustc_hir::def::Res;
-use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
-use rustc_hir::{HirId, Impl, ImplItem, ImplItemKind, ItemKind, Path};
+use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::hir::map::Map;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 use crate::utils::span_lint_and_help;
+use crate::utils::visitors::LocalUsedVisitor;
 
 declare_clippy_lint! {
     /// **What it does:** Checks methods that contain a `self` argument but don't use it
@@ -57,13 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
             then {
                 let self_param = &body.params[0];
                 let self_hir_id = self_param.pat.hir_id;
-                let mut visitor = UnusedSelfVisitor {
-                    cx,
-                    uses_self: false,
-                    self_hir_id: &self_hir_id,
-                };
-                visitor.visit_body(body);
-                if !visitor.uses_self {
+                if !LocalUsedVisitor::new(cx, self_hir_id).check_body(body) {
                     span_lint_and_help(
                         cx,
                         UNUSED_SELF,
@@ -78,28 +70,3 @@ impl<'tcx> LateLintPass<'tcx> for UnusedSelf {
         }
     }
 }
-
-struct UnusedSelfVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
-    uses_self: bool,
-    self_hir_id: &'a HirId,
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for UnusedSelfVisitor<'a, 'tcx> {
-    type Map = Map<'tcx>;
-
-    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
-        if self.uses_self {
-            // This function already uses `self`
-            return;
-        }
-        if let Res::Local(hir_id) = &path.res {
-            self.uses_self = self.self_hir_id == hir_id
-        }
-        walk_path(self, path);
-    }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index b5a8300376c..7d7b35c2168 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -169,10 +169,12 @@ define_Conf! {
     (max_fn_params_bools, "max_fn_params_bools": u64, 3),
     /// Lint: WILDCARD_IMPORTS. Whether to allow certain wildcard imports (prelude, super in tests).
     (warn_on_all_wildcard_imports, "warn_on_all_wildcard_imports": bool, false),
-    /// Lint: DISALLOWED_METHOD. The list of blacklisted methods to lint about. NB: `bar` is not here since it has legitimate uses
+    /// Lint: DISALLOWED_METHOD. The list of disallowed methods, written as fully qualified paths.
     (disallowed_methods, "disallowed_methods": Vec<String>, Vec::<String>::new()),
     /// Lint: UNREADABLE_LITERAL. Should the fraction of a decimal be linted to include separators.
     (unreadable_literal_lint_fractions, "unreadable_literal_lint_fractions": bool, true),
+    /// Lint: _CARGO_COMMON_METADATA. For internal testing only, ignores the current `publish` settings in the Cargo manifest.
+    (cargo_ignore_publish, "cargo_ignore_publish": bool, false),
 }
 
 impl Default for Conf {
diff --git a/src/tools/clippy/clippy_lints/src/utils/higher.rs b/src/tools/clippy/clippy_lints/src/utils/higher.rs
index 145703d1bdc..1cf1aa363d5 100644
--- a/src/tools/clippy/clippy_lints/src/utils/higher.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/higher.rs
@@ -9,6 +9,7 @@ use rustc_ast::ast;
 use rustc_hir as hir;
 use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp};
 use rustc_lint::LateContext;
+use rustc_span::source_map::Span;
 
 /// Converts a hir binary operator to the corresponding `ast` type.
 #[must_use]
@@ -133,11 +134,11 @@ pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
     false
 }
 
-/// Recover the essential nodes of a desugared for loop:
-/// `for pat in arg { body }` becomes `(pat, arg, body)`.
+/// Recover the essential nodes of a desugared for loop as well as the entire span:
+/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
 pub fn for_loop<'tcx>(
     expr: &'tcx hir::Expr<'tcx>,
-) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> {
+) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>, Span)> {
     if_chain! {
         if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
         if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind;
@@ -148,7 +149,7 @@ pub fn for_loop<'tcx>(
         if let hir::StmtKind::Local(ref local) = let_stmt.kind;
         if let hir::StmtKind::Expr(ref expr) = body.kind;
         then {
-            return Some((&*local.pat, &iterargs[0], expr));
+            return Some((&*local.pat, &iterargs[0], expr, arms[0].span));
         }
     }
     None
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
index ff3be590317..d8c602fab22 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -841,15 +841,13 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
     // implementations of native types. Check lang items.
     let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
     let lang_items = cx.tcx.lang_items();
-    for lang_item in lang_items.items() {
-        if let Some(def_id) = lang_item {
-            let lang_item_path = cx.get_def_path(*def_id);
-            if path_syms.starts_with(&lang_item_path) {
-                if let [item] = &path_syms[lang_item_path.len()..] {
-                    for child in cx.tcx.item_children(*def_id) {
-                        if child.ident.name == *item {
-                            return true;
-                        }
+    for item_def_id in lang_items.items().iter().flatten() {
+        let lang_item_path = cx.get_def_path(*item_def_id);
+        if path_syms.starts_with(&lang_item_path) {
+            if let [item] = &path_syms[lang_item_path.len()..] {
+                for child in cx.tcx.item_children(*item_def_id) {
+                    if child.ident.name == *item {
+                        return true;
                     }
                 }
             }
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index de1538233a8..fafa1400156 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -36,7 +36,7 @@ use rustc_ast::ast::{self, Attribute, LitKind};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_hir::Node;
@@ -49,7 +49,7 @@ use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::exports::Export;
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, layout::IntegerExt, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable};
 use rustc_semver::RustcVersion;
 use rustc_session::Session;
 use rustc_span::hygiene::{ExpnKind, MacroKind};
@@ -306,6 +306,22 @@ pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
         .all(|(a, b)| a.ident.name.as_str() == *b)
 }
 
+/// If the expression is a path to a local, returns the canonical `HirId` of the local.
+pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
+    if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
+        if let Res::Local(id) = path.res {
+            return Some(id);
+        }
+    }
+    None
+}
+
+/// Returns true if the expression is a path to a local with the specified `HirId`.
+/// Use this function to see if an expression matches a function argument or a match binding.
+pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
+    path_to_local(expr) == Some(id)
+}
+
 /// Gets the definition associated to a path.
 #[allow(clippy::shadow_unrelated)] // false positive #6563
 pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
@@ -1134,9 +1150,7 @@ pub fn is_try<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
             if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pat.kind;
             if match_qpath(path, &paths::RESULT_OK[1..]);
             if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
-            if let ExprKind::Path(QPath::Resolved(None, ref path)) = arm.body.kind;
-            if let Res::Local(lid) = path.res;
-            if lid == hir_id;
+            if path_to_local_id(arm.body, hir_id);
             then {
                 return true;
             }
@@ -1180,12 +1194,11 @@ pub fn is_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool
     cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
 }
 
-pub fn get_arg_name(pat: &Pat<'_>) -> Option<Symbol> {
-    match pat.kind {
-        PatKind::Binding(.., ident, None) => Some(ident.name),
-        PatKind::Ref(ref subpat, _) => get_arg_name(subpat),
-        _ => None,
+pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
+    while let PatKind::Ref(subpat, _) = pat.kind {
+        pat = subpat;
     }
+    pat
 }
 
 pub fn int_bits(tcx: TyCtxt<'_>, ity: ty::IntTy) -> u64 {
@@ -1702,6 +1715,30 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
     }
 }
 
+/// Check if the resolution of a given path is an `Ok` variant of `Result`.
+pub fn is_ok_ctor(cx: &LateContext<'_>, res: Res) -> bool {
+    if let Some(ok_id) = cx.tcx.lang_items().result_ok_variant() {
+        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
+            if let Some(variant_id) = cx.tcx.parent(id) {
+                return variant_id == ok_id;
+            }
+        }
+    }
+    false
+}
+
+/// Check if the resolution of a given path is a `Some` variant of `Option`.
+pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
+    if let Some(some_id) = cx.tcx.lang_items().option_some_variant() {
+        if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
+            if let Some(variant_id) = cx.tcx.parent(id) {
+                return variant_id == some_id;
+            }
+        }
+    }
+    false
+}
+
 #[cfg(test)]
 mod test {
     use super::{reindent_multiline, without_block_comments};
diff --git a/src/tools/clippy/clippy_lints/src/utils/usage.rs b/src/tools/clippy/clippy_lints/src/utils/usage.rs
index fc0db7f64ec..7c7580a2c66 100644
--- a/src/tools/clippy/clippy_lints/src/utils/usage.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/usage.rs
@@ -1,16 +1,14 @@
 use crate::utils;
-use crate::utils::match_var;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::intravisit;
-use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
 use rustc_hir::{Expr, ExprKind, HirId, Path};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty;
-use rustc_span::symbol::{Ident, Symbol};
 use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 
 /// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
@@ -81,36 +79,6 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
     }
 }
 
-pub struct UsedVisitor {
-    pub var: Symbol, // var to look for
-    pub used: bool,  // has the var been used otherwise?
-}
-
-impl<'tcx> Visitor<'tcx> for UsedVisitor {
-    type Map = Map<'tcx>;
-
-    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if match_var(expr, self.var) {
-            self.used = true;
-        } else {
-            walk_expr(self, expr);
-        }
-    }
-
-    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
-    }
-}
-
-pub fn is_unused<'tcx>(ident: &'tcx Ident, body: &'tcx Expr<'_>) -> bool {
-    let mut visitor = UsedVisitor {
-        var: ident.name,
-        used: false,
-    };
-    walk_expr(&mut visitor, body);
-    !visitor.used
-}
-
 pub struct ParamBindingIdCollector {
     binding_hir_ids: Vec<hir::HirId>,
 }
diff --git a/src/tools/clippy/clippy_lints/src/utils/visitors.rs b/src/tools/clippy/clippy_lints/src/utils/visitors.rs
index ebf69df31ca..085c1f9c0cb 100644
--- a/src/tools/clippy/clippy_lints/src/utils/visitors.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/visitors.rs
@@ -1,7 +1,7 @@
+use crate::utils::path_to_local_id;
 use rustc_hir as hir;
-use rustc_hir::def::Res;
 use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Arm, Expr, ExprKind, HirId, QPath, Stmt};
+use rustc_hir::{Arm, Body, Expr, HirId, Stmt};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
 
@@ -133,14 +133,16 @@ where
     }
 }
 
-pub struct LocalUsedVisitor {
+pub struct LocalUsedVisitor<'hir> {
+    hir: Map<'hir>,
     pub local_hir_id: HirId,
     pub used: bool,
 }
 
-impl LocalUsedVisitor {
-    pub fn new(local_hir_id: HirId) -> Self {
+impl<'hir> LocalUsedVisitor<'hir> {
+    pub fn new(cx: &LateContext<'hir>, local_hir_id: HirId) -> Self {
         Self {
+            hir: cx.tcx.hir(),
             local_hir_id,
             used: false,
         }
@@ -151,35 +153,38 @@ impl LocalUsedVisitor {
         std::mem::replace(&mut self.used, false)
     }
 
-    pub fn check_arm(&mut self, arm: &Arm<'_>) -> bool {
+    pub fn check_arm(&mut self, arm: &'hir Arm<'_>) -> bool {
         self.check(arm, Self::visit_arm)
     }
 
-    pub fn check_expr(&mut self, expr: &Expr<'_>) -> bool {
+    pub fn check_body(&mut self, body: &'hir Body<'_>) -> bool {
+        self.check(body, Self::visit_body)
+    }
+
+    pub fn check_expr(&mut self, expr: &'hir Expr<'_>) -> bool {
         self.check(expr, Self::visit_expr)
     }
 
-    pub fn check_stmt(&mut self, stmt: &Stmt<'_>) -> bool {
+    pub fn check_stmt(&mut self, stmt: &'hir Stmt<'_>) -> bool {
         self.check(stmt, Self::visit_stmt)
     }
 }
 
-impl<'v> Visitor<'v> for LocalUsedVisitor {
+impl<'v> Visitor<'v> for LocalUsedVisitor<'v> {
     type Map = Map<'v>;
 
     fn visit_expr(&mut self, expr: &'v Expr<'v>) {
-        if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
-            if let Res::Local(id) = path.res {
-                if id == self.local_hir_id {
-                    self.used = true;
-                    return;
-                }
-            }
+        if self.used {
+            return;
+        }
+        if path_to_local_id(expr, self.local_hir_id) {
+            self.used = true;
+        } else {
+            walk_expr(self, expr);
         }
-        walk_expr(self, expr);
     }
 
     fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
-        NestedVisitorMap::None
+        NestedVisitorMap::OnlyBodies(self.hir)
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index 149cceb39dd..c132e4de4f6 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -55,7 +55,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
 
         // search for `for _ in vec![…]`
         if_chain! {
-            if let Some((_, arg, _)) = higher::for_loop(expr);
+            if let Some((_, arg, _, _)) = higher::for_loop(expr);
             if let Some(vec_args) = higher::vec_macro(cx, arg);
             if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg)));
             then {