Skip to content

Commit 021145f

Browse files
committed
Group body facts by function in HIR
1 parent 96c4a96 commit 021145f

3 files changed

Lines changed: 76 additions & 18 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ It currently implements:
2121
- HIR field-access facts with resolved base type, field type, and handle status where known
2222
- HIR effect events for `manage`, `take`, and retaining calls
2323
- HIR return facts with initial freshness proof classification
24+
- Per-function HIR body views that group bindings, calls, fields, effects, and returns
2425
- AST-driven mode and call checks for local-only features, named arguments, data effects, and retaining APIs
2526
- AST-driven body checks for local moves, early-exit-aware `fresh` returns, resource escape, resolved handle-field `take`, and managed closure captures
2627
- Focused check modules for mode, calls, body semantics, and forbidden operator behavior

src/checks/body.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ struct BodyState {
3636
}
3737

3838
fn seed_function_bindings(analyzer: &Analyzer<'_>, function: &FunctionDecl, state: &mut BodyState) {
39-
for binding in analyzer.hir.function_bindings(&function.name) {
39+
let Some(body) = analyzer.hir.function_body(&function.name) else {
40+
return;
41+
};
42+
for binding in &body.bindings {
4043
if binding.kind != HirBindingKind::Param {
4144
continue;
4245
}

src/hir.rs

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,16 @@ pub struct HirReturn {
161161
pub proof: HirReturnProof,
162162
}
163163

164+
#[derive(Debug, Clone, PartialEq, Eq, Default)]
165+
pub struct HirFunctionBody {
166+
pub function_name: String,
167+
pub bindings: Vec<HirBinding>,
168+
pub call_sites: Vec<HirCallSite>,
169+
pub field_accesses: Vec<HirFieldAccess>,
170+
pub effect_events: Vec<HirEffectEvent>,
171+
pub returns: Vec<HirReturn>,
172+
}
173+
164174
#[derive(Debug, Default)]
165175
pub struct Hir {
166176
signatures: HashMap<String, FunctionSig>,
@@ -171,13 +181,13 @@ pub struct Hir {
171181
call_resolutions_by_span: HashMap<Span, CallResolution>,
172182
bindings: Vec<HirBinding>,
173183
bindings_by_span: HashMap<Span, HirBinding>,
174-
bindings_by_function: HashMap<String, Vec<HirBinding>>,
175184
field_accesses: Vec<HirFieldAccess>,
176185
field_accesses_by_span: HashMap<Span, HirFieldAccess>,
177186
effect_events: Vec<HirEffectEvent>,
178187
effect_events_by_span: HashMap<Span, Vec<HirEffectEvent>>,
179188
returns: Vec<HirReturn>,
180189
returns_by_span: HashMap<Span, HirReturn>,
190+
function_bodies: HashMap<String, HirFunctionBody>,
181191
}
182192

183193
impl Hir {
@@ -262,10 +272,8 @@ impl Hir {
262272
self.bindings_by_span.get(span)
263273
}
264274

265-
pub fn function_bindings(&self, function_name: &str) -> &[HirBinding] {
266-
self.bindings_by_function
267-
.get(function_name)
268-
.map_or(&[], Vec::as_slice)
275+
pub fn function_body(&self, function_name: &str) -> Option<&HirFunctionBody> {
276+
self.function_bodies.get(function_name)
269277
}
270278

271279
pub fn field_access(&self, span: &Span) -> Option<&HirFieldAccess> {
@@ -354,16 +362,6 @@ impl Hir {
354362
.iter()
355363
.map(|binding| (binding.span.clone(), binding.clone()))
356364
.collect();
357-
self.bindings_by_function = facts.bindings.iter().fold(
358-
HashMap::<String, Vec<HirBinding>>::new(),
359-
|mut by_function, binding| {
360-
by_function
361-
.entry(binding.function_name.clone())
362-
.or_default()
363-
.push(binding.clone());
364-
by_function
365-
},
366-
);
367365
self.field_accesses_by_span = facts
368366
.field_accesses
369367
.iter()
@@ -384,6 +382,7 @@ impl Hir {
384382
.iter()
385383
.map(|return_fact| (return_fact.span.clone(), return_fact.clone()))
386384
.collect();
385+
self.function_bodies = build_function_bodies(&facts);
387386
self.call_sites = facts.call_sites;
388387
self.bindings = facts.bindings;
389388
self.field_accesses = facts.field_accesses;
@@ -392,6 +391,48 @@ impl Hir {
392391
}
393392
}
394393

394+
fn build_function_bodies(facts: &BodyFacts) -> HashMap<String, HirFunctionBody> {
395+
let mut bodies = HashMap::<String, HirFunctionBody>::new();
396+
for binding in &facts.bindings {
397+
body_entry(&mut bodies, &binding.function_name)
398+
.bindings
399+
.push(binding.clone());
400+
}
401+
for site in &facts.call_sites {
402+
body_entry(&mut bodies, &site.function_name)
403+
.call_sites
404+
.push(site.clone());
405+
}
406+
for field in &facts.field_accesses {
407+
body_entry(&mut bodies, &field.function_name)
408+
.field_accesses
409+
.push(field.clone());
410+
}
411+
for event in &facts.effect_events {
412+
body_entry(&mut bodies, &event.function_name)
413+
.effect_events
414+
.push(event.clone());
415+
}
416+
for return_fact in &facts.returns {
417+
body_entry(&mut bodies, &return_fact.function_name)
418+
.returns
419+
.push(return_fact.clone());
420+
}
421+
bodies
422+
}
423+
424+
fn body_entry<'a>(
425+
bodies: &'a mut HashMap<String, HirFunctionBody>,
426+
function_name: &str,
427+
) -> &'a mut HirFunctionBody {
428+
bodies
429+
.entry(function_name.to_string())
430+
.or_insert_with(|| HirFunctionBody {
431+
function_name: function_name.to_string(),
432+
..HirFunctionBody::default()
433+
})
434+
}
435+
395436
#[derive(Default)]
396437
struct BodyFacts {
397438
call_sites: Vec<HirCallSite>,
@@ -1398,7 +1439,10 @@ fn render(body: read String) -> Result<fresh Response, HttpError> {
13981439
));
13991440
assert!(matches!(sites[2].resolution, CallResolution::Unknown));
14001441

1401-
let bindings = hir.function_bindings("render");
1442+
let bindings = &hir
1443+
.function_body("render")
1444+
.expect("render body exists")
1445+
.bindings;
14021446
assert_eq!(bindings.len(), 2);
14031447
assert_eq!(bindings[0].kind, HirBindingKind::Param);
14041448
assert_eq!(bindings[0].name, "body");
@@ -1426,6 +1470,13 @@ fn render(body: read String) -> Result<fresh Response, HttpError> {
14261470
.proof,
14271471
HirReturnProof::Ident { .. }
14281472
));
1473+
1474+
let body = hir.function_body("render").expect("function body exists");
1475+
assert_eq!(body.function_name, "render");
1476+
assert_eq!(body.bindings.len(), 2);
1477+
assert_eq!(body.call_sites.len(), 3);
1478+
assert_eq!(body.effect_events.len(), 0);
1479+
assert_eq!(body.returns.len(), 1);
14291480
}
14301481

14311482
#[test]
@@ -1440,7 +1491,10 @@ fn load(path: read Path) -> Unit {
14401491

14411492
let program = parse_source("test.rss", source);
14421493
let hir = Hir::from_syntax(&program);
1443-
let bindings = hir.function_bindings("load");
1494+
let bindings = &hir
1495+
.function_body("load")
1496+
.expect("load body exists")
1497+
.bindings;
14441498

14451499
assert_eq!(bindings.len(), 2);
14461500
assert_eq!(bindings[1].kind, HirBindingKind::LocalLet);

0 commit comments

Comments
 (0)