Skip to main content

fallow_engine/
source.rs

1//! Source parsing contracts owned by the engine boundary.
2
3use std::sync::atomic::AtomicBool;
4
5use fallow_types::discover::DiscoveredFile;
6#[cfg(test)]
7pub use fallow_types::extract::{ExportName, MemberKind, VisibilityTag};
8pub use fallow_types::extract::{ModuleInfo, ParseResult, SourceReadFailure};
9
10type CacheStore = fallow_extract::cache::CacheStore;
11
12/// Parse the `fallow-ignore-next-line` and `fallow-ignore-file` comments of
13/// raw source text, without a parse. Unknown tokens are dropped.
14#[must_use]
15pub fn suppressions_from_text(source: &str) -> Vec<fallow_types::suppress::Suppression> {
16    fallow_extract::suppress::parse_suppressions_from_source(source).suppressions
17}
18
19/// On-demand, transient function extraction for local similar-code inference.
20pub mod similar_code {
21    use std::path::Path;
22
23    pub use fallow_types::similar_code::{
24        ExtractedSimilarCodeFunction, SIMILAR_CODE_EXTRACTION_SEMANTICS_VERSION,
25        SimilarCodeExtraction, SimilarCodeExtractionLimits, SimilarCodeExtractionSkip,
26        SimilarCodeExtractionSkipReason, SimilarCodeFunctionKind, SimilarCodeFunctionLocation,
27        SimilarCodeSideEffectHint, SimilarCodeSourceDigest,
28    };
29
30    /// Extract bounded named functions without touching the normal parse cache.
31    #[must_use]
32    pub fn extract(
33        path: &Path,
34        source: &str,
35        limits: SimilarCodeExtractionLimits,
36    ) -> SimilarCodeExtraction {
37        fallow_extract::extract_similar_code_functions(path, source, limits)
38    }
39}
40
41/// Source inventory walking for coverage and upload surfaces.
42pub mod inventory {
43    use std::path::Path;
44
45    use rustc_hash::FxHashMap;
46
47    /// A single static-inventory entry for one function.
48    ///
49    /// This is the engine-owned inventory contract exposed to CLI upload
50    /// surfaces. The extractor owns AST traversal; the engine owns the public
51    /// shape that downstream crates construct and upload.
52    #[derive(Debug, Clone, PartialEq, Eq)]
53    pub struct InventoryEntry {
54        /// Beacon-compatible function name.
55        pub name: String,
56        /// 1-based source line of the function declaration.
57        pub line: u32,
58        /// 1-indexed UTF-16 column of the function node start.
59        pub start_column: u32,
60        /// 1-based source line where the function node ends.
61        pub end_line: u32,
62        /// 1-indexed UTF-16 column of the function node end.
63        pub end_column: u32,
64        /// Content digest of the function's full-span source slice. `None`
65        /// for a function with too many nested functions below it.
66        pub source_hash: Option<String>,
67        /// Whether the name came from the callee of the call this function is
68        /// passed to (`arr.map(cb)` -> `map`) rather than from a declaration,
69        /// a binding, or a property key.
70        pub is_callback: bool,
71    }
72
73    impl From<fallow_extract::inventory::InventoryEntry> for InventoryEntry {
74        fn from(entry: fallow_extract::inventory::InventoryEntry) -> Self {
75            Self {
76                name: entry.name,
77                line: entry.line,
78                start_column: entry.start_column,
79                end_line: entry.end_line,
80                end_column: entry.end_column,
81                source_hash: entry.source_hash,
82                is_callback: entry.is_callback,
83            }
84        }
85    }
86
87    /// Per-function static complexity collected alongside the inventory walk.
88    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
89    pub struct InventoryComplexity {
90        /// `McCabe` cyclomatic complexity (1 + decision points).
91        pub cyclomatic: u16,
92        /// `SonarSource` cognitive complexity (structural + nesting penalty).
93        pub cognitive: u16,
94    }
95
96    impl From<fallow_extract::inventory::InventoryComplexity> for InventoryComplexity {
97        fn from(complexity: fallow_extract::inventory::InventoryComplexity) -> Self {
98            Self {
99                cyclomatic: complexity.cyclomatic,
100                cognitive: complexity.cognitive,
101            }
102        }
103    }
104
105    /// Walk source and emit engine-owned function inventory entries.
106    #[must_use]
107    pub fn walk_source(path: &Path, source: &str) -> Vec<InventoryEntry> {
108        fallow_extract::inventory::walk_source(path, source)
109            .into_iter()
110            .map(InventoryEntry::from)
111            .collect()
112    }
113
114    /// Walk source once and emit inventory entries plus static complexity by source hash.
115    #[must_use]
116    pub fn walk_source_with_complexity(
117        path: &Path,
118        source: &str,
119    ) -> (Vec<InventoryEntry>, FxHashMap<String, InventoryComplexity>) {
120        let (entries, complexity) =
121            fallow_extract::inventory::walk_source_with_complexity(path, source);
122        let entries = entries.into_iter().map(InventoryEntry::from).collect();
123        let complexity = complexity
124            .into_iter()
125            .map(|(hash, metrics)| (hash, InventoryComplexity::from(metrics)))
126            .collect();
127        (entries, complexity)
128    }
129}
130
131/// Parse discovered source files into typed module facts.
132///
133/// Keeping parsing behind the engine boundary lets sessions and future
134/// incremental runners choose cache policy without exposing the extract crate
135/// as the public orchestration layer.
136///
137/// `cancellation`, when set mid-parse, turns every remaining file into a no-op
138/// and truncates the returned modules; see
139/// [`fallow_extract::parse_all_files_cancellable`]. `flag_patterns` are the
140/// user flag patterns of the config the cache is keyed on.
141#[must_use]
142pub(crate) fn parse_all_files(
143    files: &[DiscoveredFile],
144    cache: Option<&CacheStore>,
145    need_complexity: bool,
146    cancellation: Option<&AtomicBool>,
147    flag_patterns: &fallow_types::extract::FlagPatterns,
148) -> ParseResult {
149    fallow_extract::parse_all_files_cancellable(
150        files,
151        cache,
152        need_complexity,
153        cancellation,
154        flag_patterns,
155    )
156}