pedant_core/ir/semantic/function_summary.rs
1//! Per-function analysis summaries.
2//!
3//! `FunctionSummaryData` stores range indices into the file-level
4//! `data_flows` array plus lock acquisitions derived once from `FnContext`.
5//! `FunctionAnalysisSummary` is the borrowed public view that resolves
6//! ranges against the shared flow slice.
7
8use super::super::facts::DataFlowFact;
9use super::common::LockAcquisition;
10
11/// Half-open range into the file-level `data_flows` slice.
12///
13/// Stored as `u32` pair — a single file will never produce 4 billion
14/// flow facts. Eliminates per-function `Box<[DataFlowFact]>` ownership;
15/// all facts live once in the file-level `Arc<[DataFlowFact]>`.
16#[derive(Clone, Copy)]
17pub(super) struct FlowRange {
18 start: u32,
19 end: u32,
20}
21
22impl FlowRange {
23 /// Record a range from the current aggregate length before and after
24 /// appending a batch of facts.
25 pub(super) fn new(start: usize, end: usize) -> Self {
26 Self {
27 start: start as u32,
28 end: end as u32,
29 }
30 }
31
32 /// Slice the shared flow array to this function's domain partition.
33 pub(super) fn slice<'a>(&self, flows: &'a [DataFlowFact]) -> &'a [DataFlowFact] {
34 &flows[self.start as usize..self.end as usize]
35 }
36}
37
38/// Owned per-function cached semantic state.
39///
40/// Stores domain-partitioned flow ranges (indices into the file-level
41/// `data_flows`) and lock acquisitions. Everything is immutable after
42/// construction and borrowed through `FunctionAnalysisSummary`.
43pub(super) struct FunctionSummaryData {
44 pub(super) lock_acquisitions: Box<[LockAcquisition]>,
45 pub(super) taint: FlowRange,
46 pub(super) quality: FlowRange,
47 pub(super) performance: FlowRange,
48 pub(super) concurrency: FlowRange,
49}
50
51/// Borrowed view into one function's precomputed analysis state.
52///
53/// Resolves `FlowRange` indices against the file-level `data_flows`
54/// slice. Never owns data.
55pub struct FunctionAnalysisSummary<'a> {
56 data: &'a FunctionSummaryData,
57 flows: &'a [DataFlowFact],
58}
59
60impl<'a> FunctionAnalysisSummary<'a> {
61 /// Create a summary view from stored function data and the shared flow slice.
62 pub(super) fn new(data: &'a FunctionSummaryData, flows: &'a [DataFlowFact]) -> Self {
63 Self { data, flows }
64 }
65
66 /// Quality findings: dead stores, discarded results, partial error
67 /// handling, swallowed `.ok()`, immutable growable bindings.
68 pub fn quality_issues(&self) -> &[DataFlowFact] {
69 self.data.quality.slice(self.flows)
70 }
71
72 /// Performance findings: repeated calls, unnecessary clones,
73 /// allocations in loops, redundant collects.
74 pub fn performance_issues(&self) -> &[DataFlowFact] {
75 self.data.performance.slice(self.flows)
76 }
77
78 /// Concurrency findings: lock guards across `.await`, inconsistent
79 /// lock ordering, unobserved spawn calls.
80 pub fn concurrency_issues(&self) -> &[DataFlowFact] {
81 self.data.concurrency.slice(self.flows)
82 }
83
84 /// Taint flow findings: capability source → sink propagation.
85 pub fn taint_flows(&self) -> &[DataFlowFact] {
86 self.data.taint.slice(self.flows)
87 }
88}