about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/attrs.rs4
-rw-r--r--clippy_lints/src/doc.rs2
-rw-r--r--clippy_lints/src/eval_order_dependence.rs2
-rw-r--r--clippy_lints/src/loops/never_loop.rs2
-rw-r--r--clippy_lints/src/matches.rs245
-rw-r--r--clippy_lints/src/redundant_clone.rs4
-rw-r--r--clippy_lints/src/types/mod.rs6
-rw-r--r--clippy_lints/src/unnecessary_wraps.rs2
-rw-r--r--clippy_utils/src/lib.rs10
-rw-r--r--tests/ui/match_wildcard_for_single_variants.fixed52
-rw-r--r--tests/ui/match_wildcard_for_single_variants.rs52
-rw-r--r--tests/ui/match_wildcard_for_single_variants.stderr44
-rw-r--r--tests/ui/wildcard_enum_match_arm.fixed2
-rw-r--r--tests/ui/wildcard_enum_match_arm.stderr12
14 files changed, 322 insertions, 117 deletions
diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs
index f805f716e6b..a4ddef666c0 100644
--- a/clippy_lints/src/attrs.rs
+++ b/clippy_lints/src/attrs.rs
@@ -430,7 +430,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
         |stmt| match &stmt.kind {
             StmtKind::Local(_) => true,
             StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
-            _ => false,
+            StmtKind::Item(_) => false,
         },
     )
 }
@@ -613,7 +613,7 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
                             }
                         }
                     },
-                    _ => {},
+                    MetaItemKind::NameValue(..) => {},
                 }
             }
         }
diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs
index 5fa278b2897..14338ac8faf 100644
--- a/clippy_lints/src/doc.rs
+++ b/clippy_lints/src/doc.rs
@@ -583,7 +583,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
                                 let returns_nothing = match &sig.decl.output {
                                     FnRetTy::Default(..) => true,
                                     FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
-                                    _ => false,
+                                    FnRetTy::Ty(_) => false,
                                 };
 
                                 if returns_nothing && !is_async && !block.stmts.is_empty() {
diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs
index 5d5b67de843..ea33a4d98fd 100644
--- a/clippy_lints/src/eval_order_dependence.rs
+++ b/clippy_lints/src/eval_order_dependence.rs
@@ -273,7 +273,7 @@ fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -
             .init
             .as_ref()
             .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
-        _ => StopEarly::KeepGoing,
+        StmtKind::Item(..) => StopEarly::KeepGoing,
     }
 }
 
diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs
index 82ec2635aeb..f63a3761a0d 100644
--- a/clippy_lints/src/loops/never_loop.rs
+++ b/clippy_lints/src/loops/never_loop.rs
@@ -78,7 +78,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
     match stmt.kind {
         StmtKind::Semi(ref e, ..) | StmtKind::Expr(ref e, ..) => Some(e),
         StmtKind::Local(ref local) => local.init.as_deref(),
-        _ => None,
+        StmtKind::Item(..) => None,
     }
 }
 
diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs
index e94c7094cac..d43cb32ee51 100644
--- a/clippy_lints/src/matches.rs
+++ b/clippy_lints/src/matches.rs
@@ -8,21 +8,21 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, pe
 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, remove_blocks, strip_pat_refs,
+    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;
+use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::{
-    Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, Mutability, Node, Pat,
-    PatKind, QPath, RangeEnd,
+    self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
+    Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty, TyS};
+use rustc_middle::ty::{self, Ty, TyS, VariantDef};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::{Span, Spanned};
@@ -956,114 +956,181 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm
     }
 }
 
-fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
-    let ty = cx.typeck_results().expr_ty(ex);
-    if !ty.is_enum() {
-        // If there isn't a nice closed set of possible values that can be conveniently enumerated,
-        // don't complain about not enumerating the mall.
-        return;
+enum CommonPrefixSearcher<'a> {
+    None,
+    Path(&'a [PathSegment<'a>]),
+    Mixed,
+}
+impl CommonPrefixSearcher<'a> {
+    fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
+        match path {
+            [path @ .., _] => self.with_prefix(path),
+            [] => (),
+        }
     }
 
+    fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
+        match self {
+            Self::None => *self = Self::Path(path),
+            Self::Path(self_path)
+                if path
+                    .iter()
+                    .map(|p| p.ident.name)
+                    .eq(self_path.iter().map(|p| p.ident.name)) => {},
+            Self::Path(_) => *self = Self::Mixed,
+            Self::Mixed => (),
+        }
+    }
+}
+
+#[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();
+    let adt_def = match ty.kind() {
+        ty::Adt(adt_def, _)
+            if adt_def.is_enum()
+                && !(is_type_diagnostic_item(cx, ty, sym::option_type)
+                    || is_type_diagnostic_item(cx, ty, sym::result_type)) =>
+        {
+            adt_def
+        },
+        _ => return,
+    };
+
     // First pass - check for violation, but don't do much book-keeping because this is hopefully
     // the uncommon case, and the book-keeping is slightly expensive.
     let mut wildcard_span = None;
     let mut wildcard_ident = None;
+    let mut has_non_wild = false;
     for arm in arms {
-        if let PatKind::Wild = arm.pat.kind {
-            wildcard_span = Some(arm.pat.span);
-        } else if let PatKind::Binding(_, _, ident, None) = arm.pat.kind {
-            wildcard_span = Some(arm.pat.span);
-            wildcard_ident = Some(ident);
+        match peel_hir_pat_refs(arm.pat).0.kind {
+            PatKind::Wild => wildcard_span = Some(arm.pat.span),
+            PatKind::Binding(_, _, ident, None) => {
+                wildcard_span = Some(arm.pat.span);
+                wildcard_ident = Some(ident);
+            },
+            _ => has_non_wild = true,
         }
     }
+    let wildcard_span = match wildcard_span {
+        Some(x) if has_non_wild => x,
+        _ => return,
+    };
 
-    if let Some(wildcard_span) = wildcard_span {
-        // Accumulate the variants which should be put in place of the wildcard because they're not
-        // already covered.
+    // Accumulate the variants which should be put in place of the wildcard because they're not
+    // already covered.
+    let mut missing_variants: Vec<_> = adt_def.variants.iter().collect();
 
-        let mut missing_variants = vec![];
-        if let ty::Adt(def, _) = ty.kind() {
-            for variant in &def.variants {
-                missing_variants.push(variant);
+    let mut path_prefix = CommonPrefixSearcher::None;
+    for arm in arms {
+        // Guards mean that this case probably isn't exhaustively covered. Technically
+        // this is incorrect, as we should really check whether each variant is exhaustively
+        // covered by the set of guards that cover it, but that's really hard to do.
+        recurse_or_patterns(arm.pat, |pat| {
+            let path = match &peel_hir_pat_refs(pat).0.kind {
+                PatKind::Path(path) => {
+                    #[allow(clippy::match_same_arms)]
+                    let id = match cx.qpath_res(path, pat.hir_id) {
+                        Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return,
+                        Res::Def(_, id) => id,
+                        _ => return,
+                    };
+                    if arm.guard.is_none() {
+                        missing_variants.retain(|e| e.ctor_def_id != Some(id));
+                    }
+                    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));
+                    }
+                    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);
+                    }
+                    path
+                },
+                _ => return,
+            };
+            match path {
+                QPath::Resolved(_, path) => path_prefix.with_path(path.segments),
+                QPath::TypeRelative(
+                    hir::Ty {
+                        kind: TyKind::Path(QPath::Resolved(_, path)),
+                        ..
+                    },
+                    _,
+                ) => path_prefix.with_prefix(path.segments),
+                _ => (),
             }
-        }
+        });
+    }
 
-        for arm in arms {
-            if arm.guard.is_some() {
-                // Guards mean that this case probably isn't exhaustively covered. Technically
-                // this is incorrect, as we should really check whether each variant is exhaustively
-                // covered by the set of guards that cover it, but that's really hard to do.
-                continue;
-            }
-            if let PatKind::Path(ref path) = arm.pat.kind {
-                if let QPath::Resolved(_, p) = path {
-                    missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
-                }
-            } else if let PatKind::TupleStruct(QPath::Resolved(_, p), ref patterns, ..) = arm.pat.kind {
-                // Some simple checks for exhaustive patterns.
-                // There is a room for improvements to detect more cases,
-                // but it can be more expensive to do so.
-                let is_pattern_exhaustive =
-                    |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None));
-                if patterns.iter().all(is_pattern_exhaustive) {
-                    missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
+    let format_suggestion = |variant: &VariantDef| {
+        format!(
+            "{}{}{}{}",
+            if let Some(ident) = wildcard_ident {
+                format!("{} @ ", ident.name)
+            } else {
+                String::new()
+            },
+            if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
+                let mut s = String::new();
+                for seg in path_prefix {
+                    s.push_str(&seg.ident.as_str());
+                    s.push_str("::");
                 }
+                s
+            } else {
+                let mut s = cx.tcx.def_path_str(adt_def.did);
+                s.push_str("::");
+                s
+            },
+            variant.ident.name,
+            match variant.ctor_kind {
+                CtorKind::Fn if variant.fields.len() == 1 => "(_)",
+                CtorKind::Fn => "(..)",
+                CtorKind::Const => "",
+                CtorKind::Fictive => "{ .. }",
             }
-        }
-
-        let mut suggestion: Vec<String> = missing_variants
-            .iter()
-            .map(|v| {
-                let suffix = match v.ctor_kind {
-                    CtorKind::Fn => "(..)",
-                    CtorKind::Const | CtorKind::Fictive => "",
-                };
-                let ident_str = if let Some(ident) = wildcard_ident {
-                    format!("{} @ ", ident.name)
-                } else {
-                    String::new()
-                };
-                // This path assumes that the enum type is imported into scope.
-                format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix)
-            })
-            .collect();
-
-        if suggestion.is_empty() {
-            return;
-        }
-
-        let mut message = "wildcard match will miss any future added variants";
+        )
+    };
 
-        if let ty::Adt(def, _) = ty.kind() {
-            if def.is_variant_list_non_exhaustive() {
-                message = "match on non-exhaustive enum doesn't explicitly match all known variants";
-                suggestion.push(String::from("_"));
-            }
-        }
+    match missing_variants.as_slice() {
+        [] => (),
+        [x] if !adt_def.is_variant_list_non_exhaustive() => span_lint_and_sugg(
+            cx,
+            MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+            wildcard_span,
+            "wildcard matches only a single variant and will also match any future added variants",
+            "try this",
+            format_suggestion(x),
+            Applicability::MaybeIncorrect,
+        ),
+        variants => {
+            let mut suggestions: Vec<_> = variants.iter().cloned().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"
+            } else {
+                "wildcard match will also match any future added variants"
+            };
 
-        if suggestion.len() == 1 {
-            // No need to check for non-exhaustive enum as in that case len would be greater than 1
             span_lint_and_sugg(
                 cx,
-                MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+                WILDCARD_ENUM_MATCH_ARM,
                 wildcard_span,
                 message,
                 "try this",
-                suggestion[0].clone(),
+                suggestions.join(" | "),
                 Applicability::MaybeIncorrect,
             )
-        };
-
-        span_lint_and_sugg(
-            cx,
-            WILDCARD_ENUM_MATCH_ARM,
-            wildcard_span,
-            message,
-            "try this",
-            suggestion.join(" | "),
-            Applicability::MaybeIncorrect,
-        )
-    }
+        },
+    };
 }
 
 // If the block contains only a `panic!` macro (as expression or statement)
diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs
index 60da2bcb04a..37678fac1d2 100644
--- a/clippy_lints/src/redundant_clone.rs
+++ b/clippy_lints/src/redundant_clone.rs
@@ -556,7 +556,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
                     mir::Operand::Copy(p) | mir::Operand::Move(p) => {
                         self.possible_borrower.add(p.local, *dest);
                     },
-                    _ => (),
+                    mir::Operand::Constant(..) => (),
                 }
             }
         }
@@ -578,7 +578,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
 
     let mut visit_op = |op: &mir::Operand<'_>| match op {
         mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local),
-        _ => (),
+        mir::Operand::Constant(..) => (),
     };
 
     match rvalue {
diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs
index c0cd48e94b2..10c93c6ad06 100644
--- a/clippy_lints/src/types/mod.rs
+++ b/clippy_lints/src/types/mod.rs
@@ -276,7 +276,7 @@ impl<'tcx> LateLintPass<'tcx> for Types {
         match item.kind {
             TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_ty(cx, ty, false),
             TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl),
-            _ => (),
+            TraitItemKind::Type(..) => (),
         }
     }
 
@@ -452,7 +452,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeComplexity {
             TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_type(cx, ty),
             TraitItemKind::Fn(FnSig { ref decl, .. }, TraitFn::Required(_)) => self.check_fndecl(cx, decl),
             // methods with default impl are covered by check_fn
-            _ => (),
+            TraitItemKind::Type(..) | TraitItemKind::Fn(_, TraitFn::Provided(_)) => (),
         }
     }
 
@@ -460,7 +460,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeComplexity {
         match item.kind {
             ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_type(cx, ty),
             // methods are covered by check_fn
-            _ => (),
+            ImplItemKind::Fn(..) => (),
         }
     }
 
diff --git a/clippy_lints/src/unnecessary_wraps.rs b/clippy_lints/src/unnecessary_wraps.rs
index 9e227164695..c2be457e9dc 100644
--- a/clippy_lints/src/unnecessary_wraps.rs
+++ b/clippy_lints/src/unnecessary_wraps.rs
@@ -71,7 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
                 }
             },
             FnKind::Closure => return,
-            _ => (),
+            FnKind::Method(..) => (),
         }
 
         // Abort if the method is implementing a trait or of it a trait method.
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index d5a5430546d..7ee4f140d1e 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -977,6 +977,16 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
     }
 }
 
+/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
+/// the function once on the given pattern.
+pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
+    if let PatKind::Or(pats) = pat.kind {
+        pats.iter().cloned().for_each(f)
+    } else {
+        f(pat)
+    }
+}
+
 /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
 /// implementations have.
 pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed
index 519200977a7..d99f9af3faf 100644
--- a/tests/ui/match_wildcard_for_single_variants.fixed
+++ b/tests/ui/match_wildcard_for_single_variants.fixed
@@ -15,6 +15,16 @@ enum Color {
     Blue,
     Rgb(u8, u8, u8),
 }
+impl Color {
+    fn f(self) {
+        match self {
+            Self::Red => (),
+            Self::Green => (),
+            Self::Blue => (),
+            Self::Rgb(..) => (),
+        };
+    }
+}
 
 fn main() {
     let f = Foo::A;
@@ -56,4 +66,46 @@ fn main() {
         Color::Rgb(255, _, _) => {},
         _ => {},
     }
+
+    // References shouldn't change anything
+    match &color {
+        &Color::Red => (),
+        Color::Green => (),
+        &Color::Rgb(..) => (),
+        Color::Blue => (),
+    }
+
+    use self::Color as C;
+
+    match color {
+        C::Red => (),
+        C::Green => (),
+        C::Rgb(..) => (),
+        C::Blue => (),
+    }
+
+    match color {
+        C::Red => (),
+        Color::Green => (),
+        Color::Rgb(..) => (),
+        Color::Blue => (),
+    }
+
+    match Some(0) {
+        Some(0) => 0,
+        Some(_) => 1,
+        _ => 2,
+    };
+
+    #[non_exhaustive]
+    enum Bar {
+        A,
+        B,
+        C,
+    }
+    match Bar::A {
+        Bar::A => (),
+        Bar::B => (),
+        _ => (),
+    };
 }
diff --git a/tests/ui/match_wildcard_for_single_variants.rs b/tests/ui/match_wildcard_for_single_variants.rs
index 1df917e085c..1752a95de4b 100644
--- a/tests/ui/match_wildcard_for_single_variants.rs
+++ b/tests/ui/match_wildcard_for_single_variants.rs
@@ -15,6 +15,16 @@ enum Color {
     Blue,
     Rgb(u8, u8, u8),
 }
+impl Color {
+    fn f(self) {
+        match self {
+            Self::Red => (),
+            Self::Green => (),
+            Self::Blue => (),
+            _ => (),
+        };
+    }
+}
 
 fn main() {
     let f = Foo::A;
@@ -56,4 +66,46 @@ fn main() {
         Color::Rgb(255, _, _) => {},
         _ => {},
     }
+
+    // References shouldn't change anything
+    match &color {
+        &Color::Red => (),
+        Color::Green => (),
+        &Color::Rgb(..) => (),
+        &_ => (),
+    }
+
+    use self::Color as C;
+
+    match color {
+        C::Red => (),
+        C::Green => (),
+        C::Rgb(..) => (),
+        _ => (),
+    }
+
+    match color {
+        C::Red => (),
+        Color::Green => (),
+        Color::Rgb(..) => (),
+        _ => (),
+    }
+
+    match Some(0) {
+        Some(0) => 0,
+        Some(_) => 1,
+        _ => 2,
+    };
+
+    #[non_exhaustive]
+    enum Bar {
+        A,
+        B,
+        C,
+    }
+    match Bar::A {
+        Bar::A => (),
+        Bar::B => (),
+        _ => (),
+    };
 }
diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr
index 82790aa9e80..34538dea8e5 100644
--- a/tests/ui/match_wildcard_for_single_variants.stderr
+++ b/tests/ui/match_wildcard_for_single_variants.stderr
@@ -1,28 +1,52 @@
-error: wildcard match will miss any future added variants
-  --> $DIR/match_wildcard_for_single_variants.rs:24:9
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:24:13
    |
-LL |         _ => {},
-   |         ^ help: try this: `Foo::C`
+LL |             _ => (),
+   |             ^ help: try this: `Self::Rgb(..)`
    |
    = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings`
 
-error: wildcard match will miss any future added variants
+error: wildcard matches only a single variant and will also match any future added variants
   --> $DIR/match_wildcard_for_single_variants.rs:34:9
    |
 LL |         _ => {},
+   |         ^ help: try this: `Foo::C`
+
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:44:9
+   |
+LL |         _ => {},
    |         ^ help: try this: `Color::Blue`
 
-error: wildcard match will miss any future added variants
-  --> $DIR/match_wildcard_for_single_variants.rs:42:9
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:52:9
    |
 LL |         _ => {},
    |         ^ help: try this: `Color::Blue`
 
-error: wildcard match will miss any future added variants
-  --> $DIR/match_wildcard_for_single_variants.rs:48:9
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:58:9
    |
 LL |         _ => {},
    |         ^ help: try this: `Color::Blue`
 
-error: aborting due to 4 previous errors
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:75:9
+   |
+LL |         &_ => (),
+   |         ^^ help: try this: `Color::Blue`
+
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:84:9
+   |
+LL |         _ => (),
+   |         ^ help: try this: `C::Blue`
+
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:91:9
+   |
+LL |         _ => (),
+   |         ^ help: try this: `Color::Blue`
+
+error: aborting due to 8 previous errors
 
diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed
index c266f684a36..fd754e4c794 100644
--- a/tests/ui/wildcard_enum_match_arm.fixed
+++ b/tests/ui/wildcard_enum_match_arm.fixed
@@ -77,7 +77,7 @@ fn main() {
     let error_kind = ErrorKind::NotFound;
     match error_kind {
         ErrorKind::NotFound => {},
-        std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _ => {},
+        ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _ => {},
     }
     match error_kind {
         ErrorKind::NotFound => {},
diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr
index 0da2b68ba0b..a513a62c748 100644
--- a/tests/ui/wildcard_enum_match_arm.stderr
+++ b/tests/ui/wildcard_enum_match_arm.stderr
@@ -1,4 +1,4 @@
-error: wildcard match will miss any future added variants
+error: wildcard match will also match any future added variants
   --> $DIR/wildcard_enum_match_arm.rs:39:9
    |
 LL |         _ => eprintln!("Not red"),
@@ -10,29 +10,29 @@ note: the lint level is defined here
 LL | #![deny(clippy::wildcard_enum_match_arm)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: wildcard match will miss any future added variants
+error: wildcard match will also match any future added variants
   --> $DIR/wildcard_enum_match_arm.rs:43:9
    |
 LL |         _not_red => eprintln!("Not red"),
    |         ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan`
 
-error: wildcard match will miss any future added variants
+error: wildcard match will also match any future added variants
   --> $DIR/wildcard_enum_match_arm.rs:47:9
    |
 LL |         not_red => format!("{:?}", not_red),
    |         ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan`
 
-error: wildcard match will miss any future added variants
+error: wildcard match will also match any future added variants
   --> $DIR/wildcard_enum_match_arm.rs:63:9
    |
 LL |         _ => "No red",
    |         ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
 
-error: match on non-exhaustive enum doesn't explicitly match all known variants
+error: wildcard matches known variants and will also match future added variants
   --> $DIR/wildcard_enum_match_arm.rs:80:9
    |
 LL |         _ => {},
-   |         ^ help: try this: `std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _`
+   |         ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _`
 
 error: aborting due to 5 previous errors