@@ -14,8 +14,8 @@ use rustc_ast::ast::LitKind;
1414use rustc_errors:: Applicability ;
1515use rustc_hir:: def:: CtorKind ;
1616use 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 ,
1919} ;
2020use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
2121use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
@@ -311,6 +311,36 @@ declare_clippy_lint! {
311311 "a match with a single binding instead of using `let` statement"
312312}
313313
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+
314344#[ derive( Default ) ]
315345pub struct Matches {
316346 infallible_destructuring_match_linted : bool ,
@@ -327,7 +357,8 @@ impl_lint_pass!(Matches => [
327357 WILDCARD_ENUM_MATCH_ARM ,
328358 WILDCARD_IN_OR_PATTERNS ,
329359 MATCH_SINGLE_BINDING ,
330- INFALLIBLE_DESTRUCTURING_MATCH
360+ INFALLIBLE_DESTRUCTURING_MATCH ,
361+ REST_PAT_IN_FULLY_BOUND_STRUCTS
331362] ) ;
332363
333364impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Matches {
@@ -388,6 +419,28 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
388419 }
389420 }
390421 }
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+ if let Some ( def_id) = path. res. opt_def_id( ) ;
428+ let ty = cx. tcx. type_of( def_id) ;
429+ if let ty:: Adt ( def, _) = ty. kind;
430+ if def. is_struct( ) || def. is_union( ) ;
431+ if fields. len( ) == def. non_enum_variant( ) . fields. len( ) ;
432+
433+ then {
434+ span_lint_and_help(
435+ cx,
436+ REST_PAT_IN_FULLY_BOUND_STRUCTS ,
437+ pat. span,
438+ "unnecessary use of `..` pattern in struct binding. All fields were already bound" ,
439+ "consider removing `..` from this binding" ,
440+ ) ;
441+ }
442+ }
443+ }
391444}
392445
393446#[ rustfmt:: skip]
0 commit comments