From c91298aaa0b246ecbd7905cefeae374e95a5a661 Mon Sep 17 00:00:00 2001 From: Ada Alakbarova Date: Wed, 25 Feb 2026 16:14:05 +0100 Subject: [PATCH] refactor(`clippy_utils::mir::visit_local_usage`): use const generics encodes the direct relationship between the lengths of `locals` and the returned list, making the code more type-safe --- .../src/methods/readonly_write_lock.rs | 5 +-- clippy_lints/src/redundant_clone.rs | 28 ++++++------- clippy_utils/src/mir/mod.rs | 39 +++++++++++-------- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/clippy_lints/src/methods/readonly_write_lock.rs b/clippy_lints/src/methods/readonly_write_lock.rs index a98a807d1a3c..b89aa8dfb4db 100644 --- a/clippy_lints/src/methods/readonly_write_lock.rs +++ b/clippy_lints/src/methods/readonly_write_lock.rs @@ -38,15 +38,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver .local_decls .iter_enumerated() .find(|(_, decl)| local.span.contains(decl.source_info.span)) - && let Some(usages) = visit_local_usage( - &[local], + && let Some([usage]) = visit_local_usage( + [local], mir, Location { block: START_BLOCK, statement_index: 0, }, ) - && let [usage] = usages.as_slice() { let writer_never_mutated = usage.local_consume_or_mutate_locs.is_empty(); diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 13c1b10bf2e8..232f0e0ec4c4 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -368,25 +368,25 @@ struct CloneUsage { } fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage { - if let Some(( - LocalUsage { - local_use_locs: cloned_use_locs, - local_consume_or_mutate_locs: cloned_consume_or_mutate_locs, - }, - LocalUsage { - local_use_locs: _, - local_consume_or_mutate_locs: clone_consume_or_mutate_locs, - }, - )) = visit_local_usage( - &[cloned, clone], + if let Some( + [ + LocalUsage { + local_use_locs: cloned_use_locs, + local_consume_or_mutate_locs: cloned_consume_or_mutate_locs, + }, + LocalUsage { + local_use_locs: _, + local_consume_or_mutate_locs: clone_consume_or_mutate_locs, + }, + ], + ) = visit_local_usage( + [cloned, clone], mir, mir::Location { block: bb, statement_index: mir.basic_blocks[bb].statements.len(), }, - ) - .map(|mut vec| (vec.remove(0), vec.remove(0))) - { + ) { CloneUsage { cloned_use_loc: cloned_use_locs.first().copied().into(), cloned_consume_or_mutate_loc: cloned_consume_or_mutate_locs.first().copied(), diff --git a/clippy_utils/src/mir/mod.rs b/clippy_utils/src/mir/mod.rs index 35adc51ee24c..40c553d1c9e6 100644 --- a/clippy_utils/src/mir/mod.rs +++ b/clippy_utils/src/mir/mod.rs @@ -1,3 +1,5 @@ +use std::iter; + use rustc_data_structures::either::Either; use rustc_hir::{Expr, HirId}; use rustc_index::bit_set::DenseBitSet; @@ -22,14 +24,17 @@ pub struct LocalUsage { pub local_consume_or_mutate_locs: Vec, } -pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -> Option> { - let init = vec![ +pub fn visit_local_usage( + locals: [Local; N], + mir: &Body<'_>, + location: Location, +) -> Option<[LocalUsage; N]> { + let init = [const { LocalUsage { local_use_locs: Vec::new(), local_consume_or_mutate_locs: Vec::new(), - }; - locals.len() - ]; + } + }; N]; traversal::Postorder::new(&mir.basic_blocks, location.block, None) .collect::>() @@ -44,7 +49,7 @@ pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) - } let mut v = V { - locals, + locals: &locals, location, results: usage, }; @@ -53,13 +58,13 @@ pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) - }) } -struct V<'a> { - locals: &'a [Local], +struct V<'a, const N: usize> { + locals: &'a [Local; N], location: Location, - results: Vec, + results: [LocalUsage; N], } -impl<'tcx> Visitor<'tcx> for V<'_> { +impl<'tcx, const N: usize> Visitor<'tcx> for V<'_, N> { fn visit_place(&mut self, place: &Place<'tcx>, ctx: PlaceContext, loc: Location) { if loc.block == self.location.block && loc.statement_index <= self.location.statement_index { return; @@ -67,20 +72,20 @@ fn visit_place(&mut self, place: &Place<'tcx>, ctx: PlaceContext, loc: Location) let local = place.local; - for (i, self_local) in self.locals.iter().enumerate() { + for (self_local, result) in iter::zip(self.locals, &mut self.results) { if local == *self_local { if !matches!( ctx, PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_) ) { - self.results[i].local_use_locs.push(loc); + result.local_use_locs.push(loc); } if matches!( ctx, PlaceContext::NonMutatingUse(NonMutatingUseContext::Move | NonMutatingUseContext::Inspect) | PlaceContext::MutatingUse(MutatingUseContext::Borrow) ) { - self.results[i].local_consume_or_mutate_locs.push(loc); + result.local_consume_or_mutate_locs.push(loc); } } } @@ -114,16 +119,16 @@ pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { /// Convenience wrapper around `visit_local_usage`. pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option { visit_local_usage( - &[local], + [local], mir, Location { block: START_BLOCK, statement_index: 0, }, ) - .map(|mut vec| { - let LocalUsage { local_use_locs, .. } = vec.remove(0); - let mut locations = local_use_locs + .map(|[local_usage]| { + let mut locations = local_usage + .local_use_locs .into_iter() .filter(|&location| !is_local_assignment(mir, local, location)); if let Some(location) = locations.next() {