Skip to content

Commit 05fb0ba

Browse files
committed
Introduce local analysis context
1 parent 2cd9076 commit 05fb0ba

2 files changed

Lines changed: 148 additions & 50 deletions

File tree

src/checks/body.rs

Lines changed: 57 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::hir::{
55
};
66
use crate::syntax::ast::{Block, Callee, DataEffect, Expr, FunctionDecl, Item, LetKind, Stmt};
77

8-
use super::local::{BodyState, merge_if_state, merge_loop_state};
8+
use super::local::{BodyState, LocalAnalysis, merge_if_state, merge_loop_state};
99

1010
pub(crate) fn check(analyzer: &mut Analyzer<'_>) {
1111
let functions: Vec<FunctionDecl> = analyzer
@@ -19,19 +19,18 @@ pub(crate) fn check(analyzer: &mut Analyzer<'_>) {
1919
.collect();
2020

2121
for function in functions {
22-
let mut state = BodyState::default();
23-
seed_function_bindings(analyzer, &function, &mut state);
24-
check_block(analyzer, &function, &function.body, &mut state);
22+
let local_analysis = LocalAnalysis::new(analyzer.hir.function_body(&function.name));
23+
let mut state = local_analysis.initial_state();
24+
check_block(
25+
analyzer,
26+
&local_analysis,
27+
&function,
28+
&function.body,
29+
&mut state,
30+
);
2531
}
2632
}
2733

28-
fn seed_function_bindings(analyzer: &Analyzer<'_>, function: &FunctionDecl, state: &mut BodyState) {
29-
let Some(body) = analyzer.hir.function_body(&function.name) else {
30-
return;
31-
};
32-
state.seed_params(&body.bindings);
33-
}
34-
3534
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3635
pub(crate) enum Flow {
3736
Fallthrough,
@@ -42,14 +41,15 @@ pub(crate) enum Flow {
4241

4342
fn check_block(
4443
analyzer: &mut Analyzer<'_>,
44+
local_analysis: &LocalAnalysis,
4545
function: &FunctionDecl,
4646
block: &Block,
4747
state: &mut BodyState,
4848
) -> Flow {
4949
for statement in &block.statements {
5050
check_moved_uses_in_stmt(analyzer, statement, state);
51-
let flow = check_stmt_semantics(analyzer, function, statement, state);
52-
apply_stmt_effects(analyzer, statement, state);
51+
let flow = check_stmt_semantics(analyzer, local_analysis, function, statement, state);
52+
apply_stmt_effects(analyzer, local_analysis, statement, state);
5353
if flow != Flow::Fallthrough {
5454
return flow;
5555
}
@@ -59,6 +59,7 @@ fn check_block(
5959

6060
fn check_stmt_semantics(
6161
analyzer: &mut Analyzer<'_>,
62+
local_analysis: &LocalAnalysis,
6263
function: &FunctionDecl,
6364
statement: &Stmt,
6465
state: &mut BodyState,
@@ -110,19 +111,31 @@ fn check_stmt_semantics(
110111
}
111112
Stmt::With(stmt) => {
112113
check_resource_escape(analyzer, &stmt.binding, &stmt.body);
113-
check_block(analyzer, function, &stmt.body, state)
114+
check_block(analyzer, local_analysis, function, &stmt.body, state)
114115
}
115116
Stmt::If(stmt) => {
116117
check_take_of_handle_field(analyzer, &stmt.condition, state);
117-
apply_expr_effects(analyzer, &stmt.condition, state);
118+
apply_expr_effects(local_analysis, &stmt.condition, state);
118119

119120
let base_state = state.clone();
120121
let mut then_state = base_state.clone();
121-
let then_flow = check_block(analyzer, function, &stmt.then_body, &mut then_state);
122+
let then_flow = check_block(
123+
analyzer,
124+
local_analysis,
125+
function,
126+
&stmt.then_body,
127+
&mut then_state,
128+
);
122129

123130
let else_branch = stmt.else_body.as_ref().map(|else_body| {
124131
let mut else_state = base_state.clone();
125-
let else_flow = check_block(analyzer, function, else_body, &mut else_state);
132+
let else_flow = check_block(
133+
analyzer,
134+
local_analysis,
135+
function,
136+
else_body,
137+
&mut else_state,
138+
);
126139
(else_state, else_flow)
127140
});
128141

@@ -131,12 +144,18 @@ fn check_stmt_semantics(
131144
Stmt::Loop(stmt) => {
132145
if let Some(condition) = &stmt.condition {
133146
check_take_of_handle_field(analyzer, condition, state);
134-
apply_expr_effects(analyzer, condition, state);
147+
apply_expr_effects(local_analysis, condition, state);
135148
}
136149

137150
let base_state = state.clone();
138151
let mut body_state = base_state.clone();
139-
let body_flow = check_block(analyzer, function, &stmt.body, &mut body_state);
152+
let body_flow = check_block(
153+
analyzer,
154+
local_analysis,
155+
function,
156+
&stmt.body,
157+
&mut body_state,
158+
);
140159

141160
merge_loop_state(
142161
state,
@@ -156,7 +175,12 @@ fn check_stmt_semantics(
156175
}
157176
}
158177

159-
fn apply_stmt_effects(analyzer: &mut Analyzer<'_>, statement: &Stmt, state: &mut BodyState) {
178+
fn apply_stmt_effects(
179+
analyzer: &mut Analyzer<'_>,
180+
local_analysis: &LocalAnalysis,
181+
statement: &Stmt,
182+
state: &mut BodyState,
183+
) {
160184
match statement {
161185
Stmt::Let(stmt) => {
162186
match stmt.kind {
@@ -173,20 +197,20 @@ fn apply_stmt_effects(analyzer: &mut Analyzer<'_>, statement: &Stmt, state: &mut
173197
{
174198
state.record_type(stmt.name.clone(), type_name);
175199
}
176-
apply_expr_effects(analyzer, value, state);
200+
apply_expr_effects(local_analysis, value, state);
177201
}
178202
}
179203
Stmt::Return(stmt) => {
180204
if let Some(value) = &stmt.value {
181-
apply_expr_effects(analyzer, value, state);
205+
apply_expr_effects(local_analysis, value, state);
182206
}
183207
}
184208
Stmt::With(stmt) => {
185-
apply_expr_effects(analyzer, &stmt.resource, state);
209+
apply_expr_effects(local_analysis, &stmt.resource, state);
186210
}
187211
Stmt::If(_) => {}
188212
Stmt::Loop(_) => {}
189-
Stmt::Expr(expr) => apply_expr_effects(analyzer, expr, state),
213+
Stmt::Expr(expr) => apply_expr_effects(local_analysis, expr, state),
190214
Stmt::Break(_) | Stmt::Continue(_) => {}
191215
Stmt::Unknown(_) => {}
192216
}
@@ -199,28 +223,28 @@ fn hir_binding_type(analyzer: &Analyzer<'_>, span: &crate::diagnostic::Span) ->
199223
.and_then(|binding| binding.type_name.clone())
200224
}
201225

202-
fn apply_expr_effects(analyzer: &mut Analyzer<'_>, expr: &Expr, state: &mut BodyState) {
226+
fn apply_expr_effects(local_analysis: &LocalAnalysis, expr: &Expr, state: &mut BodyState) {
203227
match expr {
204228
Expr::Manage { value, span } => {
205-
apply_move_events(analyzer, span, state);
206-
apply_expr_effects(analyzer, value, state);
229+
local_analysis.apply_move_events(span, state);
230+
apply_expr_effects(local_analysis, value, state);
207231
}
208232
Expr::Effect {
209233
effect: DataEffect::Take,
210234
value,
211235
span,
212236
} => {
213-
apply_move_events(analyzer, span, state);
214-
apply_expr_effects(analyzer, value, state);
237+
local_analysis.apply_move_events(span, state);
238+
apply_expr_effects(local_analysis, value, state);
215239
}
216-
Expr::Effect { value, .. } => apply_expr_effects(analyzer, value, state),
240+
Expr::Effect { value, .. } => apply_expr_effects(local_analysis, value, state),
217241
Expr::Call { args, span, .. } => {
218-
apply_retention_events(analyzer, span, state);
242+
local_analysis.apply_retention_events(span, state);
219243
for arg in args {
220-
apply_expr_effects(analyzer, &arg.value, state);
244+
apply_expr_effects(local_analysis, &arg.value, state);
221245
}
222246
}
223-
Expr::Field { base, .. } => apply_expr_effects(analyzer, base, state),
247+
Expr::Field { base, .. } => apply_expr_effects(local_analysis, base, state),
224248
Expr::Closure { .. }
225249
| Expr::Ident(_, _)
226250
| Expr::Number(_, _)
@@ -229,22 +253,6 @@ fn apply_expr_effects(analyzer: &mut Analyzer<'_>, expr: &Expr, state: &mut Body
229253
}
230254
}
231255

232-
fn apply_move_events(
233-
analyzer: &Analyzer<'_>,
234-
span: &crate::diagnostic::Span,
235-
state: &mut BodyState,
236-
) {
237-
state.apply_move_events(analyzer.hir.effect_events(span));
238-
}
239-
240-
fn apply_retention_events(
241-
analyzer: &Analyzer<'_>,
242-
span: &crate::diagnostic::Span,
243-
state: &mut BodyState,
244-
) {
245-
state.apply_retention_events(analyzer.hir.effect_events(span));
246-
}
247-
248256
fn check_moved_uses_in_stmt(analyzer: &mut Analyzer<'_>, statement: &Stmt, state: &BodyState) {
249257
let mut uses = Vec::new();
250258
collect_stmt_idents(statement, &mut uses);

src/checks/local.rs

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::collections::{HashMap, HashSet};
22

33
use crate::diagnostic::Span;
4-
use crate::hir::{HirBinding, HirBindingKind, HirEffectEvent, HirEffectEventKind};
4+
use crate::hir::{HirBinding, HirBindingKind, HirEffectEvent, HirEffectEventKind, HirFunctionBody};
55

66
use super::body::Flow;
77

@@ -14,6 +14,50 @@ pub(crate) struct BodyState {
1414
pub(crate) value_types: HashMap<String, String>,
1515
}
1616

17+
pub(crate) struct LocalAnalysis {
18+
body: Option<HirFunctionBody>,
19+
}
20+
21+
impl LocalAnalysis {
22+
pub(crate) fn new(body: Option<&HirFunctionBody>) -> Self {
23+
Self {
24+
body: body.cloned(),
25+
}
26+
}
27+
28+
pub(crate) fn initial_state(&self) -> BodyState {
29+
let mut state = BodyState::default();
30+
if let Some(body) = &self.body {
31+
state.seed_params(&body.bindings);
32+
}
33+
state
34+
}
35+
36+
pub(crate) fn apply_move_events(&self, span: &Span, state: &mut BodyState) {
37+
state.apply_move_events(self.effect_events(span));
38+
}
39+
40+
pub(crate) fn apply_retention_events(&self, span: &Span, state: &mut BodyState) {
41+
state.apply_retention_events(self.effect_events(span));
42+
}
43+
44+
fn effect_events(&self, span: &Span) -> &[HirEffectEvent] {
45+
self.body
46+
.as_ref()
47+
.and_then(|body| events_at_span(&body.effect_events, span))
48+
.unwrap_or(&[])
49+
}
50+
}
51+
52+
fn events_at_span<'a>(events: &'a [HirEffectEvent], span: &Span) -> Option<&'a [HirEffectEvent]> {
53+
let start = events.iter().position(|event| event.span == *span)?;
54+
let end = events[start..]
55+
.iter()
56+
.position(|event| event.span != *span)
57+
.map_or(events.len(), |offset| start + offset);
58+
Some(&events[start..end])
59+
}
60+
1761
impl BodyState {
1862
pub(crate) fn seed_params(&mut self, bindings: &[HirBinding]) {
1963
for binding in bindings {
@@ -257,4 +301,50 @@ mod tests {
257301
assert!(!state.clean_locals.contains("image"));
258302
assert_eq!(state.moved["image"].line, 11);
259303
}
304+
305+
#[test]
306+
fn local_analysis_seeds_params_and_applies_events_by_span() {
307+
let body = HirFunctionBody {
308+
function_name: "run".to_string(),
309+
bindings: vec![HirBinding {
310+
function_name: "run".to_string(),
311+
name: "pool".to_string(),
312+
kind: HirBindingKind::Param,
313+
span: span(1),
314+
type_name: Some("ResourcePool<File>".to_string()),
315+
}],
316+
effect_events: vec![
317+
HirEffectEvent {
318+
function_name: "run".to_string(),
319+
kind: HirEffectEventKind::Retain {
320+
callee: "Cache.store".to_string(),
321+
param: "value".to_string(),
322+
},
323+
binding_name: "cached".to_string(),
324+
span: span(20),
325+
value_span: span(20),
326+
},
327+
HirEffectEvent {
328+
function_name: "run".to_string(),
329+
kind: HirEffectEventKind::Manage,
330+
binding_name: "image".to_string(),
331+
span: span(21),
332+
value_span: span(21),
333+
},
334+
],
335+
..HirFunctionBody::default()
336+
};
337+
let local_analysis = LocalAnalysis::new(Some(&body));
338+
let mut state = local_analysis.initial_state();
339+
state.bind_local("cached");
340+
state.bind_local("image");
341+
342+
assert_eq!(state.value_type("pool"), Some("ResourcePool<File>"));
343+
344+
local_analysis.apply_retention_events(&span(20), &mut state);
345+
local_analysis.apply_move_events(&span(21), &mut state);
346+
347+
assert!(!state.is_clean_local("cached"));
348+
assert_eq!(state.move_span("image").map(|span| span.line), Some(21));
349+
}
260350
}

0 commit comments

Comments
 (0)