Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 96c4198

Browse files
committed
New lint: pats_with_wild_match_arm
- Wildcard use with other pattern in same match arm
1 parent c092068 commit 96c4198

File tree

7 files changed

+156
-1
lines changed

7 files changed

+156
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,7 @@ Released 2018-09-13
12411241
[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
12421242
[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
12431243
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
1244+
[`pats_with_wild_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#pats_with_wild_match_arm
12441245
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
12451246
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
12461247
[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
597597
&matches::MATCH_OVERLAPPING_ARM,
598598
&matches::MATCH_REF_PATS,
599599
&matches::MATCH_WILD_ERR_ARM,
600+
&matches::PATS_WITH_WILD_MATCH_ARM,
600601
&matches::SINGLE_MATCH,
601602
&matches::SINGLE_MATCH_ELSE,
602603
&matches::WILDCARD_ENUM_MATCH_ARM,
@@ -1001,6 +1002,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
10011002
LintId::of(&integer_division::INTEGER_DIVISION),
10021003
LintId::of(&let_underscore::LET_UNDERSCORE_MUST_USE),
10031004
LintId::of(&literal_representation::DECIMAL_LITERAL_REPRESENTATION),
1005+
LintId::of(&matches::PATS_WITH_WILD_MATCH_ARM),
10041006
LintId::of(&matches::WILDCARD_ENUM_MATCH_ARM),
10051007
LintId::of(&mem_forget::MEM_FORGET),
10061008
LintId::of(&methods::CLONE_ON_REF_PTR),

clippy_lints/src/matches.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,26 @@ declare_clippy_lint! {
223223
"a wildcard enum match arm using `_`"
224224
}
225225

226+
declare_clippy_lint! {
227+
/// **What it does:** Checks for wildcard pattern used with others patterns in same match arm.
228+
///
229+
/// **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway.
230+
/// It makes the code less readable, especially to spot wildcard pattern use in match arm.
231+
///
232+
/// **Known problems:** None.
233+
///
234+
/// **Example:**
235+
/// ```rust
236+
/// match "foo" {
237+
/// "a" => {},
238+
/// "bar" | _ => {},
239+
/// }
240+
/// ```
241+
pub PATS_WITH_WILD_MATCH_ARM,
242+
restriction,
243+
"a wildcard pattern used with others patterns in same match arm"
244+
}
245+
226246
declare_lint_pass!(Matches => [
227247
SINGLE_MATCH,
228248
MATCH_REF_PATS,
@@ -231,7 +251,8 @@ declare_lint_pass!(Matches => [
231251
MATCH_OVERLAPPING_ARM,
232252
MATCH_WILD_ERR_ARM,
233253
MATCH_AS_REF,
234-
WILDCARD_ENUM_MATCH_ARM
254+
WILDCARD_ENUM_MATCH_ARM,
255+
PATS_WITH_WILD_MATCH_ARM
235256
]);
236257

237258
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
@@ -246,6 +267,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
246267
check_wild_err_arm(cx, ex, arms);
247268
check_wild_enum_match(cx, ex, arms);
248269
check_match_as_ref(cx, ex, arms, expr);
270+
check_pats_wild_match(cx, ex, arms, expr);
249271
}
250272
if let ExprKind::Match(ref ex, ref arms, _) = expr.kind {
251273
check_match_ref_pats(cx, ex, arms, expr);
@@ -664,6 +686,25 @@ fn check_match_as_ref(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>],
664686
}
665687
}
666688

689+
fn check_pats_wild_match(cx: &LateContext<'_, '_>, _ex: &Expr, arms: &[Arm], _expr: &Expr) {
690+
for arm in arms {
691+
if let PatKind::Or(ref fields) = arm.pat.kind {
692+
// look for multiple fields where one at least matches Wild pattern
693+
if fields.len() > 1 && fields.into_iter().any(|pat| is_wild(pat)) {
694+
span_lint_and_sugg(
695+
cx,
696+
PATS_WITH_WILD_MATCH_ARM,
697+
arm.pat.span,
698+
"wildcard pattern covers any other pattern as it will match anyway. Consider replacing with wildcard pattern only",
699+
"try this",
700+
"_".to_string(),
701+
Applicability::MachineApplicable,
702+
)
703+
}
704+
}
705+
}
706+
}
707+
667708
/// Gets all arms that are unbounded `PatRange`s.
668709
fn all_ranges<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arms: &'tcx [Arm<'_>]) -> Vec<SpannedRange<Constant>> {
669710
arms.iter()

src/lintlist/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,13 @@ pub const ALL_LINTS: [Lint; 345] = [
15681568
deprecation: None,
15691569
module: "path_buf_push_overwrite",
15701570
},
1571+
Lint {
1572+
name: "pats_with_wild_match_arm",
1573+
group: "restriction",
1574+
desc: "a wildcard pattern used with others patterns in same match arm",
1575+
deprecation: None,
1576+
module: "matches",
1577+
},
15711578
Lint {
15721579
name: "possible_missing_comma",
15731580
group: "correctness",
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::pats_with_wild_match_arm)]
4+
5+
fn main() {
6+
match "foo" {
7+
"a" => {
8+
dbg!("matched a");
9+
},
10+
_ => {
11+
dbg!("matched (bar or) wild");
12+
},
13+
};
14+
match "foo" {
15+
"a" => {
16+
dbg!("matched a");
17+
},
18+
_ => {
19+
dbg!("matched (bar or bar2 or) wild");
20+
},
21+
};
22+
match "foo" {
23+
"a" => {
24+
dbg!("matched a");
25+
},
26+
_ => {
27+
dbg!("matched (bar or) wild");
28+
},
29+
};
30+
match "foo" {
31+
"a" => {
32+
dbg!("matched a");
33+
},
34+
_ => {
35+
dbg!("matched (bar or) wild");
36+
},
37+
};
38+
}

tests/ui/pats_with_wild_match_arm.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// run-rustfix
2+
3+
#![warn(clippy::pats_with_wild_match_arm)]
4+
5+
fn main() {
6+
match "foo" {
7+
"a" => {
8+
dbg!("matched a");
9+
},
10+
"bar" | _ => {
11+
dbg!("matched (bar or) wild");
12+
},
13+
};
14+
match "foo" {
15+
"a" => {
16+
dbg!("matched a");
17+
},
18+
"bar" | "bar2" | _ => {
19+
dbg!("matched (bar or bar2 or) wild");
20+
},
21+
};
22+
match "foo" {
23+
"a" => {
24+
dbg!("matched a");
25+
},
26+
_ | "bar" | _ => {
27+
dbg!("matched (bar or) wild");
28+
},
29+
};
30+
match "foo" {
31+
"a" => {
32+
dbg!("matched a");
33+
},
34+
_ | "bar" => {
35+
dbg!("matched (bar or) wild");
36+
},
37+
};
38+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error: wildcard pattern covers any other pattern as it will match anyway. Consider replacing with wildcard pattern only
2+
--> $DIR/pats_with_wild_match_arm.rs:10:9
3+
|
4+
LL | "bar" | _ => {
5+
| ^^^^^^^^^ help: try this: `_`
6+
|
7+
= note: `-D clippy::pats-with-wild-match-arm` implied by `-D warnings`
8+
9+
error: wildcard pattern covers any other pattern as it will match anyway. Consider replacing with wildcard pattern only
10+
--> $DIR/pats_with_wild_match_arm.rs:18:9
11+
|
12+
LL | "bar" | "bar2" | _ => {
13+
| ^^^^^^^^^^^^^^^^^^ help: try this: `_`
14+
15+
error: wildcard pattern covers any other pattern as it will match anyway. Consider replacing with wildcard pattern only
16+
--> $DIR/pats_with_wild_match_arm.rs:26:9
17+
|
18+
LL | _ | "bar" | _ => {
19+
| ^^^^^^^^^^^^^ help: try this: `_`
20+
21+
error: wildcard pattern covers any other pattern as it will match anyway. Consider replacing with wildcard pattern only
22+
--> $DIR/pats_with_wild_match_arm.rs:34:9
23+
|
24+
LL | _ | "bar" => {
25+
| ^^^^^^^^^ help: try this: `_`
26+
27+
error: aborting due to 4 previous errors
28+

0 commit comments

Comments
 (0)