@@ -220,7 +220,7 @@ declare_clippy_lint! {
220220 /// # enum Foo { A(usize), B(usize) }
221221 /// # let x = Foo::B(1);
222222 /// match x {
223- /// A => {},
223+ /// Foo::A(_) => {},
224224 /// _ => {},
225225 /// }
226226 /// ```
@@ -229,6 +229,40 @@ declare_clippy_lint! {
229229 "a wildcard enum match arm using `_`"
230230}
231231
232+ declare_clippy_lint ! {
233+ /// **What it does:** Checks for wildcard enum matches for a single variant.
234+ ///
235+ /// **Why is this bad?** New enum variants added by library updates can be missed.
236+ ///
237+ /// **Known problems:** Suggested replacements may not use correct path to enum
238+ /// if it's not present in the current scope.
239+ ///
240+ /// **Example:**
241+ ///
242+ /// ```rust
243+ /// # enum Foo { A, B, C }
244+ /// # let x = Foo::B;
245+ /// match x {
246+ /// Foo::A => {},
247+ /// Foo::B => {},
248+ /// _ => {},
249+ /// }
250+ /// ```
251+ /// Use instead:
252+ /// ```rust
253+ /// # enum Foo { A, B, C }
254+ /// # let x = Foo::B;
255+ /// match x {
256+ /// Foo::A => {},
257+ /// Foo::B => {},
258+ /// Foo::C => {},
259+ /// }
260+ /// ```
261+ pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS ,
262+ pedantic,
263+ "a wildcard enum match for a single variant"
264+ }
265+
232266declare_clippy_lint ! {
233267 /// **What it does:** Checks for wildcard pattern used with others patterns in same match arm.
234268 ///
@@ -356,6 +390,7 @@ impl_lint_pass!(Matches => [
356390 MATCH_WILD_ERR_ARM ,
357391 MATCH_AS_REF ,
358392 WILDCARD_ENUM_MATCH_ARM ,
393+ MATCH_WILDCARD_FOR_SINGLE_VARIANTS ,
359394 WILDCARD_IN_OR_PATTERNS ,
360395 MATCH_SINGLE_BINDING ,
361396 INFALLIBLE_DESTRUCTURING_MATCH ,
@@ -729,9 +764,21 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_
729764 if let QPath :: Resolved ( _, p) = path {
730765 missing_variants. retain ( |e| e. ctor_def_id != Some ( p. res . def_id ( ) ) ) ;
731766 }
732- } else if let PatKind :: TupleStruct ( ref path, ..) = arm. pat . kind {
767+ } else if let PatKind :: TupleStruct ( ref path, ref patterns , ..) = arm. pat . kind {
733768 if let QPath :: Resolved ( _, p) = path {
734- missing_variants. retain ( |e| e. ctor_def_id != Some ( p. res . def_id ( ) ) ) ;
769+ // Some simple checks for exhaustive patterns.
770+ // There is a room for improvements to detect more cases,
771+ // but it can be more expensive to do so.
772+ let is_pattern_exhaustive = |pat : & & Pat < ' _ > | {
773+ if let PatKind :: Wild | PatKind :: Binding ( .., None ) = pat. kind {
774+ true
775+ } else {
776+ false
777+ }
778+ } ;
779+ if patterns. iter ( ) . all ( is_pattern_exhaustive) {
780+ missing_variants. retain ( |e| e. ctor_def_id != Some ( p. res . def_id ( ) ) ) ;
781+ }
735782 }
736783 }
737784 }
@@ -766,14 +813,27 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_
766813 }
767814 }
768815
816+ if suggestion. len ( ) == 1 {
817+ // No need to check for non-exhaustive enum as in that case len would be greater than 1
818+ span_lint_and_sugg (
819+ cx,
820+ MATCH_WILDCARD_FOR_SINGLE_VARIANTS ,
821+ wildcard_span,
822+ message,
823+ "try this" ,
824+ suggestion[ 0 ] . clone ( ) ,
825+ Applicability :: MaybeIncorrect ,
826+ )
827+ } ;
828+
769829 span_lint_and_sugg (
770830 cx,
771831 WILDCARD_ENUM_MATCH_ARM ,
772832 wildcard_span,
773833 message,
774834 "try this" ,
775835 suggestion. join ( " | " ) ,
776- Applicability :: MachineApplicable ,
836+ Applicability :: MaybeIncorrect ,
777837 )
778838 }
779839}
0 commit comments