From 902726c38d79d4e63567f2bbd5ddff475c2f1b2b Mon Sep 17 00:00:00 2001 From: Vincent Dal Maso Date: Thu, 16 May 2019 11:27:45 +0200 Subject: [PATCH] Fix match_same_arms to fail late Changes: - Add a function search_same_list which return a list of matched expressions - Change the match_same_arms implementation behaviour. It will lint each same arms found. --- clippy_lints/src/copies.rs | 111 ++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 37 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index d85cf97a6bd..a28af2371ed 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -185,44 +185,48 @@ fn lint_match_arms(cx: &LateContext<'_, '_>, expr: &Expr) { }; let indexed_arms: Vec<(usize, &Arm)> = arms.iter().enumerate().collect(); - if let Some((&(_, i), &(_, j))) = 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"); - - // 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, ""); - let rhs = snippet(cx, j.pats[0].span, ""); - - 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)); + search_same_list(&indexed_arms, hash, eq).map(|item| { + for match_expr in item { + let (&(_, i), &(_, j)) = match_expr; + + 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… + + if i.pats.len() == 1 && j.pats.len() == 1 { + let lhs = snippet(cx, i.pats[0].span, ""); + let rhs = snippet(cx, j.pats[0].span, ""); + + 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)); + } } - } - }, - ); - } + }, + ); + } + }); } } @@ -360,3 +364,36 @@ fn search_same(exprs: &[T], hash: Hash, eq: Eq) -> Option<(&T, &T)> None } + +fn search_same_list(exprs: &[T], hash: Hash, eq: Eq) -> Option> +where + Hash: Fn(&T) -> u64, + Eq: Fn(&T, &T) -> bool, +{ + let mut match_expr_list: Vec<(&T, &T)> = Vec::new(); + + 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) { + match_expr_list.push((o, expr)); + } + } + o.get_mut().push(expr); + }, + Entry::Vacant(v) => { + v.insert(vec![expr]); + }, + } + } + + if match_expr_list.is_empty() { + None + } else { + Some(match_expr_list) + } +} -- 2.44.0