Skip to main content

normalize_facts_rules_api/
relations.rs

1//! Relations (facts) that rules operate on.
2//!
3//! These are the inputs to Datalog rules, extracted from code by normalize-facts.
4//! Each relation type maps to a Datalog predicate:
5//!
6//! - `symbol(file, name, kind, line)` - defined symbols
7//! - `import(from_file, to_module, name)` - import statements
8//! - `call(caller_file, caller_name, callee_name, line)` - function calls
9//! - `visibility(file, name, vis)` - symbol visibility
10//! - `attribute(file, name, attr)` - symbol attributes (one per attribute)
11//! - `parent(file, child_name, parent_name)` - symbol nesting hierarchy
12//! - `qualifier(caller_file, caller_name, callee_name, qual)` - call qualifier
13//! - `symbol_range(file, name, start_line, end_line)` - symbol span
14//! - `implements(file, name, interface)` - interface/trait implementation
15//! - `is_impl(file, name)` - symbol is a trait/interface implementation
16//! - `type_method(file, type_name, method_name)` - method signatures on types
17//!
18//! Cross-file resolution predicates (Phase 0):
19//!
20//! - `resolved_import(from_file, to_file, imported_name, local_alias, kind)` - resolved imports
21//! - `module(file, canonical_module_path)` - canonical module identity of a file
22//! - `export(file, name, kind)` - exported symbols
23//! - `reexport(from_file, original_file, original_name, exported_as)` - re-export chains
24//! - `symbol_use(file, name, line)` - symbol reference/use sites
25//! - `resolved_reference(use_file, use_line, def_file, def_name, def_kind)` - resolved symbol refs
26//! - `resolved_call(caller_file, caller_name, callee_file, callee_name, line)` - resolved calls
27//! - `module_search_path(workspace_root, language, kind, path)` - module search paths
28
29/// A symbol fact: a named entity defined in a file.
30///
31/// Maps to Datalog: `symbol(file, name, kind, line)`
32#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
33#[rkyv(derive(Debug))]
34pub struct SymbolFact {
35    /// File path relative to project root
36    pub file: String,
37    /// Symbol name
38    pub name: String,
39    /// Symbol kind (function, class, method, etc.)
40    pub kind: String,
41    /// Line number where symbol is defined
42    pub line: u32,
43}
44
45/// An import fact: a dependency from one file to another module.
46///
47/// Maps to Datalog: `import(from_file, to_module, name)`
48#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
49#[rkyv(derive(Debug))]
50pub struct ImportFact {
51    /// File containing the import
52    pub from_file: String,
53    /// Raw module specifier as written in the source.
54    ///
55    /// The value depends on the language and import style:
56    ///
57    /// - **Relative or absolute file path** — e.g. `"../foo"`, `"./utils"` (JS/TS, Python
58    ///   relative imports). The path is as written in source, not resolved to an absolute path.
59    /// - **Module name** — e.g. `"os"` (Python stdlib), `"std::collections"` (Rust), `"fmt"`
60    ///   (Go). These are not file paths and cannot be resolved without a module resolver.
61    /// - **Empty string `""`** — when the grammar does not expose a module path for the
62    ///   import, or for star imports that name no explicit module (e.g. some wildcard import
63    ///   syntaxes). Callers should treat `""` as "module not known".
64    ///
65    /// Resolved file paths (when available) are stored separately in the index, not here.
66    pub module_specifier: String,
67    /// Name being imported (or "*" for wildcard)
68    pub name: String,
69}
70
71/// A call fact: a function call from one symbol to another.
72///
73/// Maps to Datalog: `call(caller_file, caller_name, callee_name, line)`
74#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
75#[rkyv(derive(Debug))]
76pub struct CallFact {
77    /// File containing the call
78    pub caller_file: String,
79    /// Name of the calling function/method
80    pub caller_name: String,
81    /// Name of the called function/method
82    pub callee_name: String,
83    /// Line number of the call
84    pub line: u32,
85}
86
87/// A visibility fact: the visibility of a symbol.
88///
89/// Maps to Datalog: `visibility(file, name, vis)`
90#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
91#[rkyv(derive(Debug))]
92pub struct VisibilityFact {
93    /// File path relative to project root
94    pub file: String,
95    /// Symbol name
96    pub name: String,
97    /// Visibility: "public", "private", "protected", "internal"
98    pub visibility: String,
99}
100
101/// An attribute fact: one attribute annotation on a symbol.
102///
103/// Maps to Datalog: `attribute(file, name, attr)`
104#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
105#[rkyv(derive(Debug))]
106pub struct AttributeFact {
107    /// File path relative to project root
108    pub file: String,
109    /// Symbol name
110    pub name: String,
111    /// Attribute string (e.g. "#[derive(Debug)]", "@Override")
112    pub attribute: String,
113}
114
115/// A parent fact: symbol nesting hierarchy.
116///
117/// Maps to Datalog: `parent(file, child_name, parent_name)`
118#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
119#[rkyv(derive(Debug))]
120pub struct ParentFact {
121    /// File path relative to project root
122    pub file: String,
123    /// Child symbol name
124    pub child_name: String,
125    /// Parent symbol name
126    pub parent_name: String,
127}
128
129/// A qualifier fact: call qualifier (receiver/module).
130///
131/// Maps to Datalog: `qualifier(caller_file, caller_name, callee_name, qual)`
132#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
133#[rkyv(derive(Debug))]
134pub struct QualifierFact {
135    /// File containing the call
136    pub caller_file: String,
137    /// Name of the calling function/method
138    pub caller_name: String,
139    /// Name of the called function/method
140    pub callee_name: String,
141    /// Qualifier ("self", module name, etc.)
142    pub qualifier: String,
143}
144
145/// A symbol range fact: start and end lines of a symbol.
146///
147/// Maps to Datalog: `symbol_range(file, name, start_line, end_line)`
148#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
149#[rkyv(derive(Debug))]
150pub struct SymbolRangeFact {
151    /// File path relative to project root
152    pub file: String,
153    /// Symbol name
154    pub name: String,
155    /// Start line number
156    pub start_line: u32,
157    /// End line number
158    pub end_line: u32,
159}
160
161/// An implements fact: a symbol implements an interface/trait.
162///
163/// Maps to Datalog: `implements(file, name, interface)`
164#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
165#[rkyv(derive(Debug))]
166pub struct ImplementsFact {
167    /// File path relative to project root
168    pub file: String,
169    /// Symbol name
170    pub name: String,
171    /// Interface/trait name
172    pub interface: String,
173}
174
175/// An is_impl fact: symbol is a trait/interface implementation.
176///
177/// Maps to Datalog: `is_impl(file, name)`
178#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
179#[rkyv(derive(Debug))]
180pub struct IsImplFact {
181    /// File path relative to project root
182    pub file: String,
183    /// Symbol name
184    pub name: String,
185}
186
187/// A type method fact: a method signature on a type.
188///
189/// Maps to Datalog: `type_method(file, type_name, method_name)`
190#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
191#[rkyv(derive(Debug))]
192pub struct TypeMethodFact {
193    /// File path relative to project root
194    pub file: String,
195    /// Type (interface/class) name
196    pub type_name: String,
197    /// Method name
198    pub method_name: String,
199}
200
201/// A resolved import fact: an import statement resolved to a specific file.
202///
203/// Maps to Datalog: `resolved_import(from_file, to_file, imported_name, local_alias, kind)`
204/// kind ∈ {"direct", "glob", "reexport", "unresolved"}
205#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
206#[rkyv(derive(Debug))]
207pub struct ResolvedImportFact {
208    /// File containing the import
209    pub from_file: String,
210    /// Resolved target file
211    pub to_file: String,
212    /// The imported name (e.g. "HashMap", "*" for glob)
213    pub imported_name: String,
214    /// Local alias (same as imported_name if no alias)
215    pub local_alias: String,
216    /// Resolution kind: "direct", "glob", "reexport", or "unresolved"
217    pub kind: String,
218}
219
220/// A module fact: canonical module identity of a file.
221///
222/// Maps to Datalog: `module(file, canonical_module_path)`
223#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
224#[rkyv(derive(Debug))]
225pub struct ModuleFact {
226    /// File path relative to project root
227    pub file: String,
228    /// Canonical module path (e.g. "mycrate::foo::bar")
229    pub canonical_module_path: String,
230}
231
232/// An export fact: a symbol exported from a file.
233///
234/// Maps to Datalog: `export(file, name, kind)`
235/// kind ∈ {"value", "type", "module", "reexport"}
236#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
237#[rkyv(derive(Debug))]
238pub struct ExportFact {
239    /// File path relative to project root
240    pub file: String,
241    /// Exported name
242    pub name: String,
243    /// Export kind: "value", "type", "module", or "reexport"
244    pub kind: String,
245}
246
247/// A reexport fact: a symbol re-exported through an intermediate file.
248///
249/// Maps to Datalog: `reexport(from_file, original_file, original_name, exported_as)`
250#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
251#[rkyv(derive(Debug))]
252pub struct ReexportFact {
253    /// File doing the re-export
254    pub from_file: String,
255    /// File where the symbol is originally defined
256    pub original_file: String,
257    /// Original name of the symbol
258    pub original_name: String,
259    /// Name it is exported as (may differ with `as` alias)
260    pub exported_as: String,
261}
262
263/// A symbol use fact: a reference/use site of a named symbol.
264///
265/// Maps to Datalog: `symbol_use(file, name, line)`
266#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
267#[rkyv(derive(Debug))]
268pub struct SymbolUseFact {
269    /// File containing the use
270    pub file: String,
271    /// Name being used
272    pub name: String,
273    /// Line number of the use
274    pub line: u32,
275}
276
277/// A resolved reference fact: a symbol use resolved to its definition.
278///
279/// Maps to Datalog: `resolved_reference(use_file, use_line, def_file, def_name, def_kind)`
280#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
281#[rkyv(derive(Debug))]
282pub struct ResolvedReferenceFact {
283    /// File containing the use
284    pub use_file: String,
285    /// Line of the use
286    pub use_line: u32,
287    /// File containing the definition
288    pub def_file: String,
289    /// Name of the defined symbol
290    pub def_name: String,
291    /// Kind of the defined symbol
292    pub def_kind: String,
293}
294
295/// A resolved call fact: a function call resolved to its definition file.
296///
297/// Maps to Datalog: `resolved_call(caller_file, caller_name, callee_file, callee_name, line)`
298#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
299#[rkyv(derive(Debug))]
300pub struct ResolvedCallFact {
301    /// File containing the call
302    pub caller_file: String,
303    /// Name of the calling function
304    pub caller_name: String,
305    /// File containing the callee definition
306    pub callee_file: String,
307    /// Name of the called function
308    pub callee_name: String,
309    /// Line number of the call
310    pub line: u32,
311}
312
313/// A module search path fact: a directory to search for modules.
314///
315/// Maps to Datalog: `module_search_path(workspace_root, language, kind, path)`
316#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
317#[rkyv(derive(Debug))]
318pub struct ModuleSearchPathFact {
319    /// Workspace root this path belongs to
320    pub workspace_root: String,
321    /// Language this path applies to (e.g. "rust", "python")
322    pub language: String,
323    /// Kind of search path (e.g. "source", "stdlib", "third-party")
324    pub kind: String,
325    /// The search path
326    pub path: String,
327}
328
329/// A CFG block fact.
330///
331/// Maps to Datalog: `cfg_block(file, func, func_line, block, kind)`
332#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
333#[rkyv(derive(Debug))]
334pub struct CfgBlockFact {
335    /// Source file path
336    pub file: String,
337    /// Qualified function name
338    pub func: String,
339    /// Function start line (1-based)
340    pub func_line: u32,
341    /// Block ID (0-based, unique within function)
342    pub block: u32,
343    /// Block kind (e.g. "entry", "exit", "branch", "loophead")
344    pub kind: String,
345}
346
347/// A CFG edge fact.
348///
349/// Maps to Datalog: `cfg_edge(file, func, func_line, from, to, kind, exception_type)`
350///
351/// `exception_type` is only meaningful for `kind == "exception"` edges:
352/// - Empty string = conservative (type unknown, applies to any exception).
353/// - Non-empty string = the exception type name (e.g. `"IOException"`).
354#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
355#[rkyv(derive(Debug))]
356pub struct CfgEdgeFact {
357    /// Source file path
358    pub file: String,
359    /// Qualified function name
360    pub func: String,
361    /// Function start line (1-based)
362    pub func_line: u32,
363    /// Source block ID
364    pub from: u32,
365    /// Target block ID
366    pub to: u32,
367    /// Edge kind (e.g. "fallthrough", "conditionaltrue", "backedge", "exception")
368    pub kind: String,
369    /// Exception type for `kind == "exception"` edges. Empty string = conservative.
370    pub exception_type: String,
371}
372
373/// A CFG variable definition fact.
374///
375/// Maps to Datalog: `cfg_def(file, func, func_line, block, name)`
376#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
377#[rkyv(derive(Debug))]
378pub struct CfgDefFact {
379    /// Source file path
380    pub file: String,
381    /// Qualified function name
382    pub func: String,
383    /// Function start line (1-based)
384    pub func_line: u32,
385    /// Block ID this def occurs in
386    pub block: u32,
387    /// Name of the variable being defined
388    pub name: String,
389}
390
391/// A CFG variable use fact.
392///
393/// Maps to Datalog: `cfg_use(file, func, func_line, block, name)`
394#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
395#[rkyv(derive(Debug))]
396pub struct CfgUseFact {
397    /// Source file path
398    pub file: String,
399    /// Qualified function name
400    pub func: String,
401    /// Function start line (1-based)
402    pub func_line: u32,
403    /// Block ID this use occurs in
404    pub block: u32,
405    /// Name of the variable being used
406    pub name: String,
407}
408
409/// A CFG side-effect fact.
410///
411/// Maps to Datalog: `cfg_effect(file, func, func_line, block, kind, line, label)`
412///
413/// `kind` is one of: `"await"`, `"defer"`, `"yield"`, `"acquire"`, `"release"`, `"send"`, `"receive"`.
414/// `label` is an optional text label (resource name, expression text, etc.; empty string if absent).
415#[derive(Clone, Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
416#[rkyv(derive(Debug))]
417pub struct CfgEffectFact {
418    /// Source file path
419    pub file: String,
420    /// Qualified function name
421    pub func: String,
422    /// Function start line (1-based)
423    pub func_line: u32,
424    /// Block ID this effect occurs in
425    pub block: u32,
426    /// Effect kind string (e.g. "await", "defer", "yield")
427    pub kind: String,
428    /// Source line of the effect (1-based)
429    pub line: u32,
430    /// Optional label (resource name, expression text). Empty string if absent.
431    pub label: String,
432}
433
434/// All relations (facts) available to rules.
435///
436/// This is the complete set of facts extracted from a codebase.
437/// Rule packs receive this and apply Datalog rules over it.
438#[derive(Clone, Debug, Default, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
439#[rkyv(derive(Debug))]
440pub struct Relations {
441    /// All symbols defined in the codebase
442    pub symbols: Vec<SymbolFact>,
443    /// All imports in the codebase
444    pub imports: Vec<ImportFact>,
445    /// All function calls in the codebase
446    pub calls: Vec<CallFact>,
447    /// Symbol visibility facts
448    pub visibilities: Vec<VisibilityFact>,
449    /// Symbol attribute facts (one per attribute per symbol)
450    pub attributes: Vec<AttributeFact>,
451    /// Symbol parent-child hierarchy
452    pub parents: Vec<ParentFact>,
453    /// Call qualifier facts (receiver/module on calls)
454    pub qualifiers: Vec<QualifierFact>,
455    /// Symbol range facts (start and end lines)
456    pub symbol_ranges: Vec<SymbolRangeFact>,
457    /// Implements facts (symbol implements interface/trait)
458    pub implements: Vec<ImplementsFact>,
459    /// Is-impl facts (symbol is a trait/interface implementation)
460    pub is_impls: Vec<IsImplFact>,
461    /// Type method facts (method signatures on types)
462    pub type_methods: Vec<TypeMethodFact>,
463    /// Resolved import facts (import resolved to a specific file)
464    pub resolved_imports: Vec<ResolvedImportFact>,
465    /// Module identity facts (file → canonical module path)
466    pub modules: Vec<ModuleFact>,
467    /// Export facts (symbol exported from a file)
468    pub exports: Vec<ExportFact>,
469    /// Reexport facts (symbol re-exported through an intermediate)
470    pub reexports: Vec<ReexportFact>,
471    /// Symbol use facts (reference/use sites)
472    pub symbol_uses: Vec<SymbolUseFact>,
473    /// Resolved reference facts (use resolved to definition)
474    pub resolved_references: Vec<ResolvedReferenceFact>,
475    /// Resolved call facts (call resolved to definition file)
476    pub resolved_calls: Vec<ResolvedCallFact>,
477    /// Module search path facts (directories to search for modules)
478    pub module_search_paths: Vec<ModuleSearchPathFact>,
479    /// CFG block facts (one per basic block per function)
480    pub cfg_blocks: Vec<CfgBlockFact>,
481    /// CFG edge facts (one per control-flow edge per function)
482    pub cfg_edges: Vec<CfgEdgeFact>,
483    /// CFG variable definition facts (one per def site per block)
484    pub cfg_defs: Vec<CfgDefFact>,
485    /// CFG variable use facts (one per use site per block)
486    pub cfg_uses: Vec<CfgUseFact>,
487    /// CFG side-effect facts (await, defer, yield, acquire, release, send, receive)
488    pub cfg_effects: Vec<CfgEffectFact>,
489}
490
491impl Relations {
492    /// Create empty relations
493    pub fn new() -> Self {
494        Self::default()
495    }
496
497    /// Add a symbol fact
498    pub fn add_symbol(&mut self, file: &str, name: &str, kind: &str, line: u32) {
499        self.symbols.push(SymbolFact {
500            file: file.into(),
501            name: name.into(),
502            kind: kind.into(),
503            line,
504        });
505    }
506
507    /// Add an import fact
508    pub fn add_import(&mut self, from_file: &str, to_module: &str, name: &str) {
509        self.imports.push(ImportFact {
510            from_file: from_file.into(),
511            module_specifier: to_module.into(),
512            name: name.into(),
513        });
514    }
515
516    /// Add a call fact
517    pub fn add_call(&mut self, caller_file: &str, caller_name: &str, callee_name: &str, line: u32) {
518        self.calls.push(CallFact {
519            caller_file: caller_file.into(),
520            caller_name: caller_name.into(),
521            callee_name: callee_name.into(),
522            line,
523        });
524    }
525
526    /// Add a visibility fact
527    pub fn add_visibility(&mut self, file: &str, name: &str, visibility: &str) {
528        self.visibilities.push(VisibilityFact {
529            file: file.into(),
530            name: name.into(),
531            visibility: visibility.into(),
532        });
533    }
534
535    /// Add an attribute fact
536    pub fn add_attribute(&mut self, file: &str, name: &str, attribute: &str) {
537        self.attributes.push(AttributeFact {
538            file: file.into(),
539            name: name.into(),
540            attribute: attribute.into(),
541        });
542    }
543
544    /// Add a parent fact
545    pub fn add_parent(&mut self, file: &str, child_name: &str, parent_name: &str) {
546        self.parents.push(ParentFact {
547            file: file.into(),
548            child_name: child_name.into(),
549            parent_name: parent_name.into(),
550        });
551    }
552
553    /// Add a qualifier fact
554    pub fn add_qualifier(
555        &mut self,
556        caller_file: &str,
557        caller_name: &str,
558        callee_name: &str,
559        qualifier: &str,
560    ) {
561        self.qualifiers.push(QualifierFact {
562            caller_file: caller_file.into(),
563            caller_name: caller_name.into(),
564            callee_name: callee_name.into(),
565            qualifier: qualifier.into(),
566        });
567    }
568
569    /// Add a symbol range fact
570    pub fn add_symbol_range(&mut self, file: &str, name: &str, start_line: u32, end_line: u32) {
571        self.symbol_ranges.push(SymbolRangeFact {
572            file: file.into(),
573            name: name.into(),
574            start_line,
575            end_line,
576        });
577    }
578
579    /// Add an implements fact
580    pub fn add_implements(&mut self, file: &str, name: &str, interface: &str) {
581        self.implements.push(ImplementsFact {
582            file: file.into(),
583            name: name.into(),
584            interface: interface.into(),
585        });
586    }
587
588    /// Add an is_impl fact
589    pub fn add_is_impl(&mut self, file: &str, name: &str) {
590        self.is_impls.push(IsImplFact {
591            file: file.into(),
592            name: name.into(),
593        });
594    }
595
596    /// Add a type method fact
597    pub fn add_type_method(&mut self, file: &str, type_name: &str, method_name: &str) {
598        self.type_methods.push(TypeMethodFact {
599            file: file.into(),
600            type_name: type_name.into(),
601            method_name: method_name.into(),
602        });
603    }
604
605    /// Add a resolved import fact
606    pub fn add_resolved_import(
607        &mut self,
608        from_file: &str,
609        to_file: &str,
610        imported_name: &str,
611        local_alias: &str,
612        kind: &str,
613    ) {
614        self.resolved_imports.push(ResolvedImportFact {
615            from_file: from_file.into(),
616            to_file: to_file.into(),
617            imported_name: imported_name.into(),
618            local_alias: local_alias.into(),
619            kind: kind.into(),
620        });
621    }
622
623    /// Add a module fact
624    pub fn add_module(&mut self, file: &str, canonical_module_path: &str) {
625        self.modules.push(ModuleFact {
626            file: file.into(),
627            canonical_module_path: canonical_module_path.into(),
628        });
629    }
630
631    /// Add an export fact
632    pub fn add_export(&mut self, file: &str, name: &str, kind: &str) {
633        self.exports.push(ExportFact {
634            file: file.into(),
635            name: name.into(),
636            kind: kind.into(),
637        });
638    }
639
640    /// Add a reexport fact
641    pub fn add_reexport(
642        &mut self,
643        from_file: &str,
644        original_file: &str,
645        original_name: &str,
646        exported_as: &str,
647    ) {
648        self.reexports.push(ReexportFact {
649            from_file: from_file.into(),
650            original_file: original_file.into(),
651            original_name: original_name.into(),
652            exported_as: exported_as.into(),
653        });
654    }
655
656    /// Add a symbol use fact
657    pub fn add_symbol_use(&mut self, file: &str, name: &str, line: u32) {
658        self.symbol_uses.push(SymbolUseFact {
659            file: file.into(),
660            name: name.into(),
661            line,
662        });
663    }
664
665    /// Add a resolved reference fact
666    pub fn add_resolved_reference(
667        &mut self,
668        use_file: &str,
669        use_line: u32,
670        def_file: &str,
671        def_name: &str,
672        def_kind: &str,
673    ) {
674        self.resolved_references.push(ResolvedReferenceFact {
675            use_file: use_file.into(),
676            use_line,
677            def_file: def_file.into(),
678            def_name: def_name.into(),
679            def_kind: def_kind.into(),
680        });
681    }
682
683    /// Add a resolved call fact
684    pub fn add_resolved_call(
685        &mut self,
686        caller_file: &str,
687        caller_name: &str,
688        callee_file: &str,
689        callee_name: &str,
690        line: u32,
691    ) {
692        self.resolved_calls.push(ResolvedCallFact {
693            caller_file: caller_file.into(),
694            caller_name: caller_name.into(),
695            callee_file: callee_file.into(),
696            callee_name: callee_name.into(),
697            line,
698        });
699    }
700
701    /// Add a module search path fact
702    pub fn add_module_search_path(
703        &mut self,
704        workspace_root: &str,
705        language: &str,
706        kind: &str,
707        path: &str,
708    ) {
709        self.module_search_paths.push(ModuleSearchPathFact {
710            workspace_root: workspace_root.into(),
711            language: language.into(),
712            kind: kind.into(),
713            path: path.into(),
714        });
715    }
716
717    /// Add a CFG block fact
718    pub fn add_cfg_block(
719        &mut self,
720        file: &str,
721        func: &str,
722        func_line: u32,
723        block: u32,
724        kind: &str,
725    ) {
726        self.cfg_blocks.push(CfgBlockFact {
727            file: file.into(),
728            func: func.into(),
729            func_line,
730            block,
731            kind: kind.into(),
732        });
733    }
734
735    /// Add a CFG edge fact
736    #[allow(clippy::too_many_arguments)]
737    pub fn add_cfg_edge(
738        &mut self,
739        file: &str,
740        func: &str,
741        func_line: u32,
742        from: u32,
743        to: u32,
744        kind: &str,
745        exception_type: &str,
746    ) {
747        self.cfg_edges.push(CfgEdgeFact {
748            file: file.into(),
749            func: func.into(),
750            func_line,
751            from,
752            to,
753            kind: kind.into(),
754            exception_type: exception_type.into(),
755        });
756    }
757
758    /// Add a CFG variable definition fact
759    pub fn add_cfg_def(&mut self, file: &str, func: &str, func_line: u32, block: u32, name: &str) {
760        self.cfg_defs.push(CfgDefFact {
761            file: file.into(),
762            func: func.into(),
763            func_line,
764            block,
765            name: name.into(),
766        });
767    }
768
769    /// Add a CFG variable use fact
770    pub fn add_cfg_use(&mut self, file: &str, func: &str, func_line: u32, block: u32, name: &str) {
771        self.cfg_uses.push(CfgUseFact {
772            file: file.into(),
773            func: func.into(),
774            func_line,
775            block,
776            name: name.into(),
777        });
778    }
779
780    /// Add a CFG side-effect fact
781    #[allow(clippy::too_many_arguments)]
782    pub fn add_cfg_effect(
783        &mut self,
784        file: &str,
785        func: &str,
786        func_line: u32,
787        block: u32,
788        kind: &str,
789        line: u32,
790        label: &str,
791    ) {
792        self.cfg_effects.push(CfgEffectFact {
793            file: file.into(),
794            func: func.into(),
795            func_line,
796            block,
797            kind: kind.into(),
798            line,
799            label: label.into(),
800        });
801    }
802}