From e0ea89c621c0bc6c537c9dbe589db06568ccc2e2 Mon Sep 17 00:00:00 2001 From: jrqc Date: Thu, 20 Aug 2020 13:33:51 +0300 Subject: [PATCH 1/9] not_exhaustive_enough --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 4 + clippy_lints/src/not_exhaustive_enough.rs | 172 ++++++++++++++++++++++ tests/ui/not_exhaustive_enough.rs | 83 +++++++++++ tests/ui/not_exhaustive_enough.stderr | 76 ++++++++++ 5 files changed, 336 insertions(+) create mode 100644 clippy_lints/src/not_exhaustive_enough.rs create mode 100644 tests/ui/not_exhaustive_enough.rs create mode 100644 tests/ui/not_exhaustive_enough.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index de8da99cdee1..a27cd9155d6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1985,6 +1985,7 @@ Released 2018-09-13 [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options +[`not_exhaustive_enough`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_exhaustive_enough [`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref [`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect [`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 35b057d7b6a4..df5f747e6376 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -278,6 +278,7 @@ mod new_without_default; mod no_effect; mod non_copy_const; mod non_expressive_names; +mod not_exhaustive_enough; mod open_options; mod option_env_unwrap; mod option_if_let_else; @@ -810,6 +811,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &non_expressive_names::JUST_UNDERSCORES_AND_DIGITS, &non_expressive_names::MANY_SINGLE_CHAR_NAMES, &non_expressive_names::SIMILAR_NAMES, + ¬_exhaustive_enough::NOT_EXHAUSTIVE_ENOUGH, &open_options::NONSENSICAL_OPEN_OPTIONS, &option_env_unwrap::OPTION_ENV_UNWRAP, &option_if_let_else::OPTION_IF_LET_ELSE, @@ -1124,6 +1126,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box path_buf_push_overwrite::PathBufPushOverwrite); store.register_late_pass(|| box integer_division::IntegerDivision); store.register_late_pass(|| box inherent_to_string::InherentToString); + store.register_late_pass(|| box not_exhaustive_enough::NotExhaustiveEnough); let max_trait_bounds = conf.max_trait_bounds; store.register_late_pass(move || box trait_bounds::TraitBounds::new(max_trait_bounds)); store.register_late_pass(|| box comparison_chain::ComparisonChain); @@ -1319,6 +1322,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&needless_continue::NEEDLESS_CONTINUE), LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), LintId::of(&non_expressive_names::SIMILAR_NAMES), + LintId::of(¬_exhaustive_enough::NOT_EXHAUSTIVE_ENOUGH), LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(&pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), diff --git a/clippy_lints/src/not_exhaustive_enough.rs b/clippy_lints/src/not_exhaustive_enough.rs new file mode 100644 index 000000000000..f56eaa351f41 --- /dev/null +++ b/clippy_lints/src/not_exhaustive_enough.rs @@ -0,0 +1,172 @@ +use crate::utils::span_lint_and_sugg; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Arm, Expr, ExprKind, FieldPat, MatchSource, Pat, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** + /// As suggested in the [non_exhaustive RFC](https://github.com/rust-lang/rfcs/blob/master/text/2008-non-exhaustive.md#unresolved-questions), when using non-exhaustive enums and structs in patterns, + /// the lint would warn the user for missing variants or fields despite having a wildcard arm or a rest pattern. + /// + /// **Why is this bad?** + /// When new fields/variants are added by the upstream crate they might go unnoticed. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// ```rust + /// #[non_exhaustive] + /// # enum E {First,Second,Third} + /// # let e = E::First; + /// // Bad + /// match e { + /// E::First => {} + /// E::Second => {} + /// _ => {} + /// } + /// // Good + /// match e { + /// E::First => {} + /// E::Second => {} + /// E::Third => {} + /// } + /// ``` + pub NOT_EXHAUSTIVE_ENOUGH, + pedantic, + "warn the user for missing variants or fields despite having a wildcard arm or a rest pattern" +} + +declare_lint_pass!(NotExhaustiveEnough => [NOT_EXHAUSTIVE_ENOUGH]); + +impl<'tcx> LateLintPass<'tcx> for NotExhaustiveEnough { + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { + if_chain! { + if let PatKind::Struct(_, ref field_pats, ref rest_pat) = &pat.kind; + if let ty::Adt(adt_def, _) = cx.typeck_results().pat_ty(pat).kind(); + if adt_def.is_struct(); + if is_struct_not_exhaustive(adt_def); + if *rest_pat; + if !field_pats.is_empty(); + if let Some(variant) = get_variant(adt_def); + if let field_defs = &variant.fields; + then + { + check_struct_pat(cx, pat, field_pats, field_defs); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if_chain! { + if let ExprKind::Match(e, ref arms, MatchSource::Normal) = expr.kind; + if !arms.is_empty(); + if let ExprKind::Path(..) = e.kind; + if let ty::Adt(adt_def, _) = cx.typeck_results().expr_ty(e).kind(); + if adt_def.is_enum(); + if is_enum_not_exhaustive(adt_def); + then + { + check_path_pat(cx, arms, e); + } + } + } +} + +fn check_path_pat<'tcx>(cx: &LateContext<'tcx>, arms: &[Arm<'_>], e: &'tcx Expr<'_>) { + let missing_variants = get_missing_variants(cx, arms, e); + span_lint_and_sugg( + cx, + NOT_EXHAUSTIVE_ENOUGH, + e.span, + "Enum not exhaustive enough", + "try adding missing field/s", + missing_variants.join(" , "), + Applicability::MaybeIncorrect, + ); +} + +fn check_struct_pat<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + field_pats: &[FieldPat<'_>], + field_defs: &[ty::FieldDef], +) { + let missing_fields = get_missing_fields(field_pats, field_defs); + span_lint_and_sugg( + cx, + NOT_EXHAUSTIVE_ENOUGH, + pat.span, + "Struct not exhaustive enough", + "try adding missing field/s", + missing_fields.join(" , "), + Applicability::MaybeIncorrect, + ); +} + +fn get_missing_variants<'tcx>(cx: &LateContext<'tcx>, arms: &[Arm<'_>], e: &'tcx Expr<'_>) -> Vec { + let ty = cx.typeck_results().expr_ty(e); + let mut missing_variants = vec![]; + if let ty::Adt(def, _) = ty.kind() { + for variant in &def.variants { + missing_variants.push(variant); + } + } + for arm in arms { + 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(ref path, ref patterns, ..) = arm.pat.kind { + if let QPath::Resolved(_, p) = path { + 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 missing_variants = missing_variants.iter().map(|v| cx.tcx.def_path_str(v.def_id)).collect(); + missing_variants +} + +// refactor - better way? +fn get_missing_fields(field_pats: &[FieldPat<'_>], field_defs: &[ty::FieldDef]) -> Vec { + let mut missing_fields = vec![]; + let mut field_match = false; + + for field_def in field_defs { + for field_pat in field_pats { + if field_def.ident == field_pat.ident { + field_match = true; + break; + } + } + if !&field_match { + missing_fields.push(field_def.ident.name.to_ident_string()) + } + field_match = false; + } + missing_fields +} + +fn is_enum_not_exhaustive(adt_def: &ty::AdtDef) -> bool { + adt_def.is_variant_list_non_exhaustive() +} + +fn is_struct_not_exhaustive(adt_def: &ty::AdtDef) -> bool { + if let Some(variant) = adt_def.variants.iter().next() { + return variant.is_field_list_non_exhaustive(); + } + false +} + +fn get_variant(adt_def: &ty::AdtDef) -> Option<&ty::VariantDef> { + if let Some(variant) = adt_def.variants.iter().next() { + return Some(variant); + } + None +} diff --git a/tests/ui/not_exhaustive_enough.rs b/tests/ui/not_exhaustive_enough.rs new file mode 100644 index 000000000000..32394e2499f9 --- /dev/null +++ b/tests/ui/not_exhaustive_enough.rs @@ -0,0 +1,83 @@ +#![warn(clippy::not_exhaustive_enough)] + +use std::io::ErrorKind; + +#[non_exhaustive] +pub enum E { + First, + Second, + Third, +} + +#[derive(Default)] +#[non_exhaustive] +pub struct S { + pub a: i32, + pub b: i32, + pub c: i32, +} + +#[derive(Default)] +#[non_exhaustive] +pub struct T(pub i32, pub i32, pub i32); + +fn main() { + //////// Enum + + let e = E::First; + + match e { + E::First => {}, + E::Second => {}, + _ => {}, + } + + //////// Struct + + let S { a: _, b: _, .. } = S::default(); + + match S::default() { + S { a: 42, b: 21, .. } => {}, + S { a: _, b: _, .. } => {}, + } + + if let S { a: 42, b: _, .. } = S::default() {} + + let v = vec![S::default()]; + + for S { a: _, b: _, .. } in v {} + + //////// Tuple Struct + + let T { 0: _, 1: _, .. } = T::default(); + + match T::default() { + T { 0: 42, 1: 21, .. } => {}, + T { 0: _, 1: _, .. } => {}, + } + + if let T { 0: 42, 1: _, .. } = T::default() {} + + let v = vec![T::default()]; + for T { 0: _, 1: _, .. } in v {} + + // Enum - Another Crate + let error_kind = ErrorKind::ConnectionReset; + + match error_kind { + ErrorKind::NotFound => {}, + ErrorKind::PermissionDenied => {}, + ErrorKind::ConnectionRefused => {}, + ErrorKind::ConnectionReset => {}, + ErrorKind::ConnectionAborted => {}, + ErrorKind::NotConnected => {}, + ErrorKind::AddrInUse => {}, + ErrorKind::AddrNotAvailable => {}, + ErrorKind::BrokenPipe => {}, + ErrorKind::AlreadyExists => {}, + _ => {}, + } + + // Struct - Another Crate + // todo +} diff --git a/tests/ui/not_exhaustive_enough.stderr b/tests/ui/not_exhaustive_enough.stderr new file mode 100644 index 000000000000..467a8d96100c --- /dev/null +++ b/tests/ui/not_exhaustive_enough.stderr @@ -0,0 +1,76 @@ +error: Enum not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:29:11 + | +LL | match e { + | ^ help: try adding missing field/s: `E::Third` + | + = note: `-D clippy::not-exhaustive-enough` implied by `-D warnings` + +error: Struct not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:37:9 + | +LL | let S { a: _, b: _, .. } = S::default(); + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `c` + +error: Struct not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:40:9 + | +LL | S { a: 42, b: 21, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `c` + +error: Struct not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:41:9 + | +LL | S { a: _, b: _, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `c` + +error: Struct not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:44:12 + | +LL | if let S { a: 42, b: _, .. } = S::default() {} + | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `c` + +error: Struct not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:48:9 + | +LL | for S { a: _, b: _, .. } in v {} + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `c` + +error: Struct not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:52:9 + | +LL | let T { 0: _, 1: _, .. } = T::default(); + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `2` + +error: Struct not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:55:9 + | +LL | T { 0: 42, 1: 21, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `2` + +error: Struct not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:56:9 + | +LL | T { 0: _, 1: _, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `2` + +error: Struct not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:59:12 + | +LL | if let T { 0: 42, 1: _, .. } = T::default() {} + | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `2` + +error: Struct not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:62:9 + | +LL | for T { 0: _, 1: _, .. } in v {} + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `2` + +error: Enum not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:67:11 + | +LL | match error_kind { + | ^^^^^^^^^^ help: try adding missing field/s: `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` + +error: aborting due to 12 previous errors + From bf2af850749328d95e5c974c66644a9e7152ce85 Mon Sep 17 00:00:00 2001 From: Gurbuz Demiroglu Date: Wed, 25 Nov 2020 11:03:48 +0300 Subject: [PATCH 2/9] Update clippy_lints/src/not_exhaustive_enough.rs Co-authored-by: Eduardo Broto --- clippy_lints/src/not_exhaustive_enough.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/not_exhaustive_enough.rs b/clippy_lints/src/not_exhaustive_enough.rs index f56eaa351f41..ec62a0028dec 100644 --- a/clippy_lints/src/not_exhaustive_enough.rs +++ b/clippy_lints/src/not_exhaustive_enough.rs @@ -8,8 +8,9 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** - /// As suggested in the [non_exhaustive RFC](https://github.com/rust-lang/rfcs/blob/master/text/2008-non-exhaustive.md#unresolved-questions), when using non-exhaustive enums and structs in patterns, - /// the lint would warn the user for missing variants or fields despite having a wildcard arm or a rest pattern. + /// As suggested in the [non_exhaustive RFC](https://github.com/rust-lang/rfcs/blob/master/text/2008-non-exhaustive.md#unresolved-questions), + /// when using non-exhaustive enums and structs in patterns, + /// this lint warns the user for missing variants or fields despite having a wildcard arm or a rest pattern. /// /// **Why is this bad?** /// When new fields/variants are added by the upstream crate they might go unnoticed. From ab1885c84390795e1da23673904d20057bb031b6 Mon Sep 17 00:00:00 2001 From: Gurbuz Demiroglu Date: Wed, 25 Nov 2020 11:05:03 +0300 Subject: [PATCH 3/9] Update clippy_lints/src/not_exhaustive_enough.rs Co-authored-by: Eduardo Broto --- clippy_lints/src/not_exhaustive_enough.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/not_exhaustive_enough.rs b/clippy_lints/src/not_exhaustive_enough.rs index ec62a0028dec..73258da5fd97 100644 --- a/clippy_lints/src/not_exhaustive_enough.rs +++ b/clippy_lints/src/not_exhaustive_enough.rs @@ -18,7 +18,7 @@ declare_clippy_lint! { /// **Known problems:** None. /// /// **Example:** - /// ```rust + /// ```rust,ignore /// #[non_exhaustive] /// # enum E {First,Second,Third} /// # let e = E::First; From be6b2fe273acd4409b3ea310c820774c17730f76 Mon Sep 17 00:00:00 2001 From: Gurbuz Demiroglu Date: Wed, 25 Nov 2020 11:27:54 +0300 Subject: [PATCH 4/9] Update clippy_lints/src/not_exhaustive_enough.rs Co-authored-by: Eduardo Broto --- clippy_lints/src/not_exhaustive_enough.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/not_exhaustive_enough.rs b/clippy_lints/src/not_exhaustive_enough.rs index 73258da5fd97..a7213a96e149 100644 --- a/clippy_lints/src/not_exhaustive_enough.rs +++ b/clippy_lints/src/not_exhaustive_enough.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { /// ``` pub NOT_EXHAUSTIVE_ENOUGH, pedantic, - "warn the user for missing variants or fields despite having a wildcard arm or a rest pattern" + "missing variants or fields in a pattern despite having a wildcard arm or a rest pattern" } declare_lint_pass!(NotExhaustiveEnough => [NOT_EXHAUSTIVE_ENOUGH]); From 6b0f8a052489e13e9c812fe1def9adb947757a3b Mon Sep 17 00:00:00 2001 From: jrqc Date: Wed, 30 Dec 2020 01:06:34 +0300 Subject: [PATCH 5/9] not_exhaustive_enough --- clippy_lints/src/not_exhaustive_enough.rs | 15 +-- tests/ui/not_exhaustive_enough.rs | 61 +++++++++ tests/ui/not_exhaustive_enough.stderr | 146 ++++++++++++++++------ 3 files changed, 178 insertions(+), 44 deletions(-) diff --git a/clippy_lints/src/not_exhaustive_enough.rs b/clippy_lints/src/not_exhaustive_enough.rs index a7213a96e149..b988947b7509 100644 --- a/clippy_lints/src/not_exhaustive_enough.rs +++ b/clippy_lints/src/not_exhaustive_enough.rs @@ -8,7 +8,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// **What it does:** - /// As suggested in the [non_exhaustive RFC](https://github.com/rust-lang/rfcs/blob/master/text/2008-non-exhaustive.md#unresolved-questions), + /// As suggested in the [non_exhaustive RFC](https://github.com/rust-lang/rfcs/blob/master/text/2008-non-exhaustive.md#unresolved-questions), /// when using non-exhaustive enums and structs in patterns, /// this lint warns the user for missing variants or fields despite having a wildcard arm or a rest pattern. /// @@ -82,8 +82,8 @@ fn check_path_pat<'tcx>(cx: &LateContext<'tcx>, arms: &[Arm<'_>], e: &'tcx Expr< cx, NOT_EXHAUSTIVE_ENOUGH, e.span, - "Enum not exhaustive enough", - "try adding missing field/s", + "enum match is not exhaustive enough", + "try adding missing variants", missing_variants.join(" , "), Applicability::MaybeIncorrect, ); @@ -96,13 +96,15 @@ fn check_struct_pat<'tcx>( field_defs: &[ty::FieldDef], ) { let missing_fields = get_missing_fields(field_pats, field_defs); + let mut suggestions = vec![]; + suggestions = missing_fields.iter().map(|v| v.to_owned() + ": _").collect(); span_lint_and_sugg( cx, NOT_EXHAUSTIVE_ENOUGH, pat.span, - "Struct not exhaustive enough", - "try adding missing field/s", - missing_fields.join(" , "), + "struct match is not exhaustive enough", + "try adding missing fields", + suggestions.join(" , "), Applicability::MaybeIncorrect, ); } @@ -134,7 +136,6 @@ fn get_missing_variants<'tcx>(cx: &LateContext<'tcx>, arms: &[Arm<'_>], e: &'tcx missing_variants } -// refactor - better way? fn get_missing_fields(field_pats: &[FieldPat<'_>], field_defs: &[ty::FieldDef]) -> Vec { let mut missing_fields = vec![]; let mut field_match = false; diff --git a/tests/ui/not_exhaustive_enough.rs b/tests/ui/not_exhaustive_enough.rs index 32394e2499f9..19cc5e1bf05a 100644 --- a/tests/ui/not_exhaustive_enough.rs +++ b/tests/ui/not_exhaustive_enough.rs @@ -1,4 +1,6 @@ #![warn(clippy::not_exhaustive_enough)] +#![allow(clippy::many_single_char_names)] +#![allow(clippy::never_loop)] use std::io::ErrorKind; @@ -9,6 +11,13 @@ pub enum E { Third, } +#[non_exhaustive] +pub enum K { + First(String), + Second(u32, u32), + Third(String) +} + #[derive(Default)] #[non_exhaustive] pub struct S { @@ -21,6 +30,10 @@ pub struct S { #[non_exhaustive] pub struct T(pub i32, pub i32, pub i32); +#[derive(Default)] +#[non_exhaustive] +pub struct W(pub i32, pub i32, i32); + fn main() { //////// Enum @@ -32,6 +45,16 @@ fn main() { _ => {}, } + // + let example = "Example".to_string(); + let k = K::First(example); + + match k { + K::First(..) => {}, + K::Second(..) => {}, + _ => {}, + } + //////// Struct let S { a: _, b: _, .. } = S::default(); @@ -47,6 +70,14 @@ fn main() { for S { a: _, b: _, .. } in v {} + while let S { a: 42, b: _, .. } = S::default() { + break; + } + + pub fn take_s(S { a, b, .. }: S) -> (i32, i32) { + (a, b) + } + //////// Tuple Struct let T { 0: _, 1: _, .. } = T::default(); @@ -61,6 +92,36 @@ fn main() { let v = vec![T::default()]; for T { 0: _, 1: _, .. } in v {} + while let T { 0: 42, 1: _, .. } = T::default() { + break; + } + + pub fn take_t(T { 0: _, 1: _, .. }: T) -> (i32, i32) { + (0, 1) + } + + // + + let W { 0: _, 1: _, .. } = W::default(); + + match W::default() { + W { 0: 42, 1: 21, .. } => {}, + W { 0: _, 1: _, .. } => {}, + } + + if let W { 0: 42, 1: _, .. } = W::default() {} + + let m = vec![W::default()]; + for W { 0: _, 1: _, .. } in m {} + + while let W { 0: 42, 1: _, .. } = W::default() { + break; + } + + pub fn take_w(W { 0: _, 1: _, .. }: W) -> (i32, i32) { + (0, 1) + } + // Enum - Another Crate let error_kind = ErrorKind::ConnectionReset; diff --git a/tests/ui/not_exhaustive_enough.stderr b/tests/ui/not_exhaustive_enough.stderr index 467a8d96100c..5a0156b066c0 100644 --- a/tests/ui/not_exhaustive_enough.stderr +++ b/tests/ui/not_exhaustive_enough.stderr @@ -1,76 +1,148 @@ -error: Enum not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:29:11 +error: enum match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:42:11 | LL | match e { - | ^ help: try adding missing field/s: `E::Third` + | ^ help: try adding missing variants: `E::Third` | = note: `-D clippy::not-exhaustive-enough` implied by `-D warnings` -error: Struct not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:37:9 +error: enum match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:52:11 + | +LL | match k { + | ^ help: try adding missing variants: `K::Third` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:60:9 | LL | let S { a: _, b: _, .. } = S::default(); - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `c` + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` -error: Struct not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:40:9 +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:63:9 | LL | S { a: 42, b: 21, .. } => {}, - | ^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `c` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` -error: Struct not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:41:9 +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:64:9 | LL | S { a: _, b: _, .. } => {}, - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `c` + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` -error: Struct not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:44:12 +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:67:12 | LL | if let S { a: 42, b: _, .. } = S::default() {} - | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `c` + | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` -error: Struct not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:48:9 +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:71:9 | LL | for S { a: _, b: _, .. } in v {} - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `c` + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:73:15 + | +LL | while let S { a: 42, b: _, .. } = S::default() { + | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:77:19 + | +LL | pub fn take_s(S { a, b, .. }: S) -> (i32, i32) { + | ^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` -error: Struct not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:52:9 +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:83:9 | LL | let T { 0: _, 1: _, .. } = T::default(); - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `2` + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` -error: Struct not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:55:9 +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:86:9 | LL | T { 0: 42, 1: 21, .. } => {}, - | ^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `2` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` -error: Struct not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:56:9 +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:87:9 | LL | T { 0: _, 1: _, .. } => {}, - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `2` + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` -error: Struct not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:59:12 +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:90:12 | LL | if let T { 0: 42, 1: _, .. } = T::default() {} - | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `2` + | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` -error: Struct not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:62:9 +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:93:9 | LL | for T { 0: _, 1: _, .. } in v {} - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing field/s: `2` + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:95:15 + | +LL | while let T { 0: 42, 1: _, .. } = T::default() { + | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:99:19 + | +LL | pub fn take_t(T { 0: _, 1: _, .. }: T) -> (i32, i32) { + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:105:9 + | +LL | let W { 0: _, 1: _, .. } = W::default(); + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:108:9 + | +LL | W { 0: 42, 1: 21, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:109:9 + | +LL | W { 0: _, 1: _, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:112:12 + | +LL | if let W { 0: 42, 1: _, .. } = W::default() {} + | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:115:9 + | +LL | for W { 0: _, 1: _, .. } in m {} + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:117:15 + | +LL | while let W { 0: 42, 1: _, .. } = W::default() { + | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:121:19 + | +LL | pub fn take_w(W { 0: _, 1: _, .. }: W) -> (i32, i32) { + | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` -error: Enum not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:67:11 +error: enum match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:128:11 | LL | match error_kind { - | ^^^^^^^^^^ help: try adding missing field/s: `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 adding missing variants: `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` -error: aborting due to 12 previous errors +error: aborting due to 24 previous errors From c0e13c20e9b7fa7e1eb5312251e81a718e4d0274 Mon Sep 17 00:00:00 2001 From: jrqc Date: Wed, 30 Dec 2020 08:03:52 +0300 Subject: [PATCH 6/9] not_exhaustive_enough --- clippy_lints/src/not_exhaustive_enough.rs | 4 +- .../auxiliary/not_exhaustive_enough_helper.rs | 20 ++++ tests/ui/not_exhaustive_enough.rs | 95 +++++++++++++------ 3 files changed, 90 insertions(+), 29 deletions(-) create mode 100644 tests/ui/auxiliary/not_exhaustive_enough_helper.rs diff --git a/clippy_lints/src/not_exhaustive_enough.rs b/clippy_lints/src/not_exhaustive_enough.rs index b988947b7509..cf7e2087b829 100644 --- a/clippy_lints/src/not_exhaustive_enough.rs +++ b/clippy_lints/src/not_exhaustive_enough.rs @@ -96,6 +96,7 @@ fn check_struct_pat<'tcx>( field_defs: &[ty::FieldDef], ) { let missing_fields = get_missing_fields(field_pats, field_defs); + if missing_fields.len() > 0 { let mut suggestions = vec![]; suggestions = missing_fields.iter().map(|v| v.to_owned() + ": _").collect(); span_lint_and_sugg( @@ -107,6 +108,7 @@ fn check_struct_pat<'tcx>( suggestions.join(" , "), Applicability::MaybeIncorrect, ); + } } fn get_missing_variants<'tcx>(cx: &LateContext<'tcx>, arms: &[Arm<'_>], e: &'tcx Expr<'_>) -> Vec { @@ -147,7 +149,7 @@ fn get_missing_fields(field_pats: &[FieldPat<'_>], field_defs: &[ty::FieldDef]) break; } } - if !&field_match { + if !&field_match && field_def.vis.is_visible_locally() { missing_fields.push(field_def.ident.name.to_ident_string()) } field_match = false; diff --git a/tests/ui/auxiliary/not_exhaustive_enough_helper.rs b/tests/ui/auxiliary/not_exhaustive_enough_helper.rs new file mode 100644 index 000000000000..ce6761877e04 --- /dev/null +++ b/tests/ui/auxiliary/not_exhaustive_enough_helper.rs @@ -0,0 +1,20 @@ + + +#[non_exhaustive] +pub enum AnotherCrateEnum { + AFirst, + ASecond, + AThird, +} + +#[derive(Default)] +#[non_exhaustive] +pub struct AnotherCrateStruct { + pub a1: i32, + pub b1: i32, + pub c1: i32, +} + +#[derive(Default)] +#[non_exhaustive] +pub struct TPrivateField(pub i32, pub i32, i32); diff --git a/tests/ui/not_exhaustive_enough.rs b/tests/ui/not_exhaustive_enough.rs index 19cc5e1bf05a..a65543bf752a 100644 --- a/tests/ui/not_exhaustive_enough.rs +++ b/tests/ui/not_exhaustive_enough.rs @@ -1,8 +1,12 @@ +// aux-build:not_exhaustive_enough_helper.rs + #![warn(clippy::not_exhaustive_enough)] #![allow(clippy::many_single_char_names)] #![allow(clippy::never_loop)] +#![allow(clippy::single_match)] -use std::io::ErrorKind; +extern crate not_exhaustive_enough_helper; +use not_exhaustive_enough_helper::{AnotherCrateEnum, AnotherCrateStruct, TPrivateField}; #[non_exhaustive] pub enum E { @@ -18,6 +22,17 @@ pub enum K { Third(String) } +enum EF { + #[non_exhaustive] + V{a: i32, b: i32} +} + +enum F { + #[non_exhaustive] + V{a: i32, b: i32}, + A{c: u32} +} + #[derive(Default)] #[non_exhaustive] pub struct S { @@ -30,21 +45,28 @@ pub struct S { #[non_exhaustive] pub struct T(pub i32, pub i32, pub i32); -#[derive(Default)] -#[non_exhaustive] -pub struct W(pub i32, pub i32, i32); fn main() { //////// Enum let e = E::First; + let ef = EF::V{a: 1, b:2}; + + let f = F::V{a:1, b:2}; + match e { E::First => {}, E::Second => {}, _ => {}, } + match ef { + EF::V{a:_, ..} => {} + } + + if let F::V{a:_, ..} = f {} + // let example = "Example".to_string(); let k = K::First(example); @@ -100,45 +122,62 @@ fn main() { (0, 1) } - // + //////// Tuple Struct - private field + + let TPrivateField { 0: _, 1: _, .. } = TPrivateField::default(); - let W { 0: _, 1: _, .. } = W::default(); + match TPrivateField::default() { + TPrivateField { 0: 42, 1: 21, .. } => {}, + TPrivateField { 0: _, 1: _, .. } => {}, + } - match W::default() { - W { 0: 42, 1: 21, .. } => {}, - W { 0: _, 1: _, .. } => {}, + match TPrivateField::default() { + TPrivateField {1: 21, .. } => {}, + _ => {} } - if let W { 0: 42, 1: _, .. } = W::default() {} + if let TPrivateField { 0: 42, 1: _, .. } = TPrivateField::default() {} - let m = vec![W::default()]; - for W { 0: _, 1: _, .. } in m {} + let m = vec![TPrivateField::default()]; + for TPrivateField { 0: _, 1: _, .. } in m {} - while let W { 0: 42, 1: _, .. } = W::default() { + while let TPrivateField { 0: 42, 1: _, .. } = TPrivateField::default() { break; } - pub fn take_w(W { 0: _, 1: _, .. }: W) -> (i32, i32) { + pub fn take_w(TPrivateField { 0: _, 1: _, .. }: TPrivateField) -> (i32, i32) { (0, 1) } // Enum - Another Crate - let error_kind = ErrorKind::ConnectionReset; - - match error_kind { - ErrorKind::NotFound => {}, - ErrorKind::PermissionDenied => {}, - ErrorKind::ConnectionRefused => {}, - ErrorKind::ConnectionReset => {}, - ErrorKind::ConnectionAborted => {}, - ErrorKind::NotConnected => {}, - ErrorKind::AddrInUse => {}, - ErrorKind::AddrNotAvailable => {}, - ErrorKind::BrokenPipe => {}, - ErrorKind::AlreadyExists => {}, + + let another_crate_enum = AnotherCrateEnum::AFirst; + + match another_crate_enum { + AnotherCrateEnum::AFirst => {}, _ => {}, } // Struct - Another Crate - // todo + + let AnotherCrateStruct { a1: _, b1: _, .. } = AnotherCrateStruct::default(); + + match AnotherCrateStruct::default() { + AnotherCrateStruct { a1: 42, b1: 21, .. } => {}, + AnotherCrateStruct { a1: _, b1: _, .. } => {}, + } + + if let AnotherCrateStruct { a1: 42, b1: _, .. } = AnotherCrateStruct::default() {} + + let a_v = vec![AnotherCrateStruct::default()]; + + for AnotherCrateStruct { a1: _, b1: _, .. } in a_v {} + + while let AnotherCrateStruct { a1: 42, b1: _, .. } = AnotherCrateStruct::default() { + break; + } + + pub fn take_a_s(AnotherCrateStruct { a1, b1, .. }: AnotherCrateStruct) -> (i32, i32) { + (a1, b1) + } } From dfb0ba15b2c808bde6aaa97a5065c8e2a38cd2d7 Mon Sep 17 00:00:00 2001 From: jrqc Date: Wed, 30 Dec 2020 08:06:32 +0300 Subject: [PATCH 7/9] not_exhaustive_enough --- clippy_lints/src/not_exhaustive_enough.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_lints/src/not_exhaustive_enough.rs b/clippy_lints/src/not_exhaustive_enough.rs index cf7e2087b829..8608cc80f896 100644 --- a/clippy_lints/src/not_exhaustive_enough.rs +++ b/clippy_lints/src/not_exhaustive_enough.rs @@ -122,6 +122,7 @@ fn get_missing_variants<'tcx>(cx: &LateContext<'tcx>, arms: &[Arm<'_>], e: &'tcx for arm in arms { if let PatKind::Path(ref path) = arm.pat.kind { if let QPath::Resolved(_, p) = path { + println!("{:?}", path); missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); } } else if let PatKind::TupleStruct(ref path, ref patterns, ..) = arm.pat.kind { From 46741cbcc1d22d423b2cf05dc763a40f389a465b Mon Sep 17 00:00:00 2001 From: jrqc Date: Wed, 30 Dec 2020 09:55:28 +0300 Subject: [PATCH 8/9] not_exhaustive_enough --- clippy_lints/src/not_exhaustive_enough.rs | 36 ++-- clippy_lints/src/types.rs | 4 +- .../auxiliary/not_exhaustive_enough_helper.rs | 2 - tests/ui/not_exhaustive_enough.rs | 96 +++++----- tests/ui/not_exhaustive_enough.stderr | 166 ++++++++++-------- 5 files changed, 160 insertions(+), 144 deletions(-) diff --git a/clippy_lints/src/not_exhaustive_enough.rs b/clippy_lints/src/not_exhaustive_enough.rs index 8608cc80f896..678af99cc9cd 100644 --- a/clippy_lints/src/not_exhaustive_enough.rs +++ b/clippy_lints/src/not_exhaustive_enough.rs @@ -47,7 +47,6 @@ impl<'tcx> LateLintPass<'tcx> for NotExhaustiveEnough { if_chain! { if let PatKind::Struct(_, ref field_pats, ref rest_pat) = &pat.kind; if let ty::Adt(adt_def, _) = cx.typeck_results().pat_ty(pat).kind(); - if adt_def.is_struct(); if is_struct_not_exhaustive(adt_def); if *rest_pat; if !field_pats.is_empty(); @@ -96,18 +95,17 @@ fn check_struct_pat<'tcx>( field_defs: &[ty::FieldDef], ) { let missing_fields = get_missing_fields(field_pats, field_defs); - if missing_fields.len() > 0 { - let mut suggestions = vec![]; - suggestions = missing_fields.iter().map(|v| v.to_owned() + ": _").collect(); - span_lint_and_sugg( - cx, - NOT_EXHAUSTIVE_ENOUGH, - pat.span, - "struct match is not exhaustive enough", - "try adding missing fields", - suggestions.join(" , "), - Applicability::MaybeIncorrect, - ); + if !missing_fields.is_empty() { + let suggestions: Vec = missing_fields.iter().map(|v| v.to_owned() + ": _").collect(); + span_lint_and_sugg( + cx, + NOT_EXHAUSTIVE_ENOUGH, + pat.span, + "struct match is not exhaustive enough", + "try adding missing fields", + suggestions.join(" , "), + Applicability::MaybeIncorrect, + ); } } @@ -122,16 +120,12 @@ fn get_missing_variants<'tcx>(cx: &LateContext<'tcx>, arms: &[Arm<'_>], e: &'tcx for arm in arms { if let PatKind::Path(ref path) = arm.pat.kind { if let QPath::Resolved(_, p) = path { - println!("{:?}", path); missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id())); } - } else if let PatKind::TupleStruct(ref path, ref patterns, ..) = arm.pat.kind { - if let QPath::Resolved(_, p) = path { - 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())); - } + } else if let PatKind::TupleStruct(QPath::Resolved(_, p), ref patterns, ..) = arm.pat.kind { + 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())); } } } diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index fd74783335d5..74ba53e6a9a0 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1104,9 +1104,7 @@ fn is_empty_block(expr: &Expr<'_>) -> bool { expr.kind, ExprKind::Block( Block { - stmts: &[], - expr: None, - .. + stmts: &[], expr: None, .. }, _, ) diff --git a/tests/ui/auxiliary/not_exhaustive_enough_helper.rs b/tests/ui/auxiliary/not_exhaustive_enough_helper.rs index ce6761877e04..6ef6f355c6a5 100644 --- a/tests/ui/auxiliary/not_exhaustive_enough_helper.rs +++ b/tests/ui/auxiliary/not_exhaustive_enough_helper.rs @@ -1,5 +1,3 @@ - - #[non_exhaustive] pub enum AnotherCrateEnum { AFirst, diff --git a/tests/ui/not_exhaustive_enough.rs b/tests/ui/not_exhaustive_enough.rs index a65543bf752a..e76ae47ee507 100644 --- a/tests/ui/not_exhaustive_enough.rs +++ b/tests/ui/not_exhaustive_enough.rs @@ -9,33 +9,38 @@ extern crate not_exhaustive_enough_helper; use not_exhaustive_enough_helper::{AnotherCrateEnum, AnotherCrateStruct, TPrivateField}; #[non_exhaustive] -pub enum E { +pub enum DefaultEnum { First, Second, Third, } #[non_exhaustive] -pub enum K { +pub enum DataEnum { First(String), Second(u32, u32), - Third(String) + Third(String), } -enum EF { +pub enum StructVariantEnum1 { #[non_exhaustive] - V{a: i32, b: i32} + V { a: i32, b: i32 }, } -enum F { +pub enum StructVariantEnum2 { #[non_exhaustive] - V{a: i32, b: i32}, - A{c: u32} + V { + a: i32, + b: i32, + }, + A { + c: u32, + }, } #[derive(Default)] #[non_exhaustive] -pub struct S { +pub struct DefaultStruct { pub a: i32, pub b: i32, pub c: i32, @@ -43,82 +48,85 @@ pub struct S { #[derive(Default)] #[non_exhaustive] -pub struct T(pub i32, pub i32, pub i32); - +pub struct DefaultTuple(pub i32, pub i32, pub i32); fn main() { //////// Enum - let e = E::First; + let default_enum = DefaultEnum::First; - let ef = EF::V{a: 1, b:2}; + let struct_variant_enum_1 = StructVariantEnum1::V { a: 1, b: 2 }; - let f = F::V{a:1, b:2}; + let struct_variant_enum_2 = StructVariantEnum2::V { a: 1, b: 2 }; - match e { - E::First => {}, - E::Second => {}, + match default_enum { + DefaultEnum::First => {}, + DefaultEnum::Second => {}, _ => {}, } - match ef { - EF::V{a:_, ..} => {} + match struct_variant_enum_1 { + StructVariantEnum1::V { a: _, .. } => {}, } - if let F::V{a:_, ..} = f {} + match struct_variant_enum_2 { + StructVariantEnum2::V { a: _, .. } => {}, + _ => {}, + } // + let example = "Example".to_string(); - let k = K::First(example); + let data_enum = DataEnum::First(example); - match k { - K::First(..) => {}, - K::Second(..) => {}, + match data_enum { + DataEnum::First(..) => {}, + DataEnum::Second(..) => {}, _ => {}, } //////// Struct - let S { a: _, b: _, .. } = S::default(); + let DefaultStruct { a: _, b: _, .. } = DefaultStruct::default(); - match S::default() { - S { a: 42, b: 21, .. } => {}, - S { a: _, b: _, .. } => {}, + match DefaultStruct::default() { + DefaultStruct { a: 42, b: 21, .. } => {}, + DefaultStruct { a: _, b: _, .. } => {}, } - if let S { a: 42, b: _, .. } = S::default() {} + if let DefaultStruct { a: 42, b: _, .. } = DefaultStruct::default() {} - let v = vec![S::default()]; + let v = vec![DefaultStruct::default()]; - for S { a: _, b: _, .. } in v {} + for DefaultStruct { a: _, b: _, .. } in v {} - while let S { a: 42, b: _, .. } = S::default() { + while let DefaultStruct { a: 42, b: _, .. } = DefaultStruct::default() { break; } - pub fn take_s(S { a, b, .. }: S) -> (i32, i32) { + pub fn take_s(DefaultStruct { a, b, .. }: DefaultStruct) -> (i32, i32) { (a, b) } //////// Tuple Struct - let T { 0: _, 1: _, .. } = T::default(); + let DefaultTuple { 0: _, 1: _, .. } = DefaultTuple::default(); - match T::default() { - T { 0: 42, 1: 21, .. } => {}, - T { 0: _, 1: _, .. } => {}, + match DefaultTuple::default() { + DefaultTuple { 0: 42, 1: 21, .. } => {}, + DefaultTuple { 0: _, 1: _, .. } => {}, } - if let T { 0: 42, 1: _, .. } = T::default() {} + if let DefaultTuple { 0: 42, 1: _, .. } = DefaultTuple::default() {} - let v = vec![T::default()]; - for T { 0: _, 1: _, .. } in v {} + let default_tuple = vec![DefaultTuple::default()]; + for DefaultTuple { 0: _, 1: _, .. } in default_tuple {} - while let T { 0: 42, 1: _, .. } = T::default() { + while let DefaultTuple { 0: 42, 1: _, .. } = DefaultTuple::default() { break; } - pub fn take_t(T { 0: _, 1: _, .. }: T) -> (i32, i32) { + pub fn take_t(DefaultTuple { 0: _, 1: _, .. }: DefaultTuple) -> (i32, i32) { (0, 1) } @@ -132,8 +140,8 @@ fn main() { } match TPrivateField::default() { - TPrivateField {1: 21, .. } => {}, - _ => {} + TPrivateField { 1: 21, .. } => {}, + _ => {}, } if let TPrivateField { 0: 42, 1: _, .. } = TPrivateField::default() {} diff --git a/tests/ui/not_exhaustive_enough.stderr b/tests/ui/not_exhaustive_enough.stderr index 5a0156b066c0..07411c3a473e 100644 --- a/tests/ui/not_exhaustive_enough.stderr +++ b/tests/ui/not_exhaustive_enough.stderr @@ -1,148 +1,166 @@ error: enum match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:42:11 + --> $DIR/not_exhaustive_enough.rs:62:11 | -LL | match e { - | ^ help: try adding missing variants: `E::Third` +LL | match default_enum { + | ^^^^^^^^^^^^ help: try adding missing variants: `DefaultEnum::Third` | = note: `-D clippy::not-exhaustive-enough` implied by `-D warnings` +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:69:9 + | +LL | StructVariantEnum1::V { a: _, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `b: _` + +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:73:9 + | +LL | StructVariantEnum2::V { a: _, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `b: _` + error: enum match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:52:11 + --> $DIR/not_exhaustive_enough.rs:82:11 | -LL | match k { - | ^ help: try adding missing variants: `K::Third` +LL | match data_enum { + | ^^^^^^^^^ help: try adding missing variants: `DataEnum::Third` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:60:9 + --> $DIR/not_exhaustive_enough.rs:90:9 | -LL | let S { a: _, b: _, .. } = S::default(); - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` +LL | let DefaultStruct { a: _, b: _, .. } = DefaultStruct::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:63:9 + --> $DIR/not_exhaustive_enough.rs:93:9 | -LL | S { a: 42, b: 21, .. } => {}, - | ^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` +LL | DefaultStruct { a: 42, b: 21, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:64:9 + --> $DIR/not_exhaustive_enough.rs:94:9 | -LL | S { a: _, b: _, .. } => {}, - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` +LL | DefaultStruct { a: _, b: _, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:67:12 + --> $DIR/not_exhaustive_enough.rs:97:12 | -LL | if let S { a: 42, b: _, .. } = S::default() {} - | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` +LL | if let DefaultStruct { a: 42, b: _, .. } = DefaultStruct::default() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:71:9 + --> $DIR/not_exhaustive_enough.rs:101:9 | -LL | for S { a: _, b: _, .. } in v {} - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` +LL | for DefaultStruct { a: _, b: _, .. } in v {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:73:15 + --> $DIR/not_exhaustive_enough.rs:103:15 | -LL | while let S { a: 42, b: _, .. } = S::default() { - | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` +LL | while let DefaultStruct { a: 42, b: _, .. } = DefaultStruct::default() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:77:19 + --> $DIR/not_exhaustive_enough.rs:107:19 | -LL | pub fn take_s(S { a, b, .. }: S) -> (i32, i32) { - | ^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` +LL | pub fn take_s(DefaultStruct { a, b, .. }: DefaultStruct) -> (i32, i32) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:83:9 + --> $DIR/not_exhaustive_enough.rs:113:9 | -LL | let T { 0: _, 1: _, .. } = T::default(); - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | let DefaultTuple { 0: _, 1: _, .. } = DefaultTuple::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:86:9 + --> $DIR/not_exhaustive_enough.rs:116:9 | -LL | T { 0: 42, 1: 21, .. } => {}, - | ^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | DefaultTuple { 0: 42, 1: 21, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:87:9 + --> $DIR/not_exhaustive_enough.rs:117:9 | -LL | T { 0: _, 1: _, .. } => {}, - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | DefaultTuple { 0: _, 1: _, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:90:12 + --> $DIR/not_exhaustive_enough.rs:120:12 | -LL | if let T { 0: 42, 1: _, .. } = T::default() {} - | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | if let DefaultTuple { 0: 42, 1: _, .. } = DefaultTuple::default() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:93:9 + --> $DIR/not_exhaustive_enough.rs:123:9 | -LL | for T { 0: _, 1: _, .. } in v {} - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | for DefaultTuple { 0: _, 1: _, .. } in default_tuple {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:95:15 + --> $DIR/not_exhaustive_enough.rs:125:15 | -LL | while let T { 0: 42, 1: _, .. } = T::default() { - | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | while let DefaultTuple { 0: 42, 1: _, .. } = DefaultTuple::default() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:99:19 + --> $DIR/not_exhaustive_enough.rs:129:19 | -LL | pub fn take_t(T { 0: _, 1: _, .. }: T) -> (i32, i32) { - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | pub fn take_t(DefaultTuple { 0: _, 1: _, .. }: DefaultTuple) -> (i32, i32) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:105:9 + --> $DIR/not_exhaustive_enough.rs:143:9 + | +LL | TPrivateField { 1: 21, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `0: _` + +error: enum match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:164:11 | -LL | let W { 0: _, 1: _, .. } = W::default(); - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | match another_crate_enum { + | ^^^^^^^^^^^^^^^^^^ help: try adding missing variants: `not_exhaustive_enough_helper::AnotherCrateEnum::ASecond , not_exhaustive_enough_helper::AnotherCrateEnum::AThird` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:108:9 + --> $DIR/not_exhaustive_enough.rs:171:9 | -LL | W { 0: 42, 1: 21, .. } => {}, - | ^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | let AnotherCrateStruct { a1: _, b1: _, .. } = AnotherCrateStruct::default(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c1: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:109:9 + --> $DIR/not_exhaustive_enough.rs:174:9 | -LL | W { 0: _, 1: _, .. } => {}, - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | AnotherCrateStruct { a1: 42, b1: 21, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c1: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:112:12 + --> $DIR/not_exhaustive_enough.rs:175:9 | -LL | if let W { 0: 42, 1: _, .. } = W::default() {} - | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | AnotherCrateStruct { a1: _, b1: _, .. } => {}, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c1: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:115:9 + --> $DIR/not_exhaustive_enough.rs:178:12 | -LL | for W { 0: _, 1: _, .. } in m {} - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | if let AnotherCrateStruct { a1: 42, b1: _, .. } = AnotherCrateStruct::default() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c1: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:117:15 + --> $DIR/not_exhaustive_enough.rs:182:9 | -LL | while let W { 0: 42, 1: _, .. } = W::default() { - | ^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | for AnotherCrateStruct { a1: _, b1: _, .. } in a_v {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c1: _` error: struct match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:121:19 + --> $DIR/not_exhaustive_enough.rs:184:15 | -LL | pub fn take_w(W { 0: _, 1: _, .. }: W) -> (i32, i32) { - | ^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `2: _` +LL | while let AnotherCrateStruct { a1: 42, b1: _, .. } = AnotherCrateStruct::default() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c1: _` -error: enum match is not exhaustive enough - --> $DIR/not_exhaustive_enough.rs:128:11 +error: struct match is not exhaustive enough + --> $DIR/not_exhaustive_enough.rs:188:21 | -LL | match error_kind { - | ^^^^^^^^^^ help: try adding missing variants: `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` +LL | pub fn take_a_s(AnotherCrateStruct { a1, b1, .. }: AnotherCrateStruct) -> (i32, i32) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try adding missing fields: `c1: _` -error: aborting due to 24 previous errors +error: aborting due to 27 previous errors From 891d91472ce3884960e119062696a32617e39515 Mon Sep 17 00:00:00 2001 From: jrqc Date: Wed, 30 Dec 2020 10:12:48 +0300 Subject: [PATCH 9/9] not_exhaustive_enough --- clippy_lints/src/types.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index 74ba53e6a9a0..fd74783335d5 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -1104,7 +1104,9 @@ fn is_empty_block(expr: &Expr<'_>) -> bool { expr.kind, ExprKind::Block( Block { - stmts: &[], expr: None, .. + stmts: &[], + expr: None, + .. }, _, )