Skip to content

Commit cb48ffb

Browse files
olwangclaude
andcommitted
perf(analyzer): borrow HIR body in LocalAnalysis instead of double-cloning (Phase 5)
LocalAnalysis::new cloned the HIR function body (`let body = body.cloned()`), but checks/body/mod.rs already clones each body into an owned `hir_body` (detached from analyzer.hir, which must stay borrowable for the downstream check_*(&mut analyzer, ...) calls). LocalAnalysis then cloned that owned copy a SECOND time. Make LocalAnalysis borrow instead — `struct LocalAnalysis<'a>` with `body: Option<&'a HirFunctionBody>` — so it references the already-owned body. Halves per-function body cloning during semantic analysis; all 5 index traversals and the initial_state/moved_uses/retained/fresh_return methods only read the body. Behavior-identical: cascade was a mechanical lifetime annotation on the 13 `&LocalAnalysis` params across body/{semantics,resource_pool,effects,binding}.rs; no diagnostic message/order/span change. Verified: build+clippy clean, runtime 455/0, differential 33/0, checks lib 16/0. Scope was clone-removal only (pass-fusion deferred). Left load-bearing clones: checks/calls.rs items.clone() and body/mod.rs functions/hir_body clones (transitive reads of syntax_program.items / detachment from analyzer.hir make mem::take unsafe), and hir/infer.rs value_types.cloned() (Cow/&str would cascade across 47 call sites). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent d35a48d commit cb48ffb

5 files changed

Lines changed: 22 additions & 27 deletions

File tree

crates/rsscript/src/checks/body/binding.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::*;
22

33
pub(super) fn check_block(
44
analyzer: &mut Analyzer<'_>,
5-
local_analysis: &LocalAnalysis,
5+
local_analysis: &LocalAnalysis<'_>,
66
block: &HirBlock,
77
state: &mut BodyState,
88
check_resource_contexts: bool,

crates/rsscript/src/checks/body/effects.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,15 @@ pub(super) fn arm_span(arms: &[HirMatchArm]) -> Span {
209209
)
210210
}
211211

212-
pub(super) fn check_moved_uses(analyzer: &mut Analyzer<'_>, local_analysis: &LocalAnalysis) {
212+
pub(super) fn check_moved_uses(analyzer: &mut Analyzer<'_>, local_analysis: &LocalAnalysis<'_>) {
213213
for moved_use in local_analysis.moved_uses() {
214214
moved_use_diagnostic(analyzer, moved_use);
215215
}
216216
}
217217

218218
pub(super) fn check_managed_to_local_uses(
219219
analyzer: &mut Analyzer<'_>,
220-
local_analysis: &LocalAnalysis,
220+
local_analysis: &LocalAnalysis<'_>,
221221
) {
222222
for managed_to_local in local_analysis.managed_to_local_uses() {
223223
managed_to_local_diagnostic(analyzer, managed_to_local);
@@ -226,7 +226,7 @@ pub(super) fn check_managed_to_local_uses(
226226

227227
pub(super) fn check_retained_local_uses(
228228
analyzer: &mut Analyzer<'_>,
229-
local_analysis: &LocalAnalysis,
229+
local_analysis: &LocalAnalysis<'_>,
230230
) {
231231
for retained in local_analysis.retained_local_uses() {
232232
retained_local_diagnostic(analyzer, retained);
@@ -235,7 +235,7 @@ pub(super) fn check_retained_local_uses(
235235

236236
pub(super) fn check_retained_closure_captures(
237237
analyzer: &mut Analyzer<'_>,
238-
local_analysis: &LocalAnalysis,
238+
local_analysis: &LocalAnalysis<'_>,
239239
) {
240240
for capture in local_analysis.retained_closure_captures() {
241241
retained_closure_capture_diagnostic(analyzer, capture);
@@ -244,7 +244,7 @@ pub(super) fn check_retained_closure_captures(
244244

245245
pub(super) fn check_take_handle_fields(
246246
analyzer: &mut Analyzer<'_>,
247-
local_analysis: &LocalAnalysis,
247+
local_analysis: &LocalAnalysis<'_>,
248248
) {
249249
for field in local_analysis.take_handle_fields() {
250250
take_handle_field_diagnostic(analyzer, field);
@@ -253,7 +253,7 @@ pub(super) fn check_take_handle_fields(
253253

254254
pub(super) fn check_fresh_returns(
255255
analyzer: &mut Analyzer<'_>,
256-
local_analysis: &LocalAnalysis,
256+
local_analysis: &LocalAnalysis<'_>,
257257
function: &FunctionDecl,
258258
) {
259259
if !function.returns_fresh {

crates/rsscript/src/checks/body/resource_pool.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub(super) fn check_resource_pool_bindings(
2323
}
2424
pub(super) fn check_managed_closure_captures(
2525
analyzer: &mut Analyzer<'_>,
26-
local_analysis: &LocalAnalysis,
26+
local_analysis: &LocalAnalysis<'_>,
2727
statement_span: &crate::diagnostic::Span,
2828
state: &BodyState,
2929
) {
@@ -47,7 +47,7 @@ pub(super) fn check_managed_closure_captures(
4747

4848
pub(super) fn check_resource_escape(
4949
analyzer: &mut Analyzer<'_>,
50-
local_analysis: &LocalAnalysis,
50+
local_analysis: &LocalAnalysis<'_>,
5151
with_span: &crate::diagnostic::Span,
5252
) {
5353
if let Some(escapes) = local_analysis.resource_escapes(with_span) {
@@ -1698,7 +1698,7 @@ pub(super) fn is_resource_pool_type(type_name: &str) -> bool {
16981698
}
16991699

17001700
pub(super) fn resource_is_active_at(
1701-
local_analysis: &LocalAnalysis,
1701+
local_analysis: &LocalAnalysis<'_>,
17021702
binding: &str,
17031703
span: &crate::diagnostic::Span,
17041704
) -> bool {

crates/rsscript/src/checks/body/semantics.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::checks::diagnostic_helpers::error_cause_manual_fix;
33

44
pub(super) fn check_stmt_semantics(
55
analyzer: &mut Analyzer<'_>,
6-
local_analysis: &LocalAnalysis,
6+
local_analysis: &LocalAnalysis<'_>,
77
statement: &HirStmt,
88
state: &mut BodyState,
99
check_resource_contexts: bool,
@@ -342,7 +342,7 @@ pub(super) fn apply_stmt_effects(statement: &HirStmt, state: &mut BodyState) {
342342

343343
pub(super) fn check_expr_semantics(
344344
analyzer: &mut Analyzer<'_>,
345-
local_analysis: &LocalAnalysis,
345+
local_analysis: &LocalAnalysis<'_>,
346346
expr: &HirExpr,
347347
state: &BodyState,
348348
live_after: &HashSet<String>,
@@ -1086,7 +1086,7 @@ pub(super) fn first_mutating_effect_stmt(
10861086

10871087
pub(super) fn check_expr_semantics_with_context(
10881088
analyzer: &mut Analyzer<'_>,
1089-
local_analysis: &LocalAnalysis,
1089+
local_analysis: &LocalAnalysis<'_>,
10901090
expr: &HirExpr,
10911091
state: &BodyState,
10921092
allow_weak_upgrade_arg: bool,

crates/rsscript/src/checks/local.rs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ pub(crate) struct FreshReturnIssue {
7676
pub(crate) span: Span,
7777
}
7878

79-
pub(crate) struct LocalAnalysis {
80-
body: Option<HirFunctionBody>,
79+
pub(crate) struct LocalAnalysis<'a> {
80+
body: Option<&'a HirFunctionBody>,
8181
managed_closure_uses_by_span: HashMap<Span, Vec<(String, Span)>>,
8282
resource_escapes_by_with_span: HashMap<Span, Vec<ResourceEscape>>,
8383
take_handle_fields: Vec<TakeHandleField>,
@@ -149,27 +149,22 @@ pub(crate) struct ResourceEscape {
149149
pub(crate) span: Span,
150150
}
151151

152-
impl LocalAnalysis {
153-
pub(crate) fn new(body: Option<&HirFunctionBody>) -> Self {
154-
let body = body.cloned();
152+
impl<'a> LocalAnalysis<'a> {
153+
pub(crate) fn new(body: Option<&'a HirFunctionBody>) -> Self {
155154
let managed_closure_uses_by_span = body
156-
.as_ref()
157155
.and_then(|body| body.block.as_ref())
158156
.map_or_else(HashMap::new, index_managed_closure_uses_from_block);
159157
let resource_escapes_by_with_span = body
160-
.as_ref()
161158
.and_then(|body| body.block.as_ref())
162159
.map_or_else(HashMap::new, index_resource_escapes_from_block);
163160
let take_handle_fields = body
164-
.as_ref()
165161
.and_then(|body| body.block.as_ref())
166162
.map_or_else(Vec::new, collect_take_handle_fields);
167163
let flow_steps = body
168-
.as_ref()
169164
.and_then(|body| body.block.as_ref())
170165
.map_or_else(Vec::new, collect_local_flow_steps);
171166
let flow_entry_states_by_span =
172-
collect_flow_entry_states(&flow_steps, initial_state_from_body(body.as_ref()));
167+
collect_flow_entry_states(&flow_steps, initial_state_from_body(body));
173168

174169
Self {
175170
body,
@@ -182,7 +177,7 @@ impl LocalAnalysis {
182177
}
183178

184179
pub(crate) fn initial_state(&self) -> BodyState {
185-
initial_state_from_body(self.body.as_ref())
180+
initial_state_from_body(self.body)
186181
}
187182

188183
pub(crate) fn managed_closure_ident_uses(&self, span: &Span) -> Option<&[(String, Span)]> {
@@ -207,7 +202,7 @@ impl LocalAnalysis {
207202

208203
pub(crate) fn moved_uses(&self) -> Vec<MovedUse> {
209204
let mut moved_uses = Vec::new();
210-
if let Some(block) = self.body.as_ref().and_then(|body| body.block.as_ref()) {
205+
if let Some(block) = self.body.and_then(|body| body.block.as_ref()) {
211206
collect_ordered_moved_uses_from_block(
212207
block,
213208
&self.flow_entry_states_by_span,
@@ -278,7 +273,7 @@ impl LocalAnalysis {
278273

279274
pub(crate) fn retained_closure_captures(&self) -> Vec<RetainedClosureCapture> {
280275
let mut captures = Vec::new();
281-
if let Some(block) = self.body.as_ref().and_then(|body| body.block.as_ref()) {
276+
if let Some(block) = self.body.and_then(|body| body.block.as_ref()) {
282277
collect_retained_closure_captures_from_block(
283278
block,
284279
&self.flow_entry_states_by_span,
@@ -290,7 +285,7 @@ impl LocalAnalysis {
290285

291286
pub(crate) fn fresh_return_issues(&self) -> Vec<FreshReturnIssue> {
292287
let mut issues = Vec::new();
293-
if let Some(block) = self.body.as_ref().and_then(|body| body.block.as_ref()) {
288+
if let Some(block) = self.body.and_then(|body| body.block.as_ref()) {
294289
collect_fresh_return_issues_from_block(
295290
block,
296291
&self.flow_entry_states_by_span,

0 commit comments

Comments
 (0)