@@ -14,8 +14,8 @@ use rustc_ast::ast::LitKind;
14
14
use rustc_errors:: Applicability ;
15
15
use rustc_hir:: def:: CtorKind ;
16
16
use rustc_hir:: {
17
- print, Arm , BindingAnnotation , Block , BorrowKind , Expr , ExprKind , Local , MatchSource , Mutability , PatKind , QPath ,
18
- RangeEnd ,
17
+ print, Arm , BindingAnnotation , Block , BorrowKind , Expr , ExprKind , Local , MatchSource , Mutability , Pat , PatKind ,
18
+ QPath , RangeEnd ,
19
19
} ;
20
20
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
21
21
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
@@ -311,6 +311,36 @@ declare_clippy_lint! {
311
311
"a match with a single binding instead of using `let` statement"
312
312
}
313
313
314
+ declare_clippy_lint ! {
315
+ /// **What it does:** Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
316
+ ///
317
+ /// **Why is this bad?** Correctness and readability. It's like having a wildcard pattern after
318
+ /// matching all enum variants explicitly.
319
+ ///
320
+ /// **Known problems:** None.
321
+ ///
322
+ /// **Example:**
323
+ /// ```rust
324
+ /// # struct A { a: i32 }
325
+ /// let a = A { a: 5 };
326
+ ///
327
+ /// // Bad
328
+ /// match a {
329
+ /// A { a: 5, .. } => {},
330
+ /// _ => {},
331
+ /// }
332
+ ///
333
+ /// // Good
334
+ /// match a {
335
+ /// A { a: 5 } => {},
336
+ /// _ => {},
337
+ /// }
338
+ /// ```
339
+ pub REST_PAT_IN_FULLY_BOUND_STRUCTS ,
340
+ restriction,
341
+ "a match on a struct that binds all fields but still uses the wildcard pattern"
342
+ }
343
+
314
344
#[ derive( Default ) ]
315
345
pub struct Matches {
316
346
infallible_destructuring_match_linted : bool ,
@@ -327,7 +357,8 @@ impl_lint_pass!(Matches => [
327
357
WILDCARD_ENUM_MATCH_ARM ,
328
358
WILDCARD_IN_OR_PATTERNS ,
329
359
MATCH_SINGLE_BINDING ,
330
- INFALLIBLE_DESTRUCTURING_MATCH
360
+ INFALLIBLE_DESTRUCTURING_MATCH ,
361
+ REST_PAT_IN_FULLY_BOUND_STRUCTS
331
362
] ) ;
332
363
333
364
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Matches {
@@ -388,6 +419,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
388
419
}
389
420
}
390
421
}
422
+
423
+ fn check_pat ( & mut self , cx : & LateContext < ' a , ' tcx > , pat : & ' tcx Pat < ' _ > ) {
424
+ if_chain ! {
425
+ if let PatKind :: Struct ( ref qpath, fields, true ) = pat. kind;
426
+ if let QPath :: Resolved ( _, ref path) = qpath;
427
+ let def_id = path. res. def_id( ) ;
428
+ let ty = cx. tcx. type_of( def_id) ;
429
+ if let ty:: Adt ( def, _) = ty. kind;
430
+ if fields. len( ) == def. non_enum_variant( ) . fields. len( ) ;
431
+
432
+ then {
433
+ span_lint_and_help(
434
+ cx,
435
+ REST_PAT_IN_FULLY_BOUND_STRUCTS ,
436
+ pat. span,
437
+ "unnecessary use of `..` pattern in struct binding. All fields were already bound" ,
438
+ "consider removing `..` from this binding" ,
439
+ ) ;
440
+ }
441
+ }
442
+ }
391
443
}
392
444
393
445
#[ rustfmt:: skip]
0 commit comments