From 4f8f4b4c82e4647a2464e364ff96cc1dc5cb8e9a Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 6 Jan 2022 14:54:57 -0500 Subject: [PATCH] Handle slice patterns in `match_same_arms` --- clippy_lints/src/matches/match_same_arms.rs | 53 ++++++++++++++------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 0afac347d08..39c1f0d0d01 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -21,27 +21,30 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { }; let arena = DroplessArena::default(); - let resolved_pats: Vec<_> = arms.iter().map(|a| ResolvedPat::from_pat(cx, &arena, a.pat)).collect(); + let normalized_pats: Vec<_> = arms + .iter() + .map(|a| NormalizedPat::from_pat(cx, &arena, a.pat)) + .collect(); // The furthast forwards a pattern can move without semantic changes - let forwards_blocking_idxs: Vec<_> = resolved_pats + let forwards_blocking_idxs: Vec<_> = normalized_pats .iter() .enumerate() .map(|(i, pat)| { - resolved_pats[i + 1..] + normalized_pats[i + 1..] .iter() .enumerate() .find_map(|(j, other)| pat.can_also_match(other).then(|| i + 1 + j)) - .unwrap_or(resolved_pats.len()) + .unwrap_or(normalized_pats.len()) }) .collect(); // The furthast backwards a pattern can move without semantic changes - let backwards_blocking_idxs: Vec<_> = resolved_pats + let backwards_blocking_idxs: Vec<_> = normalized_pats .iter() .enumerate() .map(|(i, pat)| { - resolved_pats[..i] + normalized_pats[..i] .iter() .enumerate() .rev() @@ -133,18 +136,18 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { } #[derive(Clone, Copy)] -enum ResolvedPat<'hir, 'arena> { +enum NormalizedPat<'a> { Wild, - Struct(Option, &'arena [(Symbol, Self)]), - Tuple(Option, &'arena [Self]), - Or(&'arena [Self]), + Struct(Option, &'a [(Symbol, Self)]), + Tuple(Option, &'a [Self]), + Or(&'a [Self]), Path(Option), LitStr(Symbol), - LitBytes(&'hir [u8]), + LitBytes(&'a [u8]), LitInt(u128), LitBool(bool), Range(PatRange), - Slice(&'arena [Self], &'arena [Self], bool), + Slice(&'a [Self], Option<&'a [Self]>), } #[derive(Clone, Copy)] @@ -183,9 +186,9 @@ fn is_empty(&self) -> bool { } #[allow(clippy::similar_names)] -impl<'hir, 'arena> ResolvedPat<'hir, 'arena> { +impl<'a> NormalizedPat<'a> { #[allow(clippy::too_many_lines)] - fn from_pat(cx: &LateContext<'_>, arena: &'arena DroplessArena, pat: &'hir Pat<'_>) -> Self { + fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, PatKind::Binding(.., Some(pat)) | PatKind::Box(pat) | PatKind::Ref(pat, _) => { @@ -284,8 +287,7 @@ fn from_pat(cx: &LateContext<'_>, arena: &'arena DroplessArena, pat: &'hir Pat<' }, PatKind::Slice(front, wild_pat, back) => Self::Slice( arena.alloc_from_iter(front.iter().map(|pat| Self::from_pat(cx, arena, pat))), - arena.alloc_from_iter(back.iter().map(|pat| Self::from_pat(cx, arena, pat))), - wild_pat.is_some(), + wild_pat.map(|_| &*arena.alloc_from_iter(back.iter().map(|pat| Self::from_pat(cx, arena, pat)))), ), } } @@ -345,6 +347,25 @@ fn can_also_match(&self, other: &Self) -> bool { (Self::LitBool(x), Self::LitBool(y)) => x == y, (Self::Range(ref x), Self::Range(ref y)) => x.overlaps(y), (Self::Range(ref range), Self::LitInt(x)) | (Self::LitInt(x), Self::Range(ref range)) => range.contains(x), + (Self::Slice(lpats, None), Self::Slice(rpats, None)) => { + lpats.len() == rpats.len() && lpats.iter().zip(rpats.iter()).all(|(x, y)| x.can_also_match(y)) + }, + (Self::Slice(pats, None), Self::Slice(front, Some(back))) + | (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => { + if pats.len() < front.len() + back.len() { + return false; + } + pats[..front.len()] + .iter() + .zip(front.iter()) + .chain(pats[pats.len() - back.len()..].iter().zip(back.iter())) + .all(|(x, y)| x.can_also_match(y)) + }, + (Self::Slice(lfront, Some(lback)), Self::Slice(rfront, Some(rback))) => lfront + .iter() + .zip(rfront.iter()) + .chain(lback.iter().rev().zip(rback.iter().rev())) + .all(|(x, y)| x.can_also_match(y)), // Todo: Lit* with Path, Range with Path, LitBytes with Slice, Slice with Slice _ => true,