Skip to main content

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}