From 2432e97d6ab7e44390542c00229344bf45fcaddd Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Sat, 28 Jan 2023 18:45:57 -0700 Subject: [PATCH 1/3] wildcard_enum_match_arm lint takes the enum origin into account Signed-off-by: Tyler Weaver --- clippy_lints/src/matches/match_wild_enum.rs | 17 +++++++++++------ .../ui/match_wildcard_for_single_variants.fixed | 2 +- .../match_wildcard_for_single_variants.stderr | 8 +++++++- tests/ui/wildcard_enum_match_arm.fixed | 2 +- tests/ui/wildcard_enum_match_arm.stderr | 4 ++-- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 59de8c0384ba..05cac0f99797 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -45,8 +45,12 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // Accumulate the variants which should be put in place of the wildcard because they're not // already covered. - let has_hidden = adt_def.variants().iter().any(|x| is_hidden(cx, x)); - let mut missing_variants: Vec<_> = adt_def.variants().iter().filter(|x| !is_hidden(cx, x)).collect(); + let has_hidden_external = adt_def.variants().iter().any(|x| is_hidden_and_external(cx, x)); + let mut missing_variants: Vec<_> = adt_def + .variants() + .iter() + .filter(|x| !is_hidden_and_external(cx, x)) + .collect(); let mut path_prefix = CommonPrefixSearcher::None; for arm in arms { @@ -133,7 +137,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { match missing_variants.as_slice() { [] => (), - [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg( + [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden_external => span_lint_and_sugg( cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, @@ -144,7 +148,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { ), variants => { let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); - let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden { + let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden_external { suggestions.push("_".into()); "wildcard matches known variants and will also match future added variants" } else { @@ -191,6 +195,7 @@ impl<'a> CommonPrefixSearcher<'a> { } } -fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { - cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable) +fn is_hidden_and_external(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { + (cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable)) + && variant_def.def_id.as_local().is_none() } diff --git a/tests/ui/match_wildcard_for_single_variants.fixed b/tests/ui/match_wildcard_for_single_variants.fixed index fc252cdd3529..9fd3739b69c2 100644 --- a/tests/ui/match_wildcard_for_single_variants.fixed +++ b/tests/ui/match_wildcard_for_single_variants.fixed @@ -123,7 +123,7 @@ fn main() { Enum::A => (), Enum::B => (), Enum::C => (), - _ => (), + Enum::__Private => (), } match Enum::A { Enum::A => (), diff --git a/tests/ui/match_wildcard_for_single_variants.stderr b/tests/ui/match_wildcard_for_single_variants.stderr index 6fa313dc9111..105b4c4b41d1 100644 --- a/tests/ui/match_wildcard_for_single_variants.stderr +++ b/tests/ui/match_wildcard_for_single_variants.stderr @@ -48,11 +48,17 @@ error: wildcard matches only a single variant and will also match any future add 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:126:13 + | +LL | _ => (), + | ^ help: try this: `Enum::__Private` + error: wildcard matches only a single variant and will also match any future added variants --> $DIR/match_wildcard_for_single_variants.rs:153:13 | LL | _ => 2, | ^ help: try this: `Foo::B` -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/wildcard_enum_match_arm.fixed b/tests/ui/wildcard_enum_match_arm.fixed index 23607497841e..293bf75a7176 100644 --- a/tests/ui/wildcard_enum_match_arm.fixed +++ b/tests/ui/wildcard_enum_match_arm.fixed @@ -96,7 +96,7 @@ fn main() { } match Enum::A { Enum::A => (), - Enum::B | _ => (), + Enum::B | Enum::__Private => (), } } } diff --git a/tests/ui/wildcard_enum_match_arm.stderr b/tests/ui/wildcard_enum_match_arm.stderr index efecc9576cc7..30d29aa4e77a 100644 --- a/tests/ui/wildcard_enum_match_arm.stderr +++ b/tests/ui/wildcard_enum_match_arm.stderr @@ -34,11 +34,11 @@ error: wildcard matches known variants and will also match future added variants LL | _ => {}, | ^ help: try this: `ErrorKind::PermissionDenied | _` -error: wildcard matches known variants and will also match future added variants +error: wildcard match will also match any future added variants --> $DIR/wildcard_enum_match_arm.rs:99:13 | LL | _ => (), - | ^ help: try this: `Enum::B | _` + | ^ help: try this: `Enum::B | Enum::__Private` error: aborting due to 6 previous errors From c531b09eb85e4dff966b6bfb93e7fbf128fdfba0 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Mon, 30 Jan 2023 15:46:34 -0700 Subject: [PATCH 2/3] Check external before hidden --- clippy_lints/src/matches/match_wild_enum.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 05cac0f99797..3b35c04620c1 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -3,6 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def_id::DefId; use rustc_hir::{Arm, Expr, PatKind, PathSegment, QPath, Ty, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, VariantDef}; @@ -45,11 +46,11 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // Accumulate the variants which should be put in place of the wildcard because they're not // already covered. - let has_hidden_external = adt_def.variants().iter().any(|x| is_hidden_and_external(cx, x)); + let has_hidden_external = adt_def.variants().iter().any(|x| is_external_and_hidden(cx, x)); let mut missing_variants: Vec<_> = adt_def .variants() .iter() - .filter(|x| !is_hidden_and_external(cx, x)) + .filter(|x| !is_external_and_hidden(cx, x)) .collect(); let mut path_prefix = CommonPrefixSearcher::None; @@ -195,7 +196,14 @@ impl<'a> CommonPrefixSearcher<'a> { } } -fn is_hidden_and_external(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { - (cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable)) - && variant_def.def_id.as_local().is_none() +fn is_external_and_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { + is_external(variant_def.def_id) && is_hidden(cx, variant_def) +} + +fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { + cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable) +} + +fn is_external(def_id: DefId) -> bool { + def_id.as_local().is_none() } From df7cdf732d3679f21c4a36808fb43b832f0a6725 Mon Sep 17 00:00:00 2001 From: Tyler Weaver Date: Mon, 30 Jan 2023 17:29:17 -0700 Subject: [PATCH 3/3] Pull the is_external test out of the loop --- clippy_lints/src/matches/match_wild_enum.rs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 3b35c04620c1..3126b590180e 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -3,7 +3,6 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::DefId; use rustc_hir::{Arm, Expr, PatKind, PathSegment, QPath, Ty, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, VariantDef}; @@ -46,11 +45,12 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // Accumulate the variants which should be put in place of the wildcard because they're not // already covered. - let has_hidden_external = adt_def.variants().iter().any(|x| is_external_and_hidden(cx, x)); + let is_external = adt_def.did().as_local().is_none(); + let has_external_hidden = is_external && adt_def.variants().iter().any(|x| is_hidden(cx, x)); let mut missing_variants: Vec<_> = adt_def .variants() .iter() - .filter(|x| !is_external_and_hidden(cx, x)) + .filter(|x| !(is_external && is_hidden(cx, x))) .collect(); let mut path_prefix = CommonPrefixSearcher::None; @@ -138,7 +138,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { match missing_variants.as_slice() { [] => (), - [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden_external => span_lint_and_sugg( + [x] if !adt_def.is_variant_list_non_exhaustive() && !has_external_hidden => span_lint_and_sugg( cx, MATCH_WILDCARD_FOR_SINGLE_VARIANTS, wildcard_span, @@ -149,7 +149,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { ), variants => { let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect(); - let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden_external { + let message = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden { suggestions.push("_".into()); "wildcard matches known variants and will also match future added variants" } else { @@ -196,14 +196,6 @@ impl<'a> CommonPrefixSearcher<'a> { } } -fn is_external_and_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { - is_external(variant_def.def_id) && is_hidden(cx, variant_def) -} - fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable) } - -fn is_external(def_id: DefId) -> bool { - def_id.as_local().is_none() -}