@@ -195,6 +195,40 @@ 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 (`..`) 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+
198232declare_lint_pass ! ( MiscEarlyLints => [
199233 UNNEEDED_FIELD_PATTERN ,
200234 DUPLICATE_UNDERSCORE_ARGUMENT ,
@@ -204,7 +238,8 @@ declare_lint_pass!(MiscEarlyLints => [
204238 UNSEPARATED_LITERAL_SUFFIX ,
205239 ZERO_PREFIXED_LITERAL ,
206240 BUILTIN_TYPE_SHADOW ,
207- REDUNDANT_PATTERN
241+ REDUNDANT_PATTERN ,
242+ UNNEEDED_WILDCARD_PATTERN ,
208243] ) ;
209244
210245// Used to find `return` statements or equivalents e.g., `?`
@@ -326,6 +361,62 @@ impl EarlyLintPass for MiscEarlyLints {
326361 ) ;
327362 }
328363 }
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+ }
329420 }
330421
331422 fn check_fn ( & mut self , cx : & EarlyContext < ' _ > , _: FnKind < ' _ > , decl : & FnDecl , _: Span , _: NodeId ) {
0 commit comments