From c199d9068e36a0948d12d2cda773436f0129be96 Mon Sep 17 00:00:00 2001 From: Michael Wright Date: Tue, 2 Jul 2019 08:08:28 +0200 Subject: [PATCH] Fix `match_same_arms` false negative Closes #4244 --- clippy_lints/src/copies.rs | 20 +++++++++++++++++--- tests/ui/match_same_arms.rs | 19 ++++++++++++++++++- tests/ui/match_same_arms.stderr | 19 ++++++++++++++++++- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index c1e06be7d6b..3894b56c049 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -1,4 +1,6 @@ -use crate::utils::{get_parent_expr, higher, in_macro_or_desugar, snippet, span_lint_and_then, span_note_and_lint}; +use crate::utils::{ + get_parent_expr, higher, in_macro_or_desugar, same_tys, snippet, span_lint_and_then, span_note_and_lint, +}; use crate::utils::{SpanlessEq, SpanlessHash}; use rustc::hir::*; use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; @@ -165,7 +167,18 @@ fn lint_same_cond(cx: &LateContext<'_, '_>, conds: &[&Expr]) { } /// Implementation of `MATCH_SAME_ARMS`. -fn lint_match_arms(cx: &LateContext<'_, '_>, expr: &Expr) { +fn lint_match_arms<'tcx>(cx: &LateContext<'_, 'tcx>, expr: &Expr) { + fn same_bindings<'tcx>( + cx: &LateContext<'_, 'tcx>, + lhs: &FxHashMap>, + rhs: &FxHashMap>, + ) -> bool { + lhs.len() == rhs.len() + && lhs + .iter() + .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| same_tys(cx, l_ty, r_ty))) + } + if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.node { let hash = |&(_, arm): &(usize, &Arm)| -> u64 { let mut h = SpanlessHash::new(cx, cx.tables); @@ -176,12 +189,13 @@ fn lint_match_arms(cx: &LateContext<'_, '_>, expr: &Expr) { let eq = |&(lindex, lhs): &(usize, &Arm), &(rindex, rhs): &(usize, &Arm)| -> bool { let min_index = usize::min(lindex, rindex); let max_index = usize::max(lindex, rindex); + // Arms with a guard are ignored, those can’t always be merged together // This is also the case for arms in-between each there is an arm with a guard (min_index..=max_index).all(|index| arms[index].guard.is_none()) && SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) && // all patterns should have the same bindings - bindings(cx, &lhs.pats[0]) == bindings(cx, &rhs.pats[0]) + same_bindings(cx, &bindings(cx, &lhs.pats[0]), &bindings(cx, &rhs.pats[0])) }; let indexed_arms: Vec<(usize, &Arm)> = arms.iter().enumerate().collect(); diff --git a/tests/ui/match_same_arms.rs b/tests/ui/match_same_arms.rs index 880518b010a..b53ca79adb5 100644 --- a/tests/ui/match_same_arms.rs +++ b/tests/ui/match_same_arms.rs @@ -1,3 +1,4 @@ +#![warn(clippy::match_same_arms)] #![allow( clippy::blacklisted_name, clippy::collapsible_if, @@ -21,7 +22,6 @@ pub enum Abc { C, } -#[warn(clippy::match_same_arms)] #[allow(clippy::unused_unit)] fn match_same_arms() { let _ = match 42 { @@ -126,4 +126,21 @@ fn match_same_arms() { }; } +mod issue4244 { + #[derive(PartialEq, PartialOrd, Eq, Ord)] + pub enum CommandInfo { + BuiltIn { name: String, about: Option }, + External { name: String, path: std::path::PathBuf }, + } + + impl CommandInfo { + pub fn name(&self) -> String { + match self { + CommandInfo::BuiltIn { name, .. } => name.to_string(), + CommandInfo::External { name, .. } => name.to_string(), + } + } + } +} + fn main() {} diff --git a/tests/ui/match_same_arms.stderr b/tests/ui/match_same_arms.stderr index 42c6f910dc3..38d51ebd476 100644 --- a/tests/ui/match_same_arms.stderr +++ b/tests/ui/match_same_arms.stderr @@ -224,5 +224,22 @@ help: consider refactoring into `2 | 3` LL | 2 => 2, //~ ERROR 2nd matched arms have same body | ^ -error: aborting due to 12 previous errors +error: this `match` has identical arm bodies + --> $DIR/match_same_arms.rs:140:55 + | +LL | CommandInfo::External { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^ + | +note: same as this + --> $DIR/match_same_arms.rs:139:54 + | +LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^ +help: consider refactoring into `CommandInfo::BuiltIn { name, .. } | CommandInfo::External { name, .. }` + --> $DIR/match_same_arms.rs:139:17 + | +LL | CommandInfo::BuiltIn { name, .. } => name.to_string(), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 13 previous errors