@@ -195,6 +195,40 @@ 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 (`..`) pattern.
201
+ ///
202
+ /// **Why is this bad?** The wildcard pattern is unneeded as the rest pattern
203
+ /// can match that element as well.
204
+ ///
205
+ /// **Known problems:** None.
206
+ ///
207
+ /// **Example:**
208
+ /// ```rust
209
+ /// # struct TupleStruct(u32, u32, u32);
210
+ /// # let t = TupleStruct(1, 2, 3);
211
+ ///
212
+ /// match t {
213
+ /// TupleStruct(0, .., _) => (),
214
+ /// _ => (),
215
+ /// }
216
+ /// ```
217
+ /// can be written as
218
+ /// ```rust
219
+ /// # struct TupleStruct(u32, u32, u32);
220
+ /// # let t = TupleStruct(1, 2, 3);
221
+ ///
222
+ /// match t {
223
+ /// TupleStruct(0, ..) => (),
224
+ /// _ => (),
225
+ /// }
226
+ /// ```
227
+ pub UNNEEDED_WILDCARD_PATTERN ,
228
+ complexity,
229
+ "tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`) pattern"
230
+ }
231
+
198
232
declare_lint_pass ! ( MiscEarlyLints => [
199
233
UNNEEDED_FIELD_PATTERN ,
200
234
DUPLICATE_UNDERSCORE_ARGUMENT ,
@@ -204,7 +238,8 @@ declare_lint_pass!(MiscEarlyLints => [
204
238
UNSEPARATED_LITERAL_SUFFIX ,
205
239
ZERO_PREFIXED_LITERAL ,
206
240
BUILTIN_TYPE_SHADOW ,
207
- REDUNDANT_PATTERN
241
+ REDUNDANT_PATTERN ,
242
+ UNNEEDED_WILDCARD_PATTERN ,
208
243
] ) ;
209
244
210
245
// Used to find `return` statements or equivalents e.g., `?`
@@ -326,6 +361,62 @@ impl EarlyLintPass for MiscEarlyLints {
326
361
) ;
327
362
}
328
363
}
364
+
365
+ if let PatKind :: TupleStruct ( _, ref patterns) | PatKind :: Tuple ( ref patterns) = pat. node {
366
+ fn span_lint ( cx : & EarlyContext < ' _ > , span : Span , only_one : bool ) {
367
+ span_lint_and_sugg (
368
+ cx,
369
+ UNNEEDED_WILDCARD_PATTERN ,
370
+ span,
371
+ if only_one {
372
+ "this pattern is unneeded as the `..` pattern can match that element"
373
+ } else {
374
+ "these patterns are unneeded as the `..` pattern can match those elements"
375
+ } ,
376
+ if only_one { "remove it" } else { "remove them" } ,
377
+ "" . to_string ( ) ,
378
+ Applicability :: MachineApplicable ,
379
+ ) ;
380
+ }
381
+
382
+ fn is_rest < P : std:: ops:: Deref < Target = Pat > > ( pat : & P ) -> bool {
383
+ if let PatKind :: Rest = pat. node {
384
+ true
385
+ } else {
386
+ false
387
+ }
388
+ }
389
+
390
+ fn is_wild < P : std:: ops:: Deref < Target = Pat > > ( pat : & & P ) -> bool {
391
+ if let PatKind :: Wild = pat. node {
392
+ true
393
+ } else {
394
+ false
395
+ }
396
+ }
397
+
398
+ if let Some ( rest_index) = patterns. iter ( ) . position ( is_rest) {
399
+ if let Some ( ( left_index, left_pat) ) = patterns[ ..rest_index]
400
+ . iter ( )
401
+ . rev ( )
402
+ . take_while ( is_wild)
403
+ . enumerate ( )
404
+ . last ( )
405
+ {
406
+ span_lint ( cx, left_pat. span . until ( patterns[ rest_index] . span ) , left_index == 0 ) ;
407
+ }
408
+
409
+ if let Some ( ( right_index, right_pat) ) =
410
+ patterns[ rest_index + 1 ..] . iter ( ) . take_while ( is_wild) . enumerate ( ) . last ( )
411
+ {
412
+ span_lint (
413
+ cx,
414
+ patterns[ rest_index] . span . shrink_to_hi ( ) . to ( right_pat. span ) ,
415
+ right_index == 0 ,
416
+ ) ;
417
+ }
418
+ }
419
+ }
329
420
}
330
421
331
422
fn check_fn ( & mut self , cx : & EarlyContext < ' _ > , _: FnKind < ' _ > , decl : & FnDecl , _: Span , _: NodeId ) {
0 commit comments