Skip to content

Commit ac795a6

Browse files
committed
Auto merge of #4960 - ThibsG:patterns_with_wildcard_#4640, r=flip1995
New lint: pats_with_wild_match_arm Wildcard use with other pattern in same match arm. The wildcard covers other(s) pattern(s) as it will match anyway. changelog: add new lint when multiple patterns (including wildcard) are used in a match arm. Fixes #4640.
2 parents 50403de + 0fa0df9 commit ac795a6

10 files changed

+145
-11
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1361,6 +1361,7 @@ Released 2018-09-13
13611361
[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
13621362
[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies
13631363
[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm
1364+
[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns
13641365
[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal
13651366
[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline
13661367
[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
88

9-
[There are 345 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
9+
[There are 346 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1010

1111
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1212

clippy_lints/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
600600
&matches::SINGLE_MATCH,
601601
&matches::SINGLE_MATCH_ELSE,
602602
&matches::WILDCARD_ENUM_MATCH_ARM,
603+
&matches::WILDCARD_IN_OR_PATTERNS,
603604
&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM,
604605
&mem_forget::MEM_FORGET,
605606
&mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
@@ -1188,6 +1189,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
11881189
LintId::of(&matches::MATCH_REF_PATS),
11891190
LintId::of(&matches::MATCH_WILD_ERR_ARM),
11901191
LintId::of(&matches::SINGLE_MATCH),
1192+
LintId::of(&matches::WILDCARD_IN_OR_PATTERNS),
11911193
LintId::of(&mem_discriminant::MEM_DISCRIMINANT_NON_ENUM),
11921194
LintId::of(&mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
11931195
LintId::of(&mem_replace::MEM_REPLACE_WITH_DEFAULT),
@@ -1462,6 +1464,7 @@ pub fn register_plugins(store: &mut lint::LintStore, sess: &Session, conf: &Conf
14621464
LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
14631465
LintId::of(&map_unit_fn::RESULT_MAP_UNIT_FN),
14641466
LintId::of(&matches::MATCH_AS_REF),
1467+
LintId::of(&matches::WILDCARD_IN_OR_PATTERNS),
14651468
LintId::of(&methods::CHARS_NEXT_CMP),
14661469
LintId::of(&methods::CLONE_ON_COPY),
14671470
LintId::of(&methods::FILTER_NEXT),

clippy_lints/src/matches.rs

+42-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use crate::utils::paths;
33
use crate::utils::sugg::Sugg;
44
use crate::utils::{
55
expr_block, is_allowed, is_expn_of, match_qpath, match_type, multispan_sugg, remove_blocks, snippet,
6-
snippet_with_applicability, span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty,
6+
snippet_with_applicability, span_help_and_lint, span_lint_and_sugg, span_lint_and_then, span_note_and_lint,
7+
walk_ptrs_ty,
78
};
89
use if_chain::if_chain;
910
use rustc::declare_lint_pass;
@@ -223,6 +224,26 @@ declare_clippy_lint! {
223224
"a wildcard enum match arm using `_`"
224225
}
225226

227+
declare_clippy_lint! {
228+
/// **What it does:** Checks for wildcard pattern used with others patterns in same match arm.
229+
///
230+
/// **Why is this bad?** Wildcard pattern already covers any other pattern as it will match anyway.
231+
/// It makes the code less readable, especially to spot wildcard pattern use in match arm.
232+
///
233+
/// **Known problems:** None.
234+
///
235+
/// **Example:**
236+
/// ```rust
237+
/// match "foo" {
238+
/// "a" => {},
239+
/// "bar" | _ => {},
240+
/// }
241+
/// ```
242+
pub WILDCARD_IN_OR_PATTERNS,
243+
complexity,
244+
"a wildcard pattern used with others patterns in same match arm"
245+
}
246+
226247
declare_lint_pass!(Matches => [
227248
SINGLE_MATCH,
228249
MATCH_REF_PATS,
@@ -231,7 +252,8 @@ declare_lint_pass!(Matches => [
231252
MATCH_OVERLAPPING_ARM,
232253
MATCH_WILD_ERR_ARM,
233254
MATCH_AS_REF,
234-
WILDCARD_ENUM_MATCH_ARM
255+
WILDCARD_ENUM_MATCH_ARM,
256+
WILDCARD_IN_OR_PATTERNS
235257
]);
236258

237259
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
@@ -246,6 +268,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Matches {
246268
check_wild_err_arm(cx, ex, arms);
247269
check_wild_enum_match(cx, ex, arms);
248270
check_match_as_ref(cx, ex, arms, expr);
271+
check_wild_in_or_pats(cx, arms);
249272
}
250273
if let ExprKind::Match(ref ex, ref arms, _) = expr.kind {
251274
check_match_ref_pats(cx, ex, arms, expr);
@@ -664,6 +687,23 @@ fn check_match_as_ref(cx: &LateContext<'_, '_>, ex: &Expr<'_>, arms: &[Arm<'_>],
664687
}
665688
}
666689

690+
fn check_wild_in_or_pats(cx: &LateContext<'_, '_>, arms: &[Arm<'_>]) {
691+
for arm in arms {
692+
if let PatKind::Or(ref fields) = arm.pat.kind {
693+
// look for multiple fields in this arm that contains at least one Wild pattern
694+
if fields.len() > 1 && fields.iter().any(is_wild) {
695+
span_help_and_lint(
696+
cx,
697+
WILDCARD_IN_OR_PATTERNS,
698+
arm.pat.span,
699+
"wildcard pattern covers any other pattern as it will match anyway.",
700+
"Consider handling `_` separately.",
701+
);
702+
}
703+
}
704+
}
705+
}
706+
667707
/// Gets all arms that are unbounded `PatRange`s.
668708
fn all_ranges<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arms: &'tcx [Arm<'_>]) -> Vec<SpannedRange<Constant>> {
669709
arms.iter()

src/lintlist/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 345] = [
9+
pub const ALL_LINTS: [Lint; 346] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -2345,6 +2345,13 @@ pub const ALL_LINTS: [Lint; 345] = [
23452345
deprecation: None,
23462346
module: "matches",
23472347
},
2348+
Lint {
2349+
name: "wildcard_in_or_patterns",
2350+
group: "complexity",
2351+
desc: "a wildcard pattern used with others patterns in same match arm",
2352+
deprecation: None,
2353+
module: "matches",
2354+
},
23482355
Lint {
23492356
name: "write_literal",
23502357
group: "style",

tests/ui/wild_in_or_pats.rs

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#![warn(clippy::wildcard_in_or_patterns)]
2+
3+
fn main() {
4+
match "foo" {
5+
"a" => {
6+
dbg!("matched a");
7+
},
8+
"bar" | _ => {
9+
dbg!("matched (bar or) wild");
10+
},
11+
};
12+
match "foo" {
13+
"a" => {
14+
dbg!("matched a");
15+
},
16+
"bar" | "bar2" | _ => {
17+
dbg!("matched (bar or bar2 or) wild");
18+
},
19+
};
20+
match "foo" {
21+
"a" => {
22+
dbg!("matched a");
23+
},
24+
_ | "bar" | _ => {
25+
dbg!("matched (bar or) wild");
26+
},
27+
};
28+
match "foo" {
29+
"a" => {
30+
dbg!("matched a");
31+
},
32+
_ | "bar" => {
33+
dbg!("matched (bar or) wild");
34+
},
35+
};
36+
}

tests/ui/wild_in_or_pats.stderr

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error: wildcard pattern covers any other pattern as it will match anyway.
2+
--> $DIR/wild_in_or_pats.rs:8:9
3+
|
4+
LL | "bar" | _ => {
5+
| ^^^^^^^^^
6+
|
7+
= note: `-D clippy::wildcard-in-or-patterns` implied by `-D warnings`
8+
= help: Consider handling `_` separately.
9+
10+
error: wildcard pattern covers any other pattern as it will match anyway.
11+
--> $DIR/wild_in_or_pats.rs:16:9
12+
|
13+
LL | "bar" | "bar2" | _ => {
14+
| ^^^^^^^^^^^^^^^^^^
15+
|
16+
= help: Consider handling `_` separately.
17+
18+
error: wildcard pattern covers any other pattern as it will match anyway.
19+
--> $DIR/wild_in_or_pats.rs:24:9
20+
|
21+
LL | _ | "bar" | _ => {
22+
| ^^^^^^^^^^^^^
23+
|
24+
= help: Consider handling `_` separately.
25+
26+
error: wildcard pattern covers any other pattern as it will match anyway.
27+
--> $DIR/wild_in_or_pats.rs:32:9
28+
|
29+
LL | _ | "bar" => {
30+
| ^^^^^^^^^
31+
|
32+
= help: Consider handling `_` separately.
33+
34+
error: aborting due to 4 previous errors
35+

tests/ui/wildcard_enum_match_arm.fixed

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
// run-rustfix
22

33
#![deny(clippy::wildcard_enum_match_arm)]
4-
#![allow(unreachable_code, unused_variables, dead_code, clippy::single_match)]
4+
#![allow(
5+
unreachable_code,
6+
unused_variables,
7+
dead_code,
8+
clippy::single_match,
9+
clippy::wildcard_in_or_patterns
10+
)]
511

612
use std::io::ErrorKind;
713

tests/ui/wildcard_enum_match_arm.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
// run-rustfix
22

33
#![deny(clippy::wildcard_enum_match_arm)]
4-
#![allow(unreachable_code, unused_variables, dead_code, clippy::single_match)]
4+
#![allow(
5+
unreachable_code,
6+
unused_variables,
7+
dead_code,
8+
clippy::single_match,
9+
clippy::wildcard_in_or_patterns
10+
)]
511

612
use std::io::ErrorKind;
713

tests/ui/wildcard_enum_match_arm.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: wildcard match will miss any future added variants
2-
--> $DIR/wildcard_enum_match_arm.rs:31:9
2+
--> $DIR/wildcard_enum_match_arm.rs:37:9
33
|
44
LL | _ => eprintln!("Not red"),
55
| ^ help: try this: `Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
@@ -11,25 +11,25 @@ LL | #![deny(clippy::wildcard_enum_match_arm)]
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212

1313
error: wildcard match will miss any future added variants
14-
--> $DIR/wildcard_enum_match_arm.rs:35:9
14+
--> $DIR/wildcard_enum_match_arm.rs:41:9
1515
|
1616
LL | _not_red => eprintln!("Not red"),
1717
| ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan`
1818

1919
error: wildcard match will miss any future added variants
20-
--> $DIR/wildcard_enum_match_arm.rs:39:9
20+
--> $DIR/wildcard_enum_match_arm.rs:45:9
2121
|
2222
LL | not_red => format!("{:?}", not_red),
2323
| ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan`
2424

2525
error: wildcard match will miss any future added variants
26-
--> $DIR/wildcard_enum_match_arm.rs:55:9
26+
--> $DIR/wildcard_enum_match_arm.rs:61:9
2727
|
2828
LL | _ => "No red",
2929
| ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
3030

3131
error: match on non-exhaustive enum doesn't explicitly match all known variants
32-
--> $DIR/wildcard_enum_match_arm.rs:72:9
32+
--> $DIR/wildcard_enum_match_arm.rs:78:9
3333
|
3434
LL | _ => {},
3535
| ^ help: try this: `std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _`

0 commit comments

Comments
 (0)