sinter_core/reference.rs
1use serde::{Deserialize, Serialize};
2
3use crate::edge::Relation;
4use crate::node::{NodeId, Span};
5
6/// A use of a name that extraction saw but has not bound to a definition.
7///
8/// Unresolved is a first-class outcome: references are stored and countable,
9/// never guessed into edges. Phase 3 resolution consumes these and promotes
10/// each to an evidence-backed edge — or leaves it here, counted.
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
12pub struct Reference {
13 /// Repo-relative file the reference appears in.
14 pub file: String,
15 /// Referenced name as written (call target, import path, ...).
16 pub name: String,
17 /// Full path text at the reference site when the name was qualified
18 /// (`fmt.Println`, `util::double`) — import-evidence input.
19 pub path: Option<String>,
20 /// Relation an eventual binding would carry.
21 pub relation: Relation,
22 pub span: Span,
23 /// Innermost definition containing the reference site, if any —
24 /// the `src` of the edge resolution would create.
25 pub enclosing: Option<NodeId>,
26 /// Local rebinding of the imported/referenced name: `as` clauses
27 /// (`use x as y`, `import a as b`), Go's dot import (`.`), and glob
28 /// imports (`*`). The `name` field always keeps the original path.
29 pub alias: Option<String>,
30}
31
32/// A local binding (parameter, let/const, loop variable, catch variable)
33/// that shadows outer names. Not a symbol — recorded so resolution never
34/// binds a shadowed reference to the outer definition it no longer means.
35#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
36pub struct LocalBinding {
37 pub file: String,
38 pub name: String,
39 /// Where the binding is introduced.
40 pub span: Span,
41 /// End of the innermost definition containing it — the binding shadows
42 /// references from `span.start` to here.
43 pub scope_end: u64,
44 /// Declared/constructed type when the language chose to expose it
45 /// (`c *Counter`, `c := Counter{}`): local type evidence for method
46 /// binding. None = shadow-only binding.
47 pub type_name: Option<String>,
48}
49
50/// A declared field and its written type. Resolution uses this fact for
51/// receiver chains such as `self.harness.check()` without pretending that
52/// the field access itself is a symbol definition.
53#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
54pub struct FieldBinding {
55 /// Type/class/struct that declares the field.
56 pub owner: NodeId,
57 pub name: String,
58 /// Type exactly as written (`Arc<dyn Harness>`, `&Dog`, ...).
59 pub type_name: String,
60}
61
62/// Why a reference remained outside the graph. These are deliberately
63/// outcome descriptions, not guesses at a compiler diagnostic.
64#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
65#[serde(rename_all = "snake_case")]
66pub enum UnresolvedReason {
67 /// Source evidence pointed into the corpus, but did not identify one
68 /// target (missing member, ambiguity, or incomplete type facts).
69 SyntaxAnchoredMiss,
70 /// Source extraction had no corpus anchor and no compiler index was
71 /// available to distinguish an external/builtin from a missed edge.
72 SyntaxOnly,
73 /// A compiler index was present but supplied no in-corpus target.
74 CompilerUnresolved,
75 /// The path's module is in the corpus but neither defines nor imports
76 /// the name: a dangling internal reference (renamed or deleted target).
77 MissingInternalTarget,
78}
79
80impl UnresolvedReason {
81 pub const fn as_str(self) -> &'static str {
82 match self {
83 Self::SyntaxAnchoredMiss => "syntax_anchored_miss",
84 Self::SyntaxOnly => "syntax_only",
85 Self::CompilerUnresolved => "compiler_unresolved",
86 Self::MissingInternalTarget => "missing_internal_target",
87 }
88 }
89}
90
91/// A resolution limitation the resolver recognised while failing to bind a
92/// reference. Recorded by the resolver, which is the only place that knows
93/// what it tried, so consumers never have to infer a gap from name shape.
94/// The variants are observations, not verdicts: a consumer decides what
95/// each one means for its own question.
96#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
97#[serde(rename_all = "snake_case")]
98pub enum ResolverGap {
99 /// An `as`-aliased import in the reference's own file binds this name.
100 /// Resolution did not follow the alias to a definition.
101 AliasedImport,
102 /// The written path anchored to a corpus module whose files re-export
103 /// through glob/star imports (a barrel). The name may well be reachable
104 /// through that re-export; the chain walk did not get there.
105 Reexport,
106 /// The written path anchored to exactly this corpus file and the name
107 /// was not found in it. Whether that is a sinter gap or a dangling
108 /// reference depends on how completely the file was indexed — which the
109 /// resolver does not know and its consumers do.
110 AnchoredFile(String),
111}
112
113/// Persisted unresolved outcome: the reference plus the coverage context
114/// that produced the miss.
115#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
116pub struct UnresolvedReference {
117 pub reference: Reference,
118 pub reason: UnresolvedReason,
119 /// What resolution ran into, when it recognised its own limitation.
120 /// Appended field: postcard is positional, so graphs written before it
121 /// existed are rebuilt by the schema bump that introduced it.
122 pub gap: Option<ResolverGap>,
123}
124
125/// A type embedding another (Go embedded struct field): member lookup on
126/// the owner falls through to the embedded type. Lookup fact, not an edge.
127#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
128pub struct Embed {
129 pub owner: NodeId,
130 pub type_name: String,
131}
132
133/// An impl block naming the trait it implements (`impl Runner for Widget`).
134/// Pairing fact for dynamic-dispatch edges: methods defined inside `span`
135/// implement the same-named methods of the trait `trait_name` resolves to.
136#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
137pub struct TraitImpl {
138 pub file: String,
139 /// Trait name as written (leaf segment for qualified paths).
140 pub trait_name: String,
141 /// Span of the whole impl block.
142 pub span: Span,
143}