Skip to content

Commit b8b6b7f

Browse files
committed
Try to explain MATCH_SAME_ARMS better
1 parent eb75d4e commit b8b6b7f

File tree

2 files changed

+56
-4
lines changed

2 files changed

+56
-4
lines changed

clippy_lints/src/copies.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::collections::hash_map::Entry;
66
use syntax::parse::token::InternedString;
77
use syntax::util::small_vector::SmallVector;
88
use utils::{SpanlessEq, SpanlessHash};
9-
use utils::{get_parent_expr, in_macro, span_note_and_lint};
9+
use utils::{get_parent_expr, in_macro, span_lint_and_then, span_note_and_lint, snippet};
1010

1111
/// **What it does:** This lint checks for consecutive `ifs` with the same condition. This lint is
1212
/// `Warn` by default.
@@ -52,6 +52,23 @@ declare_lint! {
5252
/// Baz => bar(), // <= oops
5353
/// }
5454
/// ```
55+
///
56+
/// This should probably be
57+
/// ```rust,ignore
58+
/// match foo {
59+
/// Bar => bar(),
60+
/// Quz => quz(),
61+
/// Baz => baz(), // <= fixed
62+
/// }
63+
/// ```
64+
///
65+
/// or if the original code was not a typo:
66+
/// ```rust,ignore
67+
/// match foo {
68+
/// Bar | Baz => bar(), // <= shows the intent better
69+
/// Quz => quz(),
70+
/// }
71+
/// ```
5572
declare_lint! {
5673
pub MATCH_SAME_ARMS,
5774
Warn,
@@ -143,12 +160,25 @@ fn lint_match_arms(cx: &LateContext, expr: &Expr) {
143160

144161
if let ExprMatch(_, ref arms, MatchSource::Normal) = expr.node {
145162
if let Some((i, j)) = search_same(arms, hash, eq) {
146-
span_note_and_lint(cx,
163+
span_lint_and_then(cx,
147164
MATCH_SAME_ARMS,
148165
j.body.span,
149166
"this `match` has identical arm bodies",
150-
i.body.span,
151-
"same as this");
167+
|db| {
168+
db.span_note(i.body.span, "same as this");
169+
170+
// Note: this does not use `span_suggestion` on purpose: there is no clean way to
171+
// remove the other arm. Building a span and suggest to replace it to "" makes an
172+
// even more confusing error message. Also in order not to make up a span for the
173+
// whole pattern, the suggestion is only shown when there is only one pattern. The
174+
// user should know about `|` if they are already using it…
175+
176+
if i.pats.len() == 1 && j.pats.len() == 1 {
177+
let lhs = snippet(cx, i.pats[0].span, "<pat1>");
178+
let rhs = snippet(cx, j.pats[0].span, "<pat2>");
179+
db.span_note(i.body.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
180+
}
181+
});
152182
}
153183
}
154184
}

tests/compile-fail/copies.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ struct Foo {
2121
#[deny(match_same_arms)]
2222
fn if_same_then_else() -> Result<&'static str, ()> {
2323
if true {
24+
//~^NOTE same as this
2425
Foo { bar: 42 };
2526
0..10;
2627
..;
@@ -62,6 +63,7 @@ fn if_same_then_else() -> Result<&'static str, ()> {
6263
}
6364

6465
let _ = if true {
66+
//~^NOTE same as this
6567
foo();
6668
42
6769
}
@@ -75,13 +77,15 @@ fn if_same_then_else() -> Result<&'static str, ()> {
7577
}
7678

7779
let _ = if true {
80+
//~^NOTE same as this
7881
42
7982
}
8083
else { //~ERROR this `if` has identical blocks
8184
42
8285
};
8386

8487
if true {
88+
//~^NOTE same as this
8589
let bar = if true {
8690
42
8791
}
@@ -105,6 +109,7 @@ fn if_same_then_else() -> Result<&'static str, ()> {
105109
}
106110

107111
if true {
112+
//~^NOTE same as this
108113
let _ = match 42 {
109114
42 => 1,
110115
a if a > 0 => 2,
@@ -125,13 +130,15 @@ fn if_same_then_else() -> Result<&'static str, ()> {
125130
}
126131

127132
if true {
133+
//~^NOTE same as this
128134
if let Some(a) = Some(42) {}
129135
}
130136
else { //~ERROR this `if` has identical blocks
131137
if let Some(a) = Some(42) {}
132138
}
133139

134140
if true {
141+
//~^NOTE same as this
135142
if let (1, .., 3) = (1, 2, 3) {}
136143
}
137144
else { //~ERROR this `if` has identical blocks
@@ -168,12 +175,16 @@ fn if_same_then_else() -> Result<&'static str, ()> {
168175

169176
let _ = match 42 {
170177
42 => foo(),
178+
//~^NOTE same as this
179+
//~|NOTE `42 | 51`
171180
51 => foo(), //~ERROR this `match` has identical arm bodies
172181
_ => true,
173182
};
174183

175184
let _ = match Some(42) {
176185
Some(_) => 24,
186+
//~^NOTE same as this
187+
//~|NOTE `Some(_) | None`
177188
None => 24, //~ERROR this `match` has identical arm bodies
178189
};
179190

@@ -196,18 +207,24 @@ fn if_same_then_else() -> Result<&'static str, ()> {
196207

197208
match (Some(42), Some(42)) {
198209
(Some(a), None) => bar(a),
210+
//~^NOTE same as this
211+
//~|NOTE `(Some(a), None) | (None, Some(a))`
199212
(None, Some(a)) => bar(a), //~ERROR this `match` has identical arm bodies
200213
_ => (),
201214
}
202215

203216
match (Some(42), Some(42)) {
204217
(Some(a), ..) => bar(a),
218+
//~^NOTE same as this
219+
//~|NOTE `(Some(a), ..) | (.., Some(a))`
205220
(.., Some(a)) => bar(a), //~ERROR this `match` has identical arm bodies
206221
_ => (),
207222
}
208223

209224
match (1, 2, 3) {
210225
(1, .., 3) => 42,
226+
//~^NOTE same as this
227+
//~|NOTE `(1, .., 3) | (.., 3)`
211228
(.., 3) => 42, //~ERROR this `match` has identical arm bodies
212229
_ => 0,
213230
};
@@ -219,13 +236,15 @@ fn if_same_then_else() -> Result<&'static str, ()> {
219236
}
220237

221238
if true {
239+
//~^NOTE same as this
222240
try!(Ok("foo"));
223241
}
224242
else { //~ERROR this `if` has identical blocks
225243
try!(Ok("foo"));
226244
}
227245

228246
if true {
247+
//~^NOTE same as this
229248
let foo = "";
230249
return Ok(&foo[0..]);
231250
}
@@ -246,16 +265,19 @@ fn ifs_same_cond() {
246265
let b = false;
247266

248267
if b {
268+
//~^NOTE same as this
249269
}
250270
else if b { //~ERROR this `if` has the same condition as a previous if
251271
}
252272

253273
if a == 1 {
274+
//~^NOTE same as this
254275
}
255276
else if a == 1 { //~ERROR this `if` has the same condition as a previous if
256277
}
257278

258279
if 2*a == 1 {
280+
//~^NOTE same as this
259281
}
260282
else if 2*a == 2 {
261283
}

0 commit comments

Comments
 (0)