]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/matches.rs
Auto merge of #7138 - mgacek8:issue6808_iter_cloned_collect_FN_with_large_array,...
[rust.git] / clippy_lints / src / matches.rs
index 425124b78f4776d7302e87e25bbda033573cc0f0..13b2a834b0a962ac4db369573dab48ddfc9bda16 100644 (file)
@@ -7,19 +7,21 @@
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
 use clippy_utils::visitors::LocalUsedVisitor;
 use clippy_utils::{
-    get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local,
-    path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs,
+    get_parent_expr, in_macro, is_allowed, is_expn_of, is_lang_ctor, is_refutable, is_wild, meets_msrv, msrvs,
+    path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
+    strip_pat_refs,
 };
 use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::Applicability;
 use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{
     self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
     Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
 };
+use rustc_hir::{HirIdMap, HirIdSet};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty, TyS, VariantDef};
@@ -29,6 +31,7 @@
 use rustc_span::sym;
 use std::cmp::Ordering;
 use std::collections::hash_map::Entry;
+use std::iter;
 use std::ops::Bound;
 
 declare_clippy_lint! {
     /// **Why is this bad?** It's more concise and clear to just use the proper
     /// utility function
     ///
-    /// **Known problems:** None.
+    /// **Known problems:** This will change the drop order for the matched type. Both `if let` and
+    /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
+    /// value before entering the block. For most types this change will not matter, but for a few
+    /// types this will not be an acceptable change (e.g. locks). See the
+    /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
+    /// drop order.
     ///
     /// **Example:**
     ///
@@ -571,8 +579,6 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
     MATCH_SAME_ARMS,
 ]);
 
-const MATCH_LIKE_MATCHES_MACRO_MSRV: RustcVersion = RustcVersion::new(1, 42, 0);
-
 impl<'tcx> LateLintPass<'tcx> for Matches {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
@@ -581,7 +587,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 
         redundant_pattern_match::check(cx, expr);
 
-        if meets_msrv(self.msrv.as_ref(), &MATCH_LIKE_MATCHES_MACRO_MSRV) {
+        if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
             if !check_match_like_matches(cx, expr) {
                 lint_match_arms(cx, expr);
             }
@@ -589,7 +595,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             lint_match_arms(cx, expr);
         }
 
-        if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
+        if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
             check_single_match(cx, ex, arms, expr);
             check_match_bool(cx, ex, arms, expr);
             check_overlapping_arms(cx, ex, arms);
@@ -604,7 +610,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 check_match_single_binding(cx, ex, arms, expr);
             }
         }
-        if let ExprKind::Match(ref ex, ref arms, _) = expr.kind {
+        if let ExprKind::Match(ex, arms, _) = expr.kind {
             check_match_ref_pats(cx, ex, arms, expr);
         }
     }
@@ -613,14 +619,14 @@ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
         if_chain! {
             if !in_external_macro(cx.sess(), local.span);
             if !in_macro(local.span);
-            if let Some(ref expr) = local.init;
-            if let ExprKind::Match(ref target, ref arms, MatchSource::Normal) = expr.kind;
+            if let Some(expr) = local.init;
+            if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
             if arms.len() == 1 && arms[0].guard.is_none();
             if let PatKind::TupleStruct(
-                QPath::Resolved(None, ref variant_name), ref args, _) = arms[0].pat.kind;
+                QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
             if args.len() == 1;
-            if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
-            let body = remove_blocks(&arms[0].body);
+            if let PatKind::Binding(_, arg, ..) = strip_pat_refs(args[0]).kind;
+            let body = remove_blocks(arms[0].body);
             if path_to_local_id(body, arg);
 
             then {
@@ -649,7 +655,7 @@ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
         if_chain! {
             if !in_external_macro(cx.sess(), pat.span);
             if !in_macro(pat.span);
-            if let PatKind::Struct(QPath::Resolved(_, ref path), fields, true) = pat.kind;
+            if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
             if let Some(def_id) = path.res.opt_def_id();
             let ty = cx.tcx.type_of(def_id);
             if let ty::Adt(def, _) = ty.kind();
@@ -732,12 +738,15 @@ fn report_single_match_single_pattern(
         format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
     });
 
+    let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
     let (msg, sugg) = if_chain! {
-        let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
         if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
         let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
-        if let Some(trait_id) = cx.tcx.lang_items().structural_peq_trait();
-        if ty.is_integral() || ty.is_char() || ty.is_str() || implements_trait(cx, ty, trait_id, &[]);
+        if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
+        if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
+        if ty.is_integral() || ty.is_char() || ty.is_str()
+            || (implements_trait(cx, ty, spe_trait_id, &[])
+                && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
         then {
             // scrutinee derives PartialEq and the pattern is a constant.
             let pat_ref_count = match pat.kind {
@@ -761,7 +770,7 @@ fn report_single_match_single_pattern(
                 // PartialEq for different reference counts may not exist.
                 "&".repeat(ref_count_diff),
                 snippet(cx, arms[0].pat.span, ".."),
-                expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
+                expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
                 els_str,
             );
             (msg, sugg)
@@ -771,7 +780,7 @@ fn report_single_match_single_pattern(
                 "if let {} = {} {}{}",
                 snippet(cx, arms[0].pat.span, ".."),
                 snippet(cx, ex.span, ".."),
-                expr_block(cx, &arms[0].body, None, "..", Some(expr.span)),
+                expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
                 els_str,
             );
             (msg, sugg)
@@ -809,7 +818,7 @@ fn check_single_match_opt_like(
     ];
 
     let path = match arms[1].pat.kind {
-        PatKind::TupleStruct(ref path, ref inner, _) => {
+        PatKind::TupleStruct(ref path, inner, _) => {
             // Contains any non wildcard patterns (e.g., `Err(err)`)?
             if !inner.iter().all(is_wild) {
                 return;
@@ -841,7 +850,7 @@ fn check_match_bool(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
             move |diag| {
                 if arms.len() == 2 {
                     // no guards
-                    let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pat.kind {
+                    let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
                         if let ExprKind::Lit(ref lit) = arm_bool.kind {
                             match lit.node {
                                 LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
@@ -917,14 +926,14 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm
     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 {
-            if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pat.kind {
+            if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
                 let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
                 if path_str == "Err" {
                     let mut matching_wild = inner.iter().any(is_wild);
                     let mut ident_bind_name = String::from("_");
                     if !matching_wild {
                         // Looking for unused bindings (i.e.: `_e`)
-                        inner.iter().for_each(|pat| {
+                        for pat in inner.iter() {
                             if let PatKind::Binding(_, id, ident, None) = pat.kind {
                                 if ident.as_str().starts_with('_')
                                     && !LocalUsedVisitor::new(cx, id).check_expr(arm.body)
@@ -933,11 +942,11 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm
                                     matching_wild = true;
                                 }
                             }
-                        });
+                        }
                     }
                     if_chain! {
                         if matching_wild;
-                        if let ExprKind::Block(ref block, _) = arm.body.kind;
+                        if let ExprKind::Block(block, _) = arm.body.kind;
                         if is_panic_block(block);
                         then {
                             // `Err(_)` or `Err(_e)` arm with `panic!` found
@@ -983,6 +992,11 @@ fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
     }
 }
 
+fn is_doc_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
+    let attrs = cx.tcx.get_attrs(variant_def.def_id);
+    clippy_utils::attrs::is_doc_hidden(attrs)
+}
+
 #[allow(clippy::too_many_lines)]
 fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
     let ty = cx.typeck_results().expr_ty(ex).peel_refs();
@@ -1041,16 +1055,18 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
                     path
                 },
                 PatKind::TupleStruct(path, patterns, ..) => {
-                    if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
-                        let id = cx.qpath_res(path, pat.hir_id).def_id();
-                        missing_variants.retain(|e| e.ctor_def_id != Some(id));
+                    if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
+                        if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
+                            missing_variants.retain(|e| e.ctor_def_id != Some(id));
+                        }
                     }
                     path
                 },
                 PatKind::Struct(path, patterns, ..) => {
-                    if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
-                        let id = cx.qpath_res(path, pat.hir_id).def_id();
-                        missing_variants.retain(|e| e.def_id != id);
+                    if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
+                        if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
+                            missing_variants.retain(|e| e.def_id != id);
+                        }
                     }
                     path
                 },
@@ -1102,7 +1118,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
 
     match missing_variants.as_slice() {
         [] => (),
-        [x] if !adt_def.is_variant_list_non_exhaustive() => span_lint_and_sugg(
+        [x] if !adt_def.is_variant_list_non_exhaustive() && !is_doc_hidden(cx, x) => span_lint_and_sugg(
             cx,
             MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
             wildcard_span,
@@ -1112,7 +1128,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
             Applicability::MaybeIncorrect,
         ),
         variants => {
-            let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect();
+            let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
             let message = if adt_def.is_variant_list_non_exhaustive() {
                 suggestions.push("_".into());
                 "wildcard matches known variants and will also match future added variants"
@@ -1136,9 +1152,7 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
 // If the block contains only a `panic!` macro (as expression or statement)
 fn is_panic_block(block: &Block<'_>) -> bool {
     match (&block.expr, block.stmts.len(), block.stmts.first()) {
-        (&Some(ref exp), 0, _) => {
-            is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none()
-        },
+        (&Some(exp), 0, _) => is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none(),
         (&None, 1, Some(stmt)) => {
             is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none()
         },
@@ -1149,7 +1163,7 @@ fn is_panic_block(block: &Block<'_>) -> bool {
 fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
     if has_only_ref_pats(arms) {
         let mut suggs = Vec::with_capacity(arms.len() + 1);
-        let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind {
+        let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
             let span = ex.span.source_callsite();
             suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
             (
@@ -1166,7 +1180,7 @@ fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], e
         };
 
         suggs.extend(arms.iter().filter_map(|a| {
-            if let PatKind::Ref(ref refp, _) = a.pat.kind {
+            if let PatKind::Ref(refp, _) = a.pat.kind {
                 Some((a.pat.span, snippet(cx, refp.span, "..").to_string()))
             } else {
                 None
@@ -1183,10 +1197,10 @@ fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], e
 
 fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
     if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
-        let arm_ref: Option<BindingAnnotation> = if is_none_arm(&arms[0]) {
-            is_ref_some_arm(&arms[1])
-        } else if is_none_arm(&arms[1]) {
-            is_ref_some_arm(&arms[0])
+        let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
+            is_ref_some_arm(cx, &arms[1])
+        } else if is_none_arm(cx, &arms[1]) {
+            is_ref_some_arm(cx, &arms[0])
         } else {
             None
         };
@@ -1235,7 +1249,7 @@ fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
 
 fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
     for arm in arms {
-        if let PatKind::Or(ref fields) = arm.pat.kind {
+        if let PatKind::Or(fields) = arm.pat.kind {
             // look for multiple fields in this arm that contains at least one Wild pattern
             if fields.len() > 1 && fields.iter().any(is_wild) {
                 span_lint_and_help(
@@ -1301,7 +1315,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
             // strip potential borrows (#6503), but only if the type is a reference
             let mut ex_new = ex;
             if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
-                if let ty::Ref(..) = cx.typeck_results().expr_ty(&ex_inner).kind() {
+                if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
                     ex_new = ex_inner;
                 }
             };
@@ -1378,7 +1392,7 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A
 
     let matched_vars = ex.span;
     let bind_names = arms[0].pat.span;
-    let match_body = remove_blocks(&arms[0].body);
+    let match_body = remove_blocks(arms[0].body);
     let mut snippet_body = if match_body.span.from_expansion() {
         Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
     } else {
@@ -1389,13 +1403,13 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A
     match match_body.kind {
         ExprKind::Block(block, _) => {
             // macro + expr_ty(body) == ()
-            if block.span.from_expansion() && cx.typeck_results().expr_ty(&match_body).is_unit() {
+            if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
                 snippet_body.push(';');
             }
         },
         _ => {
             // expr_ty(body) == ()
-            if cx.typeck_results().expr_ty(&match_body).is_unit() {
+            if cx.typeck_results().expr_ty(match_body).is_unit() {
                 snippet_body.push(';');
             }
         },
@@ -1480,8 +1494,8 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A
 
 /// Returns true if the `ex` match expression is in a local (`let`) statement
 fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
+    let map = &cx.tcx.hir();
     if_chain! {
-        let map = &cx.tcx.hir();
         if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
         if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
         then {
@@ -1494,11 +1508,8 @@ fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'
 /// Gets all arms that are unbounded `PatRange`s.
 fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<Constant>> {
     arms.iter()
-        .flat_map(|arm| {
-            if let Arm {
-                ref pat, guard: None, ..
-            } = *arm
-            {
+        .filter_map(|arm| {
+            if let Arm { pat, guard: None, .. } = *arm {
                 if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
                     let lhs = match lhs {
                         Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
@@ -1518,7 +1529,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
                     });
                 }
 
-                if let PatKind::Lit(ref value) = pat.kind {
+                if let PatKind::Lit(value) = pat.kind {
                     let value = constant(cx, cx.typeck_results(), value)?.0;
                     return Some(SpannedRange {
                         span: pat.span,
@@ -1565,28 +1576,28 @@ fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
 
 fn is_unit_expr(expr: &Expr<'_>) -> bool {
     match expr.kind {
-        ExprKind::Tup(ref v) if v.is_empty() => true,
-        ExprKind::Block(ref b, _) if b.stmts.is_empty() && b.expr.is_none() => true,
+        ExprKind::Tup(v) if v.is_empty() => true,
+        ExprKind::Block(b, _) if b.stmts.is_empty() && b.expr.is_none() => true,
         _ => false,
     }
 }
 
 // Checks if arm has the form `None => None`
-fn is_none_arm(arm: &Arm<'_>) -> bool {
-    matches!(arm.pat.kind, PatKind::Path(ref path) if match_qpath(path, &paths::OPTION_NONE))
+fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+    matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
 }
 
 // Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
-fn is_ref_some_arm(arm: &Arm<'_>) -> Option<BindingAnnotation> {
+fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
     if_chain! {
-        if let PatKind::TupleStruct(ref path, ref pats, _) = arm.pat.kind;
-        if pats.len() == 1 && match_qpath(path, &paths::OPTION_SOME);
+        if let PatKind::TupleStruct(ref qpath, pats, _) = arm.pat.kind;
+        if is_lang_ctor(cx, qpath, OptionSome);
         if let PatKind::Binding(rb, .., ident, _) = pats[0].kind;
         if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
-        if let ExprKind::Call(ref e, ref args) = remove_blocks(&arm.body).kind;
+        if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
         if let ExprKind::Path(ref some_path) = e.kind;
-        if match_qpath(some_path, &paths::OPTION_SOME) && args.len() == 1;
-        if let ExprKind::Path(QPath::Resolved(_, ref path2)) = args[0].kind;
+        if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
+        if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
         if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
         then {
             return Some(rb)
@@ -1668,7 +1679,7 @@ fn cmp(&self, other: &Self) -> Ordering {
 
     values.sort();
 
-    for (a, b) in values.iter().zip(values.iter().skip(1)) {
+    for (a, b) in iter::zip(&values, values.iter().skip(1)) {
         match (a, b) {
             (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
                 if ra.node != rb.node {
@@ -1678,7 +1689,7 @@ fn cmp(&self, other: &Self) -> Ordering {
             (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
             _ => {
                 // skip if the range `a` is completely included into the range `b`
-                if let Ordering::Equal | Ordering::Less = a.cmp(&b) {
+                if let Ordering::Equal | Ordering::Less = a.cmp(b) {
                     let kind_a = Kind::End(a.range().node.1, a.range());
                     let kind_b = Kind::End(b.range().node.1, b.range());
                     if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) {
@@ -1696,54 +1707,206 @@ fn cmp(&self, other: &Self) -> Ordering {
 mod redundant_pattern_match {
     use super::REDUNDANT_PATTERN_MATCHING;
     use clippy_utils::diagnostics::span_lint_and_then;
-    use clippy_utils::source::snippet;
-    use clippy_utils::{is_trait_method, match_qpath, paths};
+    use clippy_utils::source::{snippet, snippet_with_applicability};
+    use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
+    use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
     use if_chain::if_chain;
     use rustc_ast::ast::LitKind;
     use rustc_errors::Applicability;
-    use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind, QPath};
+    use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
+    use rustc_hir::{
+        intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
+        Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, PatKind, QPath,
+    };
     use rustc_lint::LateContext;
+    use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
     use rustc_span::sym;
 
     pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
             match match_source {
                 MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
-                MatchSource::IfLetDesugar { .. } => find_sugg_for_if_let(cx, expr, op, arms, "if"),
-                MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, arms, "while"),
+                MatchSource::IfLetDesugar { contains_else_clause } => {
+                    find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause)
+                },
+                MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "while", false),
                 _ => {},
             }
         }
     }
 
+    /// Checks if the drop order for a type matters. Some std types implement drop solely to
+    /// deallocate memory. For these types, and composites containing them, changing the drop order
+    /// won't result in any observable side effects.
+    fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+        if !ty.needs_drop(cx.tcx, cx.param_env) {
+            false
+        } else if !cx
+            .tcx
+            .lang_items()
+            .drop_trait()
+            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+        {
+            // This type doesn't implement drop, so no side effects here.
+            // Check if any component type has any.
+            match ty.kind() {
+                ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop(cx, ty)),
+                ty::Array(ty, _) => type_needs_ordered_drop(cx, ty),
+                ty::Adt(adt, subs) => adt
+                    .all_fields()
+                    .map(|f| f.ty(cx.tcx, subs))
+                    .any(|ty| type_needs_ordered_drop(cx, ty)),
+                _ => true,
+            }
+        }
+        // Check for std types which implement drop, but only for memory allocation.
+        else if is_type_diagnostic_item(cx, ty, sym::vec_type)
+            || is_type_lang_item(cx, ty, LangItem::OwnedBox)
+            || is_type_diagnostic_item(cx, ty, sym::Rc)
+            || is_type_diagnostic_item(cx, ty, sym::Arc)
+            || is_type_diagnostic_item(cx, ty, sym::cstring_type)
+            || match_type(cx, ty, &paths::BTREEMAP)
+            || match_type(cx, ty, &paths::LINKED_LIST)
+            || match_type(cx, ty, &paths::WEAK_RC)
+            || match_type(cx, ty, &paths::WEAK_ARC)
+        {
+            // Check all of the generic arguments.
+            if let ty::Adt(_, subs) = ty.kind() {
+                subs.types().any(|ty| type_needs_ordered_drop(cx, ty))
+            } else {
+                true
+            }
+        } else {
+            true
+        }
+    }
+
+    // Extract the generic arguments out of a type
+    fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
+        if_chain! {
+            if let ty::Adt(_, subs) = ty.kind();
+            if let Some(sub) = subs.get(index);
+            if let GenericArgKind::Type(sub_ty) = sub.unpack();
+            then {
+                Some(sub_ty)
+            } else {
+                None
+            }
+        }
+    }
+
+    // Checks if there are any temporaries created in the given expression for which drop order
+    // matters.
+    fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+        struct V<'a, 'tcx> {
+            cx: &'a LateContext<'tcx>,
+            res: bool,
+        }
+        impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
+            type Map = ErasedMap<'tcx>;
+            fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+                NestedVisitorMap::None
+            }
+
+            fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+                match expr.kind {
+                    // Taking the reference of a value leaves a temporary
+                    // e.g. In `&String::new()` the string is a temporary value.
+                    // Remaining fields are temporary values
+                    // e.g. In `(String::new(), 0).1` the string is a temporary value.
+                    ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
+                        if !matches!(expr.kind, ExprKind::Path(_)) {
+                            if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
+                                self.res = true;
+                            } else {
+                                self.visit_expr(expr);
+                            }
+                        }
+                    },
+                    // the base type is alway taken by reference.
+                    // e.g. In `(vec![0])[0]` the vector is a temporary value.
+                    ExprKind::Index(base, index) => {
+                        if !matches!(base.kind, ExprKind::Path(_)) {
+                            if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
+                                self.res = true;
+                            } else {
+                                self.visit_expr(base);
+                            }
+                        }
+                        self.visit_expr(index);
+                    },
+                    // Method calls can take self by reference.
+                    // e.g. In `String::new().len()` the string is a temporary value.
+                    ExprKind::MethodCall(_, _, [self_arg, args @ ..], _) => {
+                        if !matches!(self_arg.kind, ExprKind::Path(_)) {
+                            let self_by_ref = self
+                                .cx
+                                .typeck_results()
+                                .type_dependent_def_id(expr.hir_id)
+                                .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
+                            if self_by_ref
+                                && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg))
+                            {
+                                self.res = true;
+                            } else {
+                                self.visit_expr(self_arg)
+                            }
+                        }
+                        args.iter().for_each(|arg| self.visit_expr(arg));
+                    },
+                    // Either explicitly drops values, or changes control flow.
+                    ExprKind::DropTemps(_)
+                    | ExprKind::Ret(_)
+                    | ExprKind::Break(..)
+                    | ExprKind::Yield(..)
+                    | ExprKind::Block(Block { expr: None, .. }, _)
+                    | ExprKind::Loop(..) => (),
+
+                    // Only consider the final expression.
+                    ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
+
+                    _ => walk_expr(self, expr),
+                }
+            }
+        }
+
+        let mut v = V { cx, res: false };
+        v.visit_expr(expr);
+        v.res
+    }
+
     fn find_sugg_for_if_let<'tcx>(
         cx: &LateContext<'tcx>,
         expr: &'tcx Expr<'_>,
-        op: &Expr<'_>,
-        arms: &[Arm<'_>],
+        op: &'tcx Expr<'tcx>,
+        arm: &Arm<'_>,
         keyword: &'static str,
+        has_else: bool,
     ) {
         // also look inside refs
-        let mut kind = &arms[0].pat.kind;
+        let mut kind = &arm.pat.kind;
         // if we have &None for example, peel it so we can detect "if let None = x"
         if let PatKind::Ref(inner, _mutability) = kind {
             kind = &inner.kind;
         }
-        let good_method = match kind {
-            PatKind::TupleStruct(ref path, ref patterns, _) if patterns.len() == 1 => {
-                if let PatKind::Wild = patterns[0].kind {
-                    if match_qpath(path, &paths::RESULT_OK) {
-                        "is_ok()"
-                    } else if match_qpath(path, &paths::RESULT_ERR) {
-                        "is_err()"
-                    } else if match_qpath(path, &paths::OPTION_SOME) {
-                        "is_some()"
-                    } else if match_qpath(path, &paths::POLL_READY) {
-                        "is_ready()"
-                    } else if match_qpath(path, &paths::IPADDR_V4) {
-                        "is_ipv4()"
-                    } else if match_qpath(path, &paths::IPADDR_V6) {
-                        "is_ipv6()"
+        let op_ty = cx.typeck_results().expr_ty(op);
+        // Determine which function should be used, and the type contained by the corresponding
+        // variant.
+        let (good_method, inner_ty) = match kind {
+            PatKind::TupleStruct(ref path, [sub_pat], _) => {
+                if let PatKind::Wild = sub_pat.kind {
+                    if is_lang_ctor(cx, path, ResultOk) {
+                        ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
+                    } else if is_lang_ctor(cx, path, ResultErr) {
+                        ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
+                    } else if is_lang_ctor(cx, path, OptionSome) {
+                        ("is_some()", op_ty)
+                    } else if is_lang_ctor(cx, path, PollReady) {
+                        ("is_ready()", op_ty)
+                    } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) {
+                        ("is_ipv4()", op_ty)
+                    } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) {
+                        ("is_ipv6()", op_ty)
                     } else {
                         return;
                     }
@@ -1752,17 +1915,36 @@ fn find_sugg_for_if_let<'tcx>(
                 }
             },
             PatKind::Path(ref path) => {
-                if match_qpath(path, &paths::OPTION_NONE) {
+                let method = if is_lang_ctor(cx, path, OptionNone) {
                     "is_none()"
-                } else if match_qpath(path, &paths::POLL_PENDING) {
+                } else if is_lang_ctor(cx, path, PollPending) {
                     "is_pending()"
                 } else {
                     return;
-                }
+                };
+                // `None` and `Pending` don't have an inner type.
+                (method, cx.tcx.types.unit)
             },
             _ => return,
         };
 
+        // If this is the last expression in a block or there is an else clause then the whole
+        // type needs to be considered, not just the inner type of the branch being matched on.
+        // Note the last expression in a block is dropped after all local bindings.
+        let check_ty = if has_else
+            || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
+        {
+            op_ty
+        } else {
+            inner_ty
+        };
+
+        // All temporaries created in the scrutinee expression are dropped at the same time as the
+        // scrutinee would be, so they have to be considered as well.
+        // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
+        // for the duration if body.
+        let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, op);
+
         // check that `while_let_on_iterator` lint does not trigger
         if_chain! {
             if keyword == "while";
@@ -1781,7 +1963,7 @@ fn find_sugg_for_if_let<'tcx>(
         span_lint_and_then(
             cx,
             REDUNDANT_PATTERN_MATCHING,
-            arms[0].pat.span,
+            arm.pat.span,
             &format!("redundant pattern matching, consider using `{}`", good_method),
             |diag| {
                 // while let ... = ... { ... }
@@ -1795,12 +1977,20 @@ fn find_sugg_for_if_let<'tcx>(
                 // while let ... = ... { ... }
                 // ^^^^^^^^^^^^^^^^^^^
                 let span = expr_span.until(op_span.shrink_to_hi());
-                diag.span_suggestion(
-                    span,
-                    "try this",
-                    format!("{} {}.{}", keyword, snippet(cx, op_span, "_"), good_method),
-                    Applicability::MachineApplicable, // snippet
-                );
+
+                let mut app = if needs_drop {
+                    Applicability::MaybeIncorrect
+                } else {
+                    Applicability::MachineApplicable
+                };
+                let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
+
+                diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
+
+                if needs_drop {
+                    diag.note("this will change drop order of the result, as well as all temporaries");
+                    diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
+                }
             },
         );
     }
@@ -1811,11 +2001,12 @@ fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &
 
             let found_good_method = match node_pair {
                 (
-                    PatKind::TupleStruct(ref path_left, ref patterns_left, _),
-                    PatKind::TupleStruct(ref path_right, ref patterns_right, _),
+                    PatKind::TupleStruct(ref path_left, patterns_left, _),
+                    PatKind::TupleStruct(ref path_right, patterns_right, _),
                 ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
                     if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
                         find_good_method_for_match(
+                            cx,
                             arms,
                             path_left,
                             path_right,
@@ -1826,6 +2017,7 @@ fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &
                         )
                         .or_else(|| {
                             find_good_method_for_match(
+                                cx,
                                 arms,
                                 path_left,
                                 path_right,
@@ -1839,12 +2031,13 @@ fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &
                         None
                     }
                 },
-                (PatKind::TupleStruct(ref path_left, ref patterns, _), PatKind::Path(ref path_right))
-                | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, ref patterns, _))
+                (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right))
+                | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _))
                     if patterns.len() == 1 =>
                 {
                     if let PatKind::Wild = patterns[0].kind {
                         find_good_method_for_match(
+                            cx,
                             arms,
                             path_left,
                             path_right,
@@ -1855,6 +2048,7 @@ fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &
                         )
                         .or_else(|| {
                             find_good_method_for_match(
+                                cx,
                                 arms,
                                 path_left,
                                 path_right,
@@ -1895,7 +2089,9 @@ fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &
         }
     }
 
+    #[allow(clippy::too_many_arguments)]
     fn find_good_method_for_match<'a>(
+        cx: &LateContext<'_>,
         arms: &[Arm<'_>],
         path_left: &QPath<'_>,
         path_right: &QPath<'_>,
@@ -1904,9 +2100,13 @@ fn find_good_method_for_match<'a>(
         should_be_left: &'a str,
         should_be_right: &'a str,
     ) -> Option<&'a str> {
-        let body_node_pair = if match_qpath(path_left, expected_left) && match_qpath(path_right, expected_right) {
+        let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left)
+            && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right)
+        {
             (&(*arms[0].body).kind, &(*arms[1].body).kind)
-        } else if match_qpath(path_right, expected_left) && match_qpath(path_left, expected_right) {
+        } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left)
+            && is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right)
+        {
             (&(*arms[1].body).kind, &(*arms[0].body).kind)
         } else {
             return None;
@@ -1962,10 +2162,10 @@ fn test_overlapping() {
 
 /// Implementation of `MATCH_SAME_ARMS`.
 fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
-    if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
+    if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind {
         let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
             let mut h = SpanlessHash::new(cx);
-            h.hash_expr(&arm.body);
+            h.hash_expr(arm.body);
             h.finish()
         };
 
@@ -1973,7 +2173,7 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
             let min_index = usize::min(lindex, rindex);
             let max_index = usize::max(lindex, rindex);
 
-            let mut local_map: FxHashMap<HirId, HirId> = FxHashMap::default();
+            let mut local_map: HirIdMap<HirId> = HirIdMap::default();
             let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
                 if_chain! {
                     if let Some(a_id) = path_to_local(a);
@@ -2001,7 +2201,7 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
             (min_index..=max_index).all(|index| arms[index].guard.is_none())
                 && SpanlessEq::new(cx)
                     .expr_fallback(eq_fallback)
-                    .eq_expr(&lhs.body, &rhs.body)
+                    .eq_expr(lhs.body, rhs.body)
                 // these checks could be removed to allow unused bindings
                 && bindings_eq(lhs.pat, local_map.keys().copied().collect())
                 && bindings_eq(rhs.pat, local_map.values().copied().collect())
@@ -2057,7 +2257,7 @@ fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool {
 }
 
 /// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
-fn bindings_eq(pat: &Pat<'_>, mut ids: FxHashSet<HirId>) -> bool {
+fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool {
     let mut result = true;
     pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id));
     result && ids.is_empty()