@@ -195,6 +195,44 @@ declare_clippy_lint! {
195
195
"using `name @ _` in a pattern"
196
196
}
197
197
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
+
198
236
declare_lint_pass ! ( MiscEarlyLints => [
199
237
UNNEEDED_FIELD_PATTERN ,
200
238
DUPLICATE_UNDERSCORE_ARGUMENT ,
@@ -204,7 +242,8 @@ declare_lint_pass!(MiscEarlyLints => [
204
242
UNSEPARATED_LITERAL_SUFFIX ,
205
243
ZERO_PREFIXED_LITERAL ,
206
244
BUILTIN_TYPE_SHADOW ,
207
- REDUNDANT_PATTERN
245
+ REDUNDANT_PATTERN ,
246
+ UNNEEDED_WILDCARD_PATTERN ,
208
247
] ) ;
209
248
210
249
// Used to find `return` statements or equivalents e.g., `?`
@@ -326,6 +365,8 @@ impl EarlyLintPass for MiscEarlyLints {
326
365
) ;
327
366
}
328
367
}
368
+
369
+ check_unneeded_wildcard_pattern ( cx, pat) ;
329
370
}
330
371
331
372
fn check_fn ( & mut self , cx : & EarlyContext < ' _ > , _: FnKind < ' _ > , decl : & FnDecl , _: Span , _: NodeId ) {
@@ -520,3 +561,54 @@ impl MiscEarlyLints {
520
561
}
521
562
}
522
563
}
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