Add the common case search

Changes:
- Refactor the common case search into a function.
- Fix the useless Option around the vec in the search_same_list.
This commit is contained in:
Vincent Dal Maso 2019-05-20 10:22:13 +02:00
parent bfb230369e
commit fa9f744c2c
3 changed files with 169 additions and 94 deletions

View file

@ -152,7 +152,7 @@ fn lint_same_cond(cx: &LateContext<'_, '_>, conds: &[&Expr]) {
let eq: &dyn Fn(&&Expr, &&Expr) -> bool =
&|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) };
if let Some((i, j)) = search_same(conds, hash, eq) {
for (i, j) in search_same(conds, hash, eq) {
span_note_and_lint(
cx,
IFS_SAME_COND,
@ -185,48 +185,47 @@ fn lint_match_arms(cx: &LateContext<'_, '_>, expr: &Expr) {
};
let indexed_arms: Vec<(usize, &Arm)> = arms.iter().enumerate().collect();
search_same_list(&indexed_arms, hash, eq).map(|item| {
for match_expr in item {
let (&(_, i), &(_, j)) = match_expr;
for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
span_lint_and_then(
cx,
MATCH_SAME_ARMS,
j.body.span,
"this `match` has identical arm bodies",
|db| {
db.span_note(i.body.span, "same as this");
span_lint_and_then(
cx,
MATCH_SAME_ARMS,
j.body.span,
"this `match` has identical arm bodies",
|db| {
db.span_note(i.body.span, "same as this");
// Note: this does not use `span_suggestion` on purpose:
// there is no clean way
// to remove the other arm. Building a span and suggest to replace it to ""
// makes an even more confusing error message. Also in order not to make up a
// span for the whole pattern, the suggestion is only shown when there is only
// one pattern. The user should know about `|` if they are already using it…
// Note: this does not use `span_suggestion` on purpose:
// there is no clean way
// to remove the other arm. Building a span and suggest to replace it to ""
// makes an even more confusing error message. Also in order not to make up a
// span for the whole pattern, the suggestion is only shown when there is only
// one pattern. The user should know about `|` if they are already using it…
if i.pats.len() == 1 && j.pats.len() == 1 {
let lhs = snippet(cx, i.pats[0].span, "<pat1>");
let rhs = snippet(cx, j.pats[0].span, "<pat2>");
if i.pats.len() == 1 && j.pats.len() == 1 {
let lhs = snippet(cx, i.pats[0].span, "<pat1>");
let rhs = snippet(cx, j.pats[0].span, "<pat2>");
if let PatKind::Wild = j.pats[0].node {
// if the last arm is _, then i could be integrated into _
// note that i.pats[0] cannot be _, because that would mean that we're
// hiding all the subsequent arms, and rust won't compile
db.span_note(
i.body.span,
&format!(
"`{}` has the same arm body as the `_` wildcard, consider removing it`",
lhs
),
);
} else {
db.span_note(i.body.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
}
if let PatKind::Wild = j.pats[0].node {
// if the last arm is _, then i could be integrated into _
// note that i.pats[0] cannot be _, because that would mean that we're
// hiding all the subsequent arms, and rust won't compile
db.span_note(
i.body.span,
&format!(
"`{}` has the same arm body as the `_` wildcard, consider removing it`",
lhs
),
);
} else {
db.span_help(
i.pats[0].span,
&format!("consider refactoring into `{} | {}`", lhs, rhs),
);
}
},
);
}
});
}
},
);
}
}
}
@ -327,49 +326,32 @@ where
None
}
fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Option<(&T, &T)>
fn search_common_cases<'a, T, Eq>(exprs: &'a [T], eq: &Eq) -> Option<(&'a T, &'a T)>
where
Hash: Fn(&T) -> u64,
Eq: Fn(&T, &T) -> bool,
{
// common cases
if exprs.len() < 2 {
return None;
None
} else if exprs.len() == 2 {
return if eq(&exprs[0], &exprs[1]) {
if eq(&exprs[0], &exprs[1]) {
Some((&exprs[0], &exprs[1]))
} else {
None
};
}
let mut map: FxHashMap<_, Vec<&_>> =
FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
for expr in exprs {
match map.entry(hash(expr)) {
Entry::Occupied(mut o) => {
for o in o.get() {
if eq(o, expr) {
return Some((o, expr));
}
}
o.get_mut().push(expr);
},
Entry::Vacant(v) => {
v.insert(vec![expr]);
},
}
} else {
None
}
None
}
fn search_same_list<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Option<Vec<(&T, &T)>>
fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
where
Hash: Fn(&T) -> u64,
Eq: Fn(&T, &T) -> bool,
{
if let Some(expr) = search_common_cases(&exprs, &eq) {
return vec![expr];
}
let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
let mut map: FxHashMap<_, Vec<&_>> =
@ -391,9 +373,5 @@ where
}
}
if match_expr_list.is_empty() {
None
} else {
Some(match_expr_list)
}
match_expr_list
}

View file

@ -116,6 +116,14 @@ fn match_same_arms() {
52 => 2, //~ ERROR match arms have same body
_ => 0,
};
let _ = match 42 {
1 => 2,
2 => 2, //~ ERROR 2rd matched arms have same body
3 => 2, //~ ERROR 3rd matched arms have same body
4 => 3,
_ => 0,
};
}
fn main() {}

View file

@ -1,10 +1,48 @@
error: this `match` has identical arm bodies
--> $DIR/match_same_arms.rs:37:14
|
LL | _ => {
| ______________^
LL | | //~ ERROR match arms have same body
LL | | foo();
LL | | let mut a = 42 + [23].len() as i32;
... |
LL | | a
LL | | },
| |_________^
|
= note: `-D clippy::match-same-arms` implied by `-D warnings`
note: same as this
--> $DIR/match_same_arms.rs:28:15
|
LL | 42 => {
| _______________^
LL | | foo();
LL | | let mut a = 42 + [23].len() as i32;
LL | | if true {
... |
LL | | a
LL | | },
| |_________^
note: `42` has the same arm body as the `_` wildcard, consider removing it`
--> $DIR/match_same_arms.rs:28:15
|
LL | 42 => {
| _______________^
LL | | foo();
LL | | let mut a = 42 + [23].len() as i32;
LL | | if true {
... |
LL | | a
LL | | },
| |_________^
error: this `match` has identical arm bodies
--> $DIR/match_same_arms.rs:52:14
|
LL | _ => 0, //~ ERROR match arms have same body
| ^
|
= note: `-D clippy::match-same-arms` implied by `-D warnings`
note: same as this
--> $DIR/match_same_arms.rs:50:19
|
@ -27,11 +65,11 @@ note: same as this
|
LL | 42 => foo(),
| ^^^^^
note: consider refactoring into `42 | 51`
--> $DIR/match_same_arms.rs:56:15
help: consider refactoring into `42 | 51`
--> $DIR/match_same_arms.rs:56:9
|
LL | 42 => foo(),
| ^^^^^
| ^^
error: this `match` has identical arm bodies
--> $DIR/match_same_arms.rs:63:17
@ -44,11 +82,11 @@ note: same as this
|
LL | Some(_) => 24,
| ^^
note: consider refactoring into `Some(_) | None`
--> $DIR/match_same_arms.rs:62:20
help: consider refactoring into `Some(_) | None`
--> $DIR/match_same_arms.rs:62:9
|
LL | Some(_) => 24,
| ^^
| ^^^^^^^
error: this `match` has identical arm bodies
--> $DIR/match_same_arms.rs:85:28
@ -61,11 +99,11 @@ note: same as this
|
LL | (Some(a), None) => bar(a),
| ^^^^^^
note: consider refactoring into `(Some(a), None) | (None, Some(a))`
--> $DIR/match_same_arms.rs:84:28
help: consider refactoring into `(Some(a), None) | (None, Some(a))`
--> $DIR/match_same_arms.rs:84:9
|
LL | (Some(a), None) => bar(a),
| ^^^^^^
| ^^^^^^^^^^^^^^^
error: this `match` has identical arm bodies
--> $DIR/match_same_arms.rs:91:26
@ -78,11 +116,11 @@ note: same as this
|
LL | (Some(a), ..) => bar(a),
| ^^^^^^
note: consider refactoring into `(Some(a), ..) | (.., Some(a))`
--> $DIR/match_same_arms.rs:90:26
help: consider refactoring into `(Some(a), ..) | (.., Some(a))`
--> $DIR/match_same_arms.rs:90:9
|
LL | (Some(a), ..) => bar(a),
| ^^^^^^
| ^^^^^^^^^^^^^
error: this `match` has identical arm bodies
--> $DIR/match_same_arms.rs:97:20
@ -95,11 +133,11 @@ note: same as this
|
LL | (1, .., 3) => 42,
| ^^
note: consider refactoring into `(1, .., 3) | (.., 3)`
--> $DIR/match_same_arms.rs:96:23
help: consider refactoring into `(1, .., 3) | (.., 3)`
--> $DIR/match_same_arms.rs:96:9
|
LL | (1, .., 3) => 42,
| ^^
| ^^^^^^^^^^
error: this `match` has identical arm bodies
--> $DIR/match_same_arms.rs:114:15
@ -112,11 +150,11 @@ note: same as this
|
LL | 42 => 1,
| ^
note: consider refactoring into `42 | 51`
--> $DIR/match_same_arms.rs:113:15
help: consider refactoring into `42 | 51`
--> $DIR/match_same_arms.rs:113:9
|
LL | 42 => 1,
| ^
| ^^
error: this `match` has identical arm bodies
--> $DIR/match_same_arms.rs:116:15
@ -129,11 +167,62 @@ note: same as this
|
LL | 41 => 2,
| ^
note: consider refactoring into `41 | 52`
--> $DIR/match_same_arms.rs:115:15
help: consider refactoring into `41 | 52`
--> $DIR/match_same_arms.rs:115:9
|
LL | 41 => 2,
| ^
| ^^
error: aborting due to 8 previous errors
error: this `match` has identical arm bodies
--> $DIR/match_same_arms.rs:122:14
|
LL | 2 => 2, //~ ERROR 2rd matched arms have same body
| ^
|
note: same as this
--> $DIR/match_same_arms.rs:121:14
|
LL | 1 => 2,
| ^
help: consider refactoring into `1 | 2`
--> $DIR/match_same_arms.rs:121:9
|
LL | 1 => 2,
| ^
error: this `match` has identical arm bodies
--> $DIR/match_same_arms.rs:123:14
|
LL | 3 => 2, //~ ERROR 3rd matched arms have same body
| ^
|
note: same as this
--> $DIR/match_same_arms.rs:121:14
|
LL | 1 => 2,
| ^
help: consider refactoring into `1 | 3`
--> $DIR/match_same_arms.rs:121:9
|
LL | 1 => 2,
| ^
error: this `match` has identical arm bodies
--> $DIR/match_same_arms.rs:123:14
|
LL | 3 => 2, //~ ERROR 3rd matched arms have same body
| ^
|
note: same as this
--> $DIR/match_same_arms.rs:122:14
|
LL | 2 => 2, //~ ERROR 2rd matched arms have same body
| ^
help: consider refactoring into `2 | 3`
--> $DIR/match_same_arms.rs:122:9
|
LL | 2 => 2, //~ ERROR 2rd matched arms have same body
| ^
error: aborting due to 12 previous errors