Skip to main content

comindly_core/
facts.rs

1//! Per-file syntactic facts the parser extracts and the resolver binds: imports, references
2//! (as dotted paths) and type hints. Scope ids are rendered `GlobalSymbolId`s.
3
4use serde::{Deserialize, Serialize};
5
6use crate::model::Language;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9pub struct FileFacts {
10    pub repo: String,
11    pub path: String,
12    pub language: Language,
13    /// Rendered id of the file symbol.
14    pub file_id: String,
15    /// Names other files import this one by (`acme.common.http`, `@acme/ui-kit`).
16    pub modules: Vec<String>,
17    pub imports: Vec<Import>,
18    pub refs: Vec<Ref>,
19    pub types: Vec<TypeHint>,
20    /// Compiler-grade occurrences from a SCIP index, when one was ingested.
21    #[serde(default)]
22    pub scip: Option<ScipFacts>,
23    /// Service-boundary facts (routes, clients, queue/bucket/table operations).
24    #[serde(default)]
25    pub contracts: Vec<ContractFact>,
26    /// Statically known values of module- and class-level names (for contract keys).
27    #[serde(default)]
28    pub values: Vec<ValueFact>,
29    /// Parameter names of callables, in order (receiver excluded), for param-flow summaries.
30    #[serde(default)]
31    pub params: Vec<ParamFact>,
32    /// Calls with statically readable arguments, for param-flow summaries.
33    #[serde(default)]
34    pub calls: Vec<CallFact>,
35}
36
37/// A queue (`sqs`), topic (`sns`) or bucket (`s3`) named in IaC; `logical` names are the
38/// short name a module expands (`inference-tasks`), not the full resource name.
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
40pub struct InfraRef {
41    pub scheme: String,
42    pub name: String,
43    pub logical: bool,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47pub struct ParamFact {
48    pub func: String,
49    pub names: Vec<String>,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
53pub struct CallFact {
54    pub from: String,
55    pub callee: Vec<String>,
56    pub args: Args,
57    pub line: u32,
58}
59
60/// A value an expression evaluates to, as far as static analysis can tell.
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62pub enum ValueExpr {
63    Lit(String),
64    /// An f-string / template: literal parts and placeholder expressions.
65    Template(Vec<TemplatePart>),
66    /// A name or attribute path, resolved later (`QUEUE_URL`, `settings.bucket`).
67    Name(Vec<String>),
68    /// An environment variable read (`os.environ["X"]`, `os.getenv("X")`).
69    Env(String),
70    /// `a / b` (path join, `sep = "/"`) or `a + b` (concatenation, `sep = ""`).
71    Join {
72        sep: String,
73        parts: Vec<ValueExpr>,
74    },
75    /// A call: `Path("/x")`, `str(x)`, `AttrDict(k=v)`; evaluated by known callee names.
76    Call {
77        func: Vec<String>,
78        args: Vec<ValueExpr>,
79        kwargs: Vec<(String, ValueExpr)>,
80    },
81    /// A literal mapping with string keys: `{"k": v}`.
82    Record(Vec<(String, ValueExpr)>),
83}
84
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
86pub enum TemplatePart {
87    Lit(String),
88    Expr(Option<Vec<String>>),
89}
90
91#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
92pub struct ValueFact {
93    /// File id (module level) or class id (class attribute).
94    pub scope: String,
95    pub name: String,
96    pub value: ValueExpr,
97}
98
99/// A call's arguments as values: positional, then `name=` keywords.
100#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
101pub struct Args {
102    pub positional: Vec<Option<ValueExpr>>,
103    pub keywords: Vec<(String, ValueExpr)>,
104    /// Positional arguments that are calls, by callee path (`Course(...)`).
105    pub calls: Vec<(usize, Vec<String>)>,
106}
107
108impl Args {
109    pub fn get(&self, keyword: &str, position: usize) -> Option<&ValueExpr> {
110        self.keywords
111            .iter()
112            .find(|(k, _)| k == keyword)
113            .map(|(_, v)| v)
114            .or_else(|| self.positional.get(position).and_then(Option::as_ref))
115    }
116}
117
118#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
119pub enum ContractFact {
120    /// `@router.get("/x")` on `handler`.
121    Route {
122        handler: String,
123        receiver: Vec<String>,
124        method: String,
125        path: ValueExpr,
126        line: u32,
127        /// A declarative client method (Feign) consumes the route instead of serving it.
128        #[serde(default)]
129        client: bool,
130    },
131    /// `mux.HandleFunc("GET /x", h)` / `app.get("/x", h)` in `from`; `handler` names the
132    /// function when it is a reference rather than an inline closure.
133    Handle {
134        from: String,
135        /// The router or route group the handler is registered on.
136        #[serde(default)]
137        receiver: Vec<String>,
138        method: String,
139        path: ValueExpr,
140        handler: Vec<String>,
141        line: u32,
142    },
143    /// `target = Ctor(args)` where the constructor may be a router, app or client.
144    Construct {
145        scope: String,
146        target: String,
147        ctor: Vec<String>,
148        args: Args,
149    },
150    /// A method or function call that may be a service operation.
151    Op {
152        from: String,
153        receiver: Vec<String>,
154        method: String,
155        args: Args,
156        line: u32,
157    },
158    /// Infrastructure as code declares (or subscribes) a queue, topic or bucket.
159    Infra {
160        declares: InfraRef,
161        /// The topic this queue is subscribed to.
162        #[serde(default)]
163        subscribes: Option<InfraRef>,
164    },
165    /// `__tablename__ = "x"` on a model class.
166    Table { class: String, name: String },
167}
168
169#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
170pub struct ScipFacts {
171    /// (SCIP symbol, our symbol id) for definitions in this file.
172    pub defs: Vec<(String, String)>,
173    pub refs: Vec<ScipRef>,
174}
175
176#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
177pub struct ScipRef {
178    pub line: u32,
179    pub name: String,
180    pub symbol: String,
181}
182
183#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
184pub struct Import {
185    /// Python dotted module (without leading dots) or a TS module specifier.
186    pub module: String,
187    /// Python relative-import level (number of leading dots); 0 for absolute and TS.
188    pub level: u32,
189    pub items: Vec<ImportItem>,
190    pub line: u32,
191    /// Repo-relative file a non-relative TS specifier maps to via tsconfig `paths`/`baseUrl`.
192    #[serde(default)]
193    pub file: Option<String>,
194}
195
196#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
197pub enum ImportItem {
198    /// `from m import name as alias` / `import { name as alias } from "m"`.
199    Name { name: String, alias: Option<String> },
200    /// The module itself: `import a.b [as x]`, `import * as x from "m"`, `import "m"`.
201    Module { alias: Option<String> },
202    /// `from m import *` / `export * from "m"`.
203    All,
204}
205
206#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
207pub struct Ref {
208    pub kind: RefKind,
209    /// Scope the reference occurs in (callable, class for bases, or file).
210    pub from: String,
211    /// Dotted path of the referenced expression, e.g. `["self", "api", "get"]`.
212    pub path: Vec<String>,
213    pub line: u32,
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
217pub enum RefKind {
218    Call,
219    New,
220    Inherits,
221    Implements,
222}
223
224/// `name` has a type in `scope`: a parameter/variable, or `self.x`/`this.x` on a class scope.
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
226pub struct TypeHint {
227    pub scope: String,
228    pub name: String,
229    pub source: TypeSource,
230}
231
232#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
233pub enum TypeSource {
234    /// Declared type path: `x: Client`.
235    Annotation(Vec<String>),
236    /// Assigned from a call or `new`: `x = Client(...)`.
237    Construct(Vec<String>),
238    /// Copied from another name in another scope: `self.api = api` (param of `__init__`).
239    Alias { name: String, scope: String },
240}