@@ -195,6 +195,44 @@ declare_clippy_lint! {
195195 "using `name @ _` in a pattern"
196196}
197197
198+ declare_clippy_lint ! {
199+ /// **What it does:** Checks for tuple patterns with a wildcard
200+ /// pattern (`_`) is next to a rest pattern (`..`).
201+ ///
202+ /// _NOTE_: While `_, ..` means there is at least one element left, `..`
203+ /// means there are 0 or more elements left. This can make a difference
204+ /// when refactoring, but shouldn't result in errors in the refactored code,
205+ /// since the wildcard pattern isn't used anyway.
206+ /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern
207+ /// can match that element as well.
208+ ///
209+ /// **Known problems:** None.
210+ ///
211+ /// **Example:**
212+ /// ```rust
213+ /// # struct TupleStruct(u32, u32, u32);
214+ /// # let t = TupleStruct(1, 2, 3);
215+ ///
216+ /// match t {
217+ /// TupleStruct(0, .., _) => (),
218+ /// _ => (),
219+ /// }
220+ /// ```
221+ /// can be written as
222+ /// ```rust
223+ /// # struct TupleStruct(u32, u32, u32);
224+ /// # let t = TupleStruct(1, 2, 3);
225+ ///
226+ /// match t {
227+ /// TupleStruct(0, ..) => (),
228+ /// _ => (),
229+ /// }
230+ /// ```
231+ pub UNNEEDED_WILDCARD_PATTERN ,
232+ complexity,
233+ "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
234+ }
235+
198236declare_lint_pass ! ( MiscEarlyLints => [
199237 UNNEEDED_FIELD_PATTERN ,
200238 DUPLICATE_UNDERSCORE_ARGUMENT ,
@@ -204,7 +242,8 @@ declare_lint_pass!(MiscEarlyLints => [
204242 UNSEPARATED_LITERAL_SUFFIX ,
205243 ZERO_PREFIXED_LITERAL ,
206244 BUILTIN_TYPE_SHADOW ,
207- REDUNDANT_PATTERN
245+ REDUNDANT_PATTERN ,
246+ UNNEEDED_WILDCARD_PATTERN ,
208247] ) ;
209248
210249// Used to find `return` statements or equivalents e.g., `?`
@@ -326,6 +365,8 @@ impl EarlyLintPass for MiscEarlyLints {
326365 ) ;
327366 }
328367 }
368+
369+ check_unneeded_wildcard_pattern ( cx, pat) ;
329370 }
330371
331372 fn check_fn ( & mut self , cx : & EarlyContext < ' _ > , _: FnKind < ' _ > , decl : & FnDecl , _: Span , _: NodeId ) {
@@ -520,3 +561,54 @@ impl MiscEarlyLints {
520561 }
521562 }
522563}
564+
565+ fn check_unneeded_wildcard_pattern ( cx : & EarlyContext < ' _ > , pat : & Pat ) {
566+ if let PatKind :: TupleStruct ( _, ref patterns) | PatKind :: Tuple ( ref patterns) = pat. node {
567+ fn span_lint ( cx : & EarlyContext < ' _ > , span : Span , only_one : bool ) {
568+ span_lint_and_sugg (
569+ cx,
570+ UNNEEDED_WILDCARD_PATTERN ,
571+ span,
572+ if only_one {
573+ "this pattern is unneeded as the `..` pattern can match that element"
574+ } else {
575+ "these patterns are unneeded as the `..` pattern can match those elements"
576+ } ,
577+ if only_one { "remove it" } else { "remove them" } ,
578+ "" . to_string ( ) ,
579+ Applicability :: MachineApplicable ,
580+ ) ;
581+ }
582+
583+ #[ allow( clippy:: trivially_copy_pass_by_ref) ]
584+ fn is_wild < P : std:: ops:: Deref < Target = Pat > > ( pat : & & P ) -> bool {
585+ if let PatKind :: Wild = pat. node {
586+ true
587+ } else {
588+ false
589+ }
590+ }
591+
592+ if let Some ( rest_index) = patterns. iter ( ) . position ( |pat| pat. is_rest ( ) ) {
593+ if let Some ( ( left_index, left_pat) ) = patterns[ ..rest_index]
594+ . iter ( )
595+ . rev ( )
596+ . take_while ( is_wild)
597+ . enumerate ( )
598+ . last ( )
599+ {
600+ span_lint ( cx, left_pat. span . until ( patterns[ rest_index] . span ) , left_index == 0 ) ;
601+ }
602+
603+ if let Some ( ( right_index, right_pat) ) =
604+ patterns[ rest_index + 1 ..] . iter ( ) . take_while ( is_wild) . enumerate ( ) . last ( )
605+ {
606+ span_lint (
607+ cx,
608+ patterns[ rest_index] . span . shrink_to_hi ( ) . to ( right_pat. span ) ,
609+ right_index == 0 ,
610+ ) ;
611+ }
612+ }
613+ }
614+ }
0 commit comments