about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-07-03 16:41:26 +0000
committerbors <bors@rust-lang.org>2020-07-03 16:41:26 +0000
commit57cdf2dc16e1833008ae5b120cb2c045d267dda8 (patch)
treefc61082fd0ccea7433ff7a7e43d07853d08629de
parentfff8e7291381327f5106120b98d7f9585a493cad (diff)
parentc0fd452840c7cc53b3396268f62ed2a0a2e8fef7 (diff)
downloadrust-57cdf2dc16e1833008ae5b120cb2c045d267dda8.tar.gz
rust-57cdf2dc16e1833008ae5b120cb2c045d267dda8.zip
Auto merge of #4841 - phaylon:pattern-type-mismatch, r=flip1995
Added restriction lint: pattern-type-mismatch

changelog: Added a new restriction lint `pattern-type-mismatch`. This lint is especially helpful for beginners learning about the magic behind pattern matching. (This explanation might be worth to include in the next changelog.)
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/lib.rs4
-rw-r--r--clippy_lints/src/pattern_type_mismatch.rs311
-rw-r--r--src/lintlist/mod.rs7
-rw-r--r--tests/ui/pattern_type_mismatch/mutability.rs40
-rw-r--r--tests/ui/pattern_type_mismatch/mutability.stderr19
-rw-r--r--tests/ui/pattern_type_mismatch/pattern_alternatives.rs24
-rw-r--r--tests/ui/pattern_type_mismatch/pattern_alternatives.stderr27
-rw-r--r--tests/ui/pattern_type_mismatch/pattern_structs.rs45
-rw-r--r--tests/ui/pattern_type_mismatch/pattern_structs.stderr67
-rw-r--r--tests/ui/pattern_type_mismatch/pattern_tuples.rs57
-rw-r--r--tests/ui/pattern_type_mismatch/pattern_tuples.stderr83
-rw-r--r--tests/ui/pattern_type_mismatch/syntax.rs146
-rw-r--r--tests/ui/pattern_type_mismatch/syntax.stderr79
14 files changed, 910 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b88044d6ce8..ed8f16e65bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1588,6 +1588,7 @@ Released 2018-09-13
 [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
 [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
 [`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
+[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
 [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 38f8d007c72..1d5be893ffb 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -268,6 +268,7 @@ mod overflow_check_conditional;
 mod panic_unimplemented;
 mod partialeq_ne_impl;
 mod path_buf_push_overwrite;
+mod pattern_type_mismatch;
 mod precedence;
 mod ptr;
 mod ptr_offset_with_cast;
@@ -741,6 +742,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &panic_unimplemented::UNREACHABLE,
         &partialeq_ne_impl::PARTIALEQ_NE_IMPL,
         &path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
+        &pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
         &precedence::PRECEDENCE,
         &ptr::CMP_NULL,
         &ptr::MUT_FROM_REF,
@@ -1065,6 +1067,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns);
     store.register_late_pass(|| box macro_use::MacroUseImports::default());
     store.register_late_pass(|| box map_identity::MapIdentity);
+    store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
         LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1098,6 +1101,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&panic_unimplemented::TODO),
         LintId::of(&panic_unimplemented::UNIMPLEMENTED),
         LintId::of(&panic_unimplemented::UNREACHABLE),
+        LintId::of(&pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
         LintId::of(&shadow::SHADOW_REUSE),
         LintId::of(&shadow::SHADOW_SAME),
         LintId::of(&strings::STRING_ADD),
diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs
new file mode 100644
index 00000000000..a49dc87c0b4
--- /dev/null
+++ b/clippy_lints/src/pattern_type_mismatch.rs
@@ -0,0 +1,311 @@
+use crate::utils::{last_path_segment, span_lint_and_help};
+use rustc_hir::{
+    intravisit, Body, Expr, ExprKind, FieldPat, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind,
+    QPath, Stmt, StmtKind,
+};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Span;
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for patterns that aren't exact representations of the types
+    /// they are applied to.
+    ///
+    /// To satisfy this lint, you will have to adjust either the expression that is matched
+    /// against or the pattern itself, as well as the bindings that are introduced by the
+    /// adjusted patterns. For matching you will have to either dereference the expression
+    /// with the `*` operator, or amend the patterns to explicitly match against `&<pattern>`
+    /// or `&mut <pattern>` depending on the reference mutability. For the bindings you need
+    /// to use the inverse. You can leave them as plain bindings if you wish for the value
+    /// to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct
+    /// a reference into the matched structure.
+    ///
+    /// If you are looking for a way to learn about ownership semantics in more detail, it
+    /// is recommended to look at IDE options available to you to highlight types, lifetimes
+    /// and reference semantics in your code. The available tooling would expose these things
+    /// in a general way even outside of the various pattern matching mechanics. Of course
+    /// this lint can still be used to highlight areas of interest and ensure a good understanding
+    /// of ownership semantics.
+    ///
+    /// **Why is this bad?** It isn't bad in general. But in some contexts it can be desirable
+    /// because it increases ownership hints in the code, and will guard against some changes
+    /// in ownership.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// This example shows the basic adjustments necessary to satisfy the lint. Note how
+    /// the matched expression is explicitly dereferenced with `*` and the `inner` variable
+    /// is bound to a shared borrow via `ref inner`.
+    ///
+    /// ```rust,ignore
+    /// // Bad
+    /// let value = &Some(Box::new(23));
+    /// match value {
+    ///     Some(inner) => println!("{}", inner),
+    ///     None => println!("none"),
+    /// }
+    ///
+    /// // Good
+    /// let value = &Some(Box::new(23));
+    /// match *value {
+    ///     Some(ref inner) => println!("{}", inner),
+    ///     None => println!("none"),
+    /// }
+    /// ```
+    ///
+    /// The following example demonstrates one of the advantages of the more verbose style.
+    /// Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable
+    /// borrow, while `b` is simply taken by value. This ensures that the loop body cannot
+    /// accidentally modify the wrong part of the structure.
+    ///
+    /// ```rust,ignore
+    /// // Bad
+    /// let mut values = vec![(2, 3), (3, 4)];
+    /// for (a, b) in &mut values {
+    ///     *a += *b;
+    /// }
+    ///
+    /// // Good
+    /// let mut values = vec![(2, 3), (3, 4)];
+    /// for &mut (ref mut a, b) in &mut values {
+    ///     *a += b;
+    /// }
+    /// ```
+    pub PATTERN_TYPE_MISMATCH,
+    restriction,
+    "type of pattern does not match the expression type"
+}
+
+declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
+
+impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
+    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
+        if let StmtKind::Local(ref local) = stmt.kind {
+            if let Some(init) = &local.init {
+                if let Some(init_ty) = cx.tables().node_type_opt(init.hir_id) {
+                    let pat = &local.pat;
+                    if in_external_macro(cx.sess(), pat.span) {
+                        return;
+                    }
+                    let deref_possible = match local.source {
+                        LocalSource::Normal => DerefPossible::Possible,
+                        _ => DerefPossible::Impossible,
+                    };
+                    apply_lint(cx, pat, init_ty, deref_possible);
+                }
+            }
+        }
+    }
+
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if let ExprKind::Match(ref expr, arms, source) = expr.kind {
+            match source {
+                MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => {
+                    if let Some(expr_ty) = cx.tables().node_type_opt(expr.hir_id) {
+                        'pattern_checks: for arm in arms {
+                            let pat = &arm.pat;
+                            if in_external_macro(cx.sess(), pat.span) {
+                                continue 'pattern_checks;
+                            }
+                            if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) {
+                                break 'pattern_checks;
+                            }
+                        }
+                    }
+                },
+                _ => (),
+            }
+        }
+    }
+
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        _: intravisit::FnKind<'tcx>,
+        _: &'tcx FnDecl<'_>,
+        body: &'tcx Body<'_>,
+        _: Span,
+        hir_id: HirId,
+    ) {
+        if let Some(fn_sig) = cx.tables().liberated_fn_sigs().get(hir_id) {
+            for (param, ty) in body.params.iter().zip(fn_sig.inputs().iter()) {
+                apply_lint(cx, &param.pat, ty, DerefPossible::Impossible);
+            }
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum DerefPossible {
+    Possible,
+    Impossible,
+}
+
+fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, expr_ty: Ty<'tcx>, deref_possible: DerefPossible) -> bool {
+    let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top);
+    if let Some((span, mutability, level)) = maybe_mismatch {
+        span_lint_and_help(
+            cx,
+            PATTERN_TYPE_MISMATCH,
+            span,
+            "type of pattern does not match the expression type",
+            None,
+            &format!(
+                "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
+                match (deref_possible, level) {
+                    (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
+                    _ => "",
+                },
+                match mutability {
+                    Mutability::Mut => "&mut _",
+                    Mutability::Not => "&_",
+                },
+            ),
+        );
+        true
+    } else {
+        false
+    }
+}
+
+#[derive(Debug, Copy, Clone)]
+enum Level {
+    Top,
+    Lower,
+}
+
+#[allow(rustc::usage_of_ty_tykind)]
+fn find_first_mismatch<'tcx>(
+    cx: &LateContext<'tcx>,
+    pat: &Pat<'_>,
+    ty: Ty<'tcx>,
+    level: Level,
+) -> Option<(Span, Mutability, Level)> {
+    if let PatKind::Ref(ref sub_pat, _) = pat.kind {
+        if let TyKind::Ref(_, sub_ty, _) = ty.kind {
+            return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower);
+        }
+    }
+
+    if let TyKind::Ref(_, _, mutability) = ty.kind {
+        if is_non_ref_pattern(&pat.kind) {
+            return Some((pat.span, mutability, level));
+        }
+    }
+
+    if let PatKind::Struct(ref qpath, ref field_pats, _) = pat.kind {
+        if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
+            if let Some(variant) = get_variant(adt_def, qpath) {
+                let field_defs = &variant.fields;
+                return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref);
+            }
+        }
+    }
+
+    if let PatKind::TupleStruct(ref qpath, ref pats, _) = pat.kind {
+        if let TyKind::Adt(ref adt_def, ref substs_ref) = ty.kind {
+            if let Some(variant) = get_variant(adt_def, qpath) {
+                let field_defs = &variant.fields;
+                let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref));
+                return find_first_mismatch_in_tuple(cx, pats, ty_iter);
+            }
+        }
+    }
+
+    if let PatKind::Tuple(ref pats, _) = pat.kind {
+        if let TyKind::Tuple(..) = ty.kind {
+            return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields());
+        }
+    }
+
+    if let PatKind::Or(sub_pats) = pat.kind {
+        for pat in sub_pats {
+            let maybe_mismatch = find_first_mismatch(cx, pat, ty, level);
+            if let Some(mismatch) = maybe_mismatch {
+                return Some(mismatch);
+            }
+        }
+    }
+
+    None
+}
+
+fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> {
+    if adt_def.is_struct() {
+        if let Some(variant) = adt_def.variants.iter().next() {
+            return Some(variant);
+        }
+    }
+
+    if adt_def.is_enum() {
+        let pat_ident = last_path_segment(qpath).ident;
+        for variant in &adt_def.variants {
+            if variant.ident == pat_ident {
+                return Some(variant);
+            }
+        }
+    }
+
+    None
+}
+
+fn find_first_mismatch_in_tuple<'tcx, I>(
+    cx: &LateContext<'tcx>,
+    pats: &[&Pat<'_>],
+    ty_iter_src: I,
+) -> Option<(Span, Mutability, Level)>
+where
+    I: IntoIterator<Item = Ty<'tcx>>,
+{
+    let mut field_tys = ty_iter_src.into_iter();
+    'fields: for pat in pats {
+        let field_ty = if let Some(ty) = field_tys.next() {
+            ty
+        } else {
+            break 'fields;
+        };
+
+        let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
+        if let Some(mismatch) = maybe_mismatch {
+            return Some(mismatch);
+        }
+    }
+
+    None
+}
+
+fn find_first_mismatch_in_struct<'tcx>(
+    cx: &LateContext<'tcx>,
+    field_pats: &[FieldPat<'_>],
+    field_defs: &[FieldDef],
+    substs_ref: SubstsRef<'tcx>,
+) -> Option<(Span, Mutability, Level)> {
+    for field_pat in field_pats {
+        'definitions: for field_def in field_defs {
+            if field_pat.ident == field_def.ident {
+                let field_ty = field_def.ty(cx.tcx, substs_ref);
+                let pat = &field_pat.pat;
+                let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
+                if let Some(mismatch) = maybe_mismatch {
+                    return Some(mismatch);
+                }
+                break 'definitions;
+            }
+        }
+    }
+
+    None
+}
+
+fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool {
+    match pat_kind {
+        PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true,
+        PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)),
+        _ => false,
+    }
+}
diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs
index a2998d74130..6402efc2521 100644
--- a/src/lintlist/mod.rs
+++ b/src/lintlist/mod.rs
@@ -1698,6 +1698,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
         module: "path_buf_push_overwrite",
     },
     Lint {
+        name: "pattern_type_mismatch",
+        group: "restriction",
+        desc: "type of pattern does not match the expression type",
+        deprecation: None,
+        module: "pattern_type_mismatch",
+    },
+    Lint {
         name: "possible_missing_comma",
         group: "correctness",
         desc: "possible missing comma in array",
diff --git a/tests/ui/pattern_type_mismatch/mutability.rs b/tests/ui/pattern_type_mismatch/mutability.rs
new file mode 100644
index 00000000000..9b4f2f1f579
--- /dev/null
+++ b/tests/ui/pattern_type_mismatch/mutability.rs
@@ -0,0 +1,40 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn should_lint() {
+    let value = &Some(23);
+    match value {
+        Some(_) => (),
+        _ => (),
+    }
+
+    let value = &mut Some(23);
+    match value {
+        Some(_) => (),
+        _ => (),
+    }
+}
+
+fn should_not_lint() {
+    let value = &Some(23);
+    match value {
+        &Some(_) => (),
+        _ => (),
+    }
+    match *value {
+        Some(_) => (),
+        _ => (),
+    }
+
+    let value = &mut Some(23);
+    match value {
+        &mut Some(_) => (),
+        _ => (),
+    }
+    match *value {
+        Some(_) => (),
+        _ => (),
+    }
+}
diff --git a/tests/ui/pattern_type_mismatch/mutability.stderr b/tests/ui/pattern_type_mismatch/mutability.stderr
new file mode 100644
index 00000000000..3421d568365
--- /dev/null
+++ b/tests/ui/pattern_type_mismatch/mutability.stderr
@@ -0,0 +1,19 @@
+error: type of pattern does not match the expression type
+  --> $DIR/mutability.rs:9:9
+   |
+LL |         Some(_) => (),
+   |         ^^^^^^^
+   |
+   = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/mutability.rs:15:9
+   |
+LL |         Some(_) => (),
+   |         ^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&mut _` pattern and adjust the enclosed variable bindings
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/pattern_type_mismatch/pattern_alternatives.rs b/tests/ui/pattern_type_mismatch/pattern_alternatives.rs
new file mode 100644
index 00000000000..065ea9fb9b5
--- /dev/null
+++ b/tests/ui/pattern_type_mismatch/pattern_alternatives.rs
@@ -0,0 +1,24 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn alternatives() {
+    enum Value<'a> {
+        Unused,
+        A(&'a Option<i32>),
+        B,
+    }
+    let ref_value = &Value::A(&Some(23));
+
+    // not ok
+    if let Value::B | Value::A(_) = ref_value {}
+    if let &Value::B | &Value::A(Some(_)) = ref_value {}
+    if let Value::B | Value::A(Some(_)) = *ref_value {}
+
+    // ok
+    if let &Value::B | &Value::A(_) = ref_value {}
+    if let Value::B | Value::A(_) = *ref_value {}
+    if let &Value::B | &Value::A(&Some(_)) = ref_value {}
+    if let Value::B | Value::A(&Some(_)) = *ref_value {}
+}
diff --git a/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr b/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr
new file mode 100644
index 00000000000..d285c93782c
--- /dev/null
+++ b/tests/ui/pattern_type_mismatch/pattern_alternatives.stderr
@@ -0,0 +1,27 @@
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_alternatives.rs:15:12
+   |
+LL |     if let Value::B | Value::A(_) = ref_value {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_alternatives.rs:16:34
+   |
+LL |     if let &Value::B | &Value::A(Some(_)) = ref_value {}
+   |                                  ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_alternatives.rs:17:32
+   |
+LL |     if let Value::B | Value::A(Some(_)) = *ref_value {}
+   |                                ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: aborting due to 3 previous errors
+
diff --git a/tests/ui/pattern_type_mismatch/pattern_structs.rs b/tests/ui/pattern_type_mismatch/pattern_structs.rs
new file mode 100644
index 00000000000..417b1c107c5
--- /dev/null
+++ b/tests/ui/pattern_type_mismatch/pattern_structs.rs
@@ -0,0 +1,45 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn struct_types() {
+    struct Struct<'a> {
+        ref_inner: &'a Option<i32>,
+    }
+    let ref_value = &Struct { ref_inner: &Some(42) };
+
+    // not ok
+    let Struct { .. } = ref_value;
+    if let &Struct { ref_inner: Some(_) } = ref_value {}
+    if let Struct { ref_inner: Some(_) } = *ref_value {}
+
+    // ok
+    let &Struct { .. } = ref_value;
+    let Struct { .. } = *ref_value;
+    if let &Struct { ref_inner: &Some(_) } = ref_value {}
+    if let Struct { ref_inner: &Some(_) } = *ref_value {}
+}
+
+fn struct_enum_variants() {
+    enum StructEnum<'a> {
+        Empty,
+        Var { inner_ref: &'a Option<i32> },
+    }
+    let ref_value = &StructEnum::Var { inner_ref: &Some(42) };
+
+    // not ok
+    if let StructEnum::Var { .. } = ref_value {}
+    if let StructEnum::Var { inner_ref: Some(_) } = ref_value {}
+    if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {}
+    if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {}
+    if let StructEnum::Empty = ref_value {}
+
+    // ok
+    if let &StructEnum::Var { .. } = ref_value {}
+    if let StructEnum::Var { .. } = *ref_value {}
+    if let &StructEnum::Var { inner_ref: &Some(_) } = ref_value {}
+    if let StructEnum::Var { inner_ref: &Some(_) } = *ref_value {}
+    if let &StructEnum::Empty = ref_value {}
+    if let StructEnum::Empty = *ref_value {}
+}
diff --git a/tests/ui/pattern_type_mismatch/pattern_structs.stderr b/tests/ui/pattern_type_mismatch/pattern_structs.stderr
new file mode 100644
index 00000000000..d428e85b0c9
--- /dev/null
+++ b/tests/ui/pattern_type_mismatch/pattern_structs.stderr
@@ -0,0 +1,67 @@
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:13:9
+   |
+LL |     let Struct { .. } = ref_value;
+   |         ^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:14:33
+   |
+LL |     if let &Struct { ref_inner: Some(_) } = ref_value {}
+   |                                 ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:15:32
+   |
+LL |     if let Struct { ref_inner: Some(_) } = *ref_value {}
+   |                                ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:32:12
+   |
+LL |     if let StructEnum::Var { .. } = ref_value {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:33:12
+   |
+LL |     if let StructEnum::Var { inner_ref: Some(_) } = ref_value {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:34:42
+   |
+LL |     if let &StructEnum::Var { inner_ref: Some(_) } = ref_value {}
+   |                                          ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:35:41
+   |
+LL |     if let StructEnum::Var { inner_ref: Some(_) } = *ref_value {}
+   |                                         ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_structs.rs:36:12
+   |
+LL |     if let StructEnum::Empty = ref_value {}
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: aborting due to 8 previous errors
+
diff --git a/tests/ui/pattern_type_mismatch/pattern_tuples.rs b/tests/ui/pattern_type_mismatch/pattern_tuples.rs
new file mode 100644
index 00000000000..19504a051d8
--- /dev/null
+++ b/tests/ui/pattern_type_mismatch/pattern_tuples.rs
@@ -0,0 +1,57 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn tuple_types() {
+    struct TupleStruct<'a>(&'a Option<i32>);
+    let ref_value = &TupleStruct(&Some(42));
+
+    // not ok
+    let TupleStruct(_) = ref_value;
+    if let &TupleStruct(Some(_)) = ref_value {}
+    if let TupleStruct(Some(_)) = *ref_value {}
+
+    // ok
+    let &TupleStruct(_) = ref_value;
+    let TupleStruct(_) = *ref_value;
+    if let &TupleStruct(&Some(_)) = ref_value {}
+    if let TupleStruct(&Some(_)) = *ref_value {}
+}
+
+fn tuple_enum_variants() {
+    enum TupleEnum<'a> {
+        Empty,
+        Var(&'a Option<i32>),
+    }
+    let ref_value = &TupleEnum::Var(&Some(42));
+
+    // not ok
+    if let TupleEnum::Var(_) = ref_value {}
+    if let &TupleEnum::Var(Some(_)) = ref_value {}
+    if let TupleEnum::Var(Some(_)) = *ref_value {}
+    if let TupleEnum::Empty = ref_value {}
+
+    // ok
+    if let &TupleEnum::Var(_) = ref_value {}
+    if let TupleEnum::Var(_) = *ref_value {}
+    if let &TupleEnum::Var(&Some(_)) = ref_value {}
+    if let TupleEnum::Var(&Some(_)) = *ref_value {}
+    if let &TupleEnum::Empty = ref_value {}
+    if let TupleEnum::Empty = *ref_value {}
+}
+
+fn plain_tuples() {
+    let ref_value = &(&Some(23), &Some(42));
+
+    // not ok
+    let (_a, _b) = ref_value;
+    if let &(_a, Some(_)) = ref_value {}
+    if let (_a, Some(_)) = *ref_value {}
+
+    // ok
+    let &(_a, _b) = ref_value;
+    let (_a, _b) = *ref_value;
+    if let &(_a, &Some(_)) = ref_value {}
+    if let (_a, &Some(_)) = *ref_value {}
+}
diff --git a/tests/ui/pattern_type_mismatch/pattern_tuples.stderr b/tests/ui/pattern_type_mismatch/pattern_tuples.stderr
new file mode 100644
index 00000000000..edd0074d00d
--- /dev/null
+++ b/tests/ui/pattern_type_mismatch/pattern_tuples.stderr
@@ -0,0 +1,83 @@
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:11:9
+   |
+LL |     let TupleStruct(_) = ref_value;
+   |         ^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:12:25
+   |
+LL |     if let &TupleStruct(Some(_)) = ref_value {}
+   |                         ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:13:24
+   |
+LL |     if let TupleStruct(Some(_)) = *ref_value {}
+   |                        ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:30:12
+   |
+LL |     if let TupleEnum::Var(_) = ref_value {}
+   |            ^^^^^^^^^^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:31:28
+   |
+LL |     if let &TupleEnum::Var(Some(_)) = ref_value {}
+   |                            ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:32:27
+   |
+LL |     if let TupleEnum::Var(Some(_)) = *ref_value {}
+   |                           ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:33:12
+   |
+LL |     if let TupleEnum::Empty = ref_value {}
+   |            ^^^^^^^^^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:48:9
+   |
+LL |     let (_a, _b) = ref_value;
+   |         ^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:49:18
+   |
+LL |     if let &(_a, Some(_)) = ref_value {}
+   |                  ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/pattern_tuples.rs:50:17
+   |
+LL |     if let (_a, Some(_)) = *ref_value {}
+   |                 ^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: aborting due to 10 previous errors
+
diff --git a/tests/ui/pattern_type_mismatch/syntax.rs b/tests/ui/pattern_type_mismatch/syntax.rs
new file mode 100644
index 00000000000..e89917c41e8
--- /dev/null
+++ b/tests/ui/pattern_type_mismatch/syntax.rs
@@ -0,0 +1,146 @@
+#![allow(clippy::all)]
+#![warn(clippy::pattern_type_mismatch)]
+
+fn main() {}
+
+fn syntax_match() {
+    let ref_value = &Some(&Some(42));
+
+    // not ok
+    match ref_value {
+        Some(_) => (),
+        None => (),
+    }
+
+    // ok
+    match ref_value {
+        &Some(_) => (),
+        &None => (),
+    }
+    match *ref_value {
+        Some(_) => (),
+        None => (),
+    }
+}
+
+fn syntax_if_let() {
+    let ref_value = &Some(42);
+
+    // not ok
+    if let Some(_) = ref_value {}
+
+    // ok
+    if let &Some(_) = ref_value {}
+    if let Some(_) = *ref_value {}
+}
+
+fn syntax_while_let() {
+    let ref_value = &Some(42);
+
+    // not ok
+    while let Some(_) = ref_value {
+        break;
+    }
+
+    // ok
+    while let &Some(_) = ref_value {
+        break;
+    }
+    while let Some(_) = *ref_value {
+        break;
+    }
+}
+
+fn syntax_for() {
+    let ref_value = &Some(23);
+    let slice = &[(2, 3), (4, 2)];
+
+    // not ok
+    for (_a, _b) in slice.iter() {}
+
+    // ok
+    for &(_a, _b) in slice.iter() {}
+}
+
+fn syntax_let() {
+    let ref_value = &(2, 3);
+
+    // not ok
+    let (_n, _m) = ref_value;
+
+    // ok
+    let &(_n, _m) = ref_value;
+    let (_n, _m) = *ref_value;
+}
+
+fn syntax_fn() {
+    // not ok
+    fn foo((_a, _b): &(i32, i32)) {}
+
+    // ok
+    fn foo_ok_1(&(_a, _b): &(i32, i32)) {}
+}
+
+fn syntax_closure() {
+    fn foo<F>(f: F)
+    where
+        F: FnOnce(&(i32, i32)),
+    {
+    }
+
+    // not ok
+    foo(|(_a, _b)| ());
+
+    // ok
+    foo(|&(_a, _b)| ());
+}
+
+fn macro_with_expression() {
+    macro_rules! matching_macro {
+        ($e:expr) => {
+            $e
+        };
+    }
+    let value = &Some(23);
+
+    // not ok
+    matching_macro!(match value {
+        Some(_) => (),
+        _ => (),
+    });
+
+    // ok
+    matching_macro!(match value {
+        &Some(_) => (),
+        _ => (),
+    });
+    matching_macro!(match *value {
+        Some(_) => (),
+        _ => (),
+    });
+}
+
+fn macro_expansion() {
+    macro_rules! matching_macro {
+        ($e:expr) => {
+            // not ok
+            match $e {
+                Some(_) => (),
+                _ => (),
+            }
+
+            // ok
+            match $e {
+                &Some(_) => (),
+                _ => (),
+            }
+            match *$e {
+                Some(_) => (),
+                _ => (),
+            }
+        };
+    }
+
+    let value = &Some(23);
+    matching_macro!(value);
+}
diff --git a/tests/ui/pattern_type_mismatch/syntax.stderr b/tests/ui/pattern_type_mismatch/syntax.stderr
new file mode 100644
index 00000000000..5a5186bd4fc
--- /dev/null
+++ b/tests/ui/pattern_type_mismatch/syntax.stderr
@@ -0,0 +1,79 @@
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:11:9
+   |
+LL |         Some(_) => (),
+   |         ^^^^^^^
+   |
+   = note: `-D clippy::pattern-type-mismatch` implied by `-D warnings`
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:30:12
+   |
+LL |     if let Some(_) = ref_value {}
+   |            ^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:41:15
+   |
+LL |     while let Some(_) = ref_value {
+   |               ^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:59:9
+   |
+LL |     for (_a, _b) in slice.iter() {}
+   |         ^^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:69:9
+   |
+LL |     let (_n, _m) = ref_value;
+   |         ^^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:78:12
+   |
+LL |     fn foo((_a, _b): &(i32, i32)) {}
+   |            ^^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:92:10
+   |
+LL |     foo(|(_a, _b)| ());
+   |          ^^^^^^^^
+   |
+   = help: explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:108:9
+   |
+LL |         Some(_) => (),
+   |         ^^^^^^^
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+
+error: type of pattern does not match the expression type
+  --> $DIR/syntax.rs:128:17
+   |
+LL |                 Some(_) => (),
+   |                 ^^^^^^^
+...
+LL |     matching_macro!(value);
+   |     ----------------------- in this macro invocation
+   |
+   = help: use `*` to dereference the match expression or explicitly match against a `&_` pattern and adjust the enclosed variable bindings
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 9 previous errors
+