Add check for same guards in match_same_arms

This commit is contained in:
roife 2023-12-30 21:31:19 +08:00 committed by Alex Macleod
parent 74f611f7fc
commit 087c7c828d
4 changed files with 87 additions and 47 deletions

View File

@ -131,8 +131,7 @@ pub(super) fn check(
let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),)); let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),));
let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) { let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
(false, false) if from_nbits > to_nbits => "", (_, false) if from_nbits > to_nbits => "",
(true, false) if from_nbits > to_nbits => "",
(false, true) if from_nbits > 64 => "", (false, true) if from_nbits > 64 => "",
(false, true) if from_nbits > 32 => " on targets with 32-bit wide pointers", (false, true) if from_nbits > 32 => " on targets with 32-bit wide pointers",
_ => return, _ => return,

View File

@ -64,38 +64,50 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
let min_index = usize::min(lindex, rindex); let min_index = usize::min(lindex, rindex);
let max_index = usize::max(lindex, rindex); let max_index = usize::max(lindex, rindex);
let mut local_map: HirIdMap<HirId> = HirIdMap::default(); let check_eq_with_pat = |expr_a: &Expr<'_>, expr_b: &Expr<'_>| {
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { let mut local_map: HirIdMap<HirId> = HirIdMap::default();
if let Some(a_id) = path_to_local(a) let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
&& let Some(b_id) = path_to_local(b) if let Some(a_id) = path_to_local(a)
&& let entry = match local_map.entry(a_id) { && let Some(b_id) = path_to_local(b)
HirIdMapEntry::Vacant(entry) => entry, && let entry = match local_map.entry(a_id) {
// check if using the same bindings as before HirIdMapEntry::Vacant(entry) => entry,
HirIdMapEntry::Occupied(entry) => return *entry.get() == b_id, // check if using the same bindings as before
} HirIdMapEntry::Occupied(entry) => return *entry.get() == b_id,
}
// the names technically don't have to match; this makes the lint more conservative // the names technically don't have to match; this makes the lint more conservative
&& cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id) && cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id)
&& cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b) && cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b)
&& pat_contains_local(lhs.pat, a_id) && pat_contains_local(lhs.pat, a_id)
&& pat_contains_local(rhs.pat, b_id) && pat_contains_local(rhs.pat, b_id)
{ {
entry.insert(b_id); entry.insert(b_id);
true true
} else { } else {
false false
} }
}; };
// Arms with a guard are ignored, those cant always be merged together
// If both arms overlap with an arm in between then these can't be merged either. SpanlessEq::new(cx)
!(backwards_blocking_idxs[max_index] > min_index && forwards_blocking_idxs[min_index] < max_index) .expr_fallback(eq_fallback)
&& lhs.guard.is_none() .eq_expr(expr_a, expr_b)
&& rhs.guard.is_none()
&& SpanlessEq::new(cx)
.expr_fallback(eq_fallback)
.eq_expr(lhs.body, rhs.body)
// these checks could be removed to allow unused bindings // these checks could be removed to allow unused bindings
&& bindings_eq(lhs.pat, local_map.keys().copied().collect()) && bindings_eq(lhs.pat, local_map.keys().copied().collect())
&& bindings_eq(rhs.pat, local_map.values().copied().collect()) && bindings_eq(rhs.pat, local_map.values().copied().collect())
};
let check_same_guard = || match (&lhs.guard, &rhs.guard) {
(None, None) => true,
(Some(lhs_guard), Some(rhs_guard)) => check_eq_with_pat(lhs_guard, rhs_guard),
_ => false,
};
let check_same_body = || check_eq_with_pat(lhs.body, rhs.body);
// Arms with different guard are ignored, those cant always be merged together
// If both arms overlap with an arm in between then these can't be merged either.
!(backwards_blocking_idxs[max_index] > min_index && forwards_blocking_idxs[min_index] < max_index)
&& check_same_guard()
&& check_same_body()
}; };
let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect(); let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();

View File

@ -67,6 +67,20 @@ fn match_same_arms() {
_ => (), _ => (),
} }
// No warning because guards are different
let _ = match Some(42) {
Some(a) if a == 42 => a,
Some(a) if a == 24 => a,
Some(_) => 24,
None => 0,
};
let _ = match (Some(42), Some(42)) {
(Some(a), None) if a == 42 => a,
(None, Some(a)) if a == 42 => a, //~ ERROR: this match arm has an identical body to another arm
_ => 0,
};
match (Some(42), Some(42)) { match (Some(42), Some(42)) {
(Some(a), ..) => bar(a), //~ ERROR: this match arm has an identical body to another arm (Some(a), ..) => bar(a), //~ ERROR: this match arm has an identical body to another arm
(.., Some(a)) => bar(a), (.., Some(a)) => bar(a),

View File

@ -71,7 +71,22 @@ LL | (Some(a), None) => bar(a),
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^
error: this match arm has an identical body to another arm error: this match arm has an identical body to another arm
--> tests/ui/match_same_arms2.rs:71:9 --> tests/ui/match_same_arms2.rs:80:9
|
LL | (None, Some(a)) if a == 42 => a,
| ---------------^^^^^^^^^^^^^^^^
| |
| help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)`
|
= help: or try changing either arm body
note: other arm here
--> tests/ui/match_same_arms2.rs:79:9
|
LL | (Some(a), None) if a == 42 => a,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this match arm has an identical body to another arm
--> tests/ui/match_same_arms2.rs:85:9
| |
LL | (Some(a), ..) => bar(a), LL | (Some(a), ..) => bar(a),
| -------------^^^^^^^^^^ | -------------^^^^^^^^^^
@ -80,13 +95,13 @@ LL | (Some(a), ..) => bar(a),
| |
= help: or try changing either arm body = help: or try changing either arm body
note: other arm here note: other arm here
--> tests/ui/match_same_arms2.rs:72:9 --> tests/ui/match_same_arms2.rs:86:9
| |
LL | (.., Some(a)) => bar(a), LL | (.., Some(a)) => bar(a),
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
error: this match arm has an identical body to another arm error: this match arm has an identical body to another arm
--> tests/ui/match_same_arms2.rs:105:9 --> tests/ui/match_same_arms2.rs:119:9
| |
LL | (Ok(x), Some(_)) => println!("ok {}", x), LL | (Ok(x), Some(_)) => println!("ok {}", x),
| ----------------^^^^^^^^^^^^^^^^^^^^^^^^ | ----------------^^^^^^^^^^^^^^^^^^^^^^^^
@ -95,13 +110,13 @@ LL | (Ok(x), Some(_)) => println!("ok {}", x),
| |
= help: or try changing either arm body = help: or try changing either arm body
note: other arm here note: other arm here
--> tests/ui/match_same_arms2.rs:106:9 --> tests/ui/match_same_arms2.rs:120:9
| |
LL | (Ok(_), Some(x)) => println!("ok {}", x), LL | (Ok(_), Some(x)) => println!("ok {}", x),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this match arm has an identical body to another arm error: this match arm has an identical body to another arm
--> tests/ui/match_same_arms2.rs:121:9 --> tests/ui/match_same_arms2.rs:135:9
| |
LL | Ok(_) => println!("ok"), LL | Ok(_) => println!("ok"),
| -----^^^^^^^^^^^^^^^^^^ | -----^^^^^^^^^^^^^^^^^^
@ -110,13 +125,13 @@ LL | Ok(_) => println!("ok"),
| |
= help: or try changing either arm body = help: or try changing either arm body
note: other arm here note: other arm here
--> tests/ui/match_same_arms2.rs:120:9 --> tests/ui/match_same_arms2.rs:134:9
| |
LL | Ok(3) => println!("ok"), LL | Ok(3) => println!("ok"),
| ^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^
error: this match arm has an identical body to another arm error: this match arm has an identical body to another arm
--> tests/ui/match_same_arms2.rs:148:9 --> tests/ui/match_same_arms2.rs:162:9
| |
LL | 1 => { LL | 1 => {
| ^ help: try merging the arm patterns: `1 | 0` | ^ help: try merging the arm patterns: `1 | 0`
@ -128,7 +143,7 @@ LL | | },
| |
= help: or try changing either arm body = help: or try changing either arm body
note: other arm here note: other arm here
--> tests/ui/match_same_arms2.rs:145:9 --> tests/ui/match_same_arms2.rs:159:9
| |
LL | / 0 => { LL | / 0 => {
LL | | empty!(0); LL | | empty!(0);
@ -136,7 +151,7 @@ LL | | },
| |_________^ | |_________^
error: match expression looks like `matches!` macro error: match expression looks like `matches!` macro
--> tests/ui/match_same_arms2.rs:167:16 --> tests/ui/match_same_arms2.rs:181:16
| |
LL | let _ans = match x { LL | let _ans = match x {
| ________________^ | ________________^
@ -150,7 +165,7 @@ LL | | };
= help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]` = help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]`
error: this match arm has an identical body to another arm error: this match arm has an identical body to another arm
--> tests/ui/match_same_arms2.rs:199:9 --> tests/ui/match_same_arms2.rs:213:9
| |
LL | Foo::X(0) => 1, LL | Foo::X(0) => 1,
| ---------^^^^^ | ---------^^^^^
@ -159,13 +174,13 @@ LL | Foo::X(0) => 1,
| |
= help: or try changing either arm body = help: or try changing either arm body
note: other arm here note: other arm here
--> tests/ui/match_same_arms2.rs:201:9 --> tests/ui/match_same_arms2.rs:215:9
| |
LL | Foo::Z(_) => 1, LL | Foo::Z(_) => 1,
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: this match arm has an identical body to another arm error: this match arm has an identical body to another arm
--> tests/ui/match_same_arms2.rs:209:9 --> tests/ui/match_same_arms2.rs:223:9
| |
LL | Foo::Z(_) => 1, LL | Foo::Z(_) => 1,
| ---------^^^^^ | ---------^^^^^
@ -174,13 +189,13 @@ LL | Foo::Z(_) => 1,
| |
= help: or try changing either arm body = help: or try changing either arm body
note: other arm here note: other arm here
--> tests/ui/match_same_arms2.rs:207:9 --> tests/ui/match_same_arms2.rs:221:9
| |
LL | Foo::X(0) => 1, LL | Foo::X(0) => 1,
| ^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^
error: this match arm has an identical body to another arm error: this match arm has an identical body to another arm
--> tests/ui/match_same_arms2.rs:232:9 --> tests/ui/match_same_arms2.rs:246:9
| |
LL | Some(Bar { y: 0, x: 5, .. }) => 1, LL | Some(Bar { y: 0, x: 5, .. }) => 1,
| ----------------------------^^^^^ | ----------------------------^^^^^
@ -189,13 +204,13 @@ LL | Some(Bar { y: 0, x: 5, .. }) => 1,
| |
= help: or try changing either arm body = help: or try changing either arm body
note: other arm here note: other arm here
--> tests/ui/match_same_arms2.rs:229:9 --> tests/ui/match_same_arms2.rs:243:9
| |
LL | Some(Bar { x: 0, y: 5, .. }) => 1, LL | Some(Bar { x: 0, y: 5, .. }) => 1,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this match arm has an identical body to another arm error: this match arm has an identical body to another arm
--> tests/ui/match_same_arms2.rs:246:9 --> tests/ui/match_same_arms2.rs:260:9
| |
LL | 1 => cfg!(not_enable), LL | 1 => cfg!(not_enable),
| -^^^^^^^^^^^^^^^^^^^^ | -^^^^^^^^^^^^^^^^^^^^
@ -204,10 +219,10 @@ LL | 1 => cfg!(not_enable),
| |
= help: or try changing either arm body = help: or try changing either arm body
note: other arm here note: other arm here
--> tests/ui/match_same_arms2.rs:245:9 --> tests/ui/match_same_arms2.rs:259:9
| |
LL | 0 => cfg!(not_enable), LL | 0 => cfg!(not_enable),
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 13 previous errors error: aborting due to 14 previous errors