Documentation
¶
Overview ¶
Package resolve turns unresolved reference edges (dst NULL) into graph edges, following PLAN "Name resolution":
T1 import-qualified 100 qualifier/alias/named import -> unique def in target T2 lexical 90 same file (inner scope first), then same module T3 receiver-typed 80 x.M() with x's type known locally + ancestor walk T4 unique global 50 single candidate in the language T5 ambiguous 30/n 2..5 candidates, one edge each; more -> dst NULL
The first matching tier wins. Names with more than Options.CommonNameLimit definitions or references skip T4/T5.
Work is driven by the pending_names queue: for every pending name, all edges carrying that name are re-resolved and their rows REPLACED (a T5 fan-out collapses back to one row when a candidate disappears), so an incremental run converges to the same graph as a full build. Processed names leave the queue in the same transaction as their edge updates, which makes the pass crash-safe.
Language specifics plug in through LangResolver (resolve/<lang>.go, registered from init()); module identity and import targets come from the parse layer's model.LanguageSpec.
Every run that changed something ends with the Go implements pass (goimpl.go), which derives the implicit interface satisfaction of Go types from the resolved graph.
Index ¶
- Constants
- func PendingDependentsKey(id int64) string
- func PendingFileKey(id int64) string
- func PendingRemovedKey(id int64) string
- func Register(lang model.Lang, r LangResolver)
- type Closer
- type DeclarationResolver
- type Def
- type File
- type Generic
- func (Generic) ImportBindings(imp Import) []string
- func (Generic) IsMember(s *Session, d *Def) bool
- func (Generic) MembersInScope() bool
- func (Generic) OwnerName(*Session, *Def) string
- func (Generic) Receiver(s *Session, site *Site) Receiver
- func (Generic) ResolveType(s *Session, from *File, qual, name string) TypeResult
- type Import
- type InheritRef
- type LangResolver
- type Options
- type ReExporter
- type Receiver
- type RecvKind
- type Resolver
- type ScopedRepoContext
- type Session
- func (s *Session) Ancestors(t *Def) []*Def
- func (s *Session) BatchState(lang model.Lang) any
- func (s *Session) Binding(f *File, root string) (model.ImportTarget, bool)
- func (s *Session) Children(d *Def) []*Def
- func (s *Session) Common(name string) bool
- func (s *Session) Context() context.Context
- func (s *Session) Def(id int64) *Def
- func (s *Session) DefaultResolveType(from *File, qual, name string) TypeResult
- func (s *Session) DefsNamed(name string) []*Def
- func (s *Session) EnclosingType(d *Def) *Def
- func (s *Session) File(id int64) *File
- func (s *Session) ImportTarget(f *File, spec string) model.ImportTarget
- func (s *Session) Imports(f *File) []Import
- func (s *Session) InTarget(d *Def, t model.ImportTarget) bool
- func (s *Session) Inherits(d *Def) []InheritRef
- func (s *Session) Members(t *Def, name string) []*Def
- func (s *Session) Module(f *File) string
- func (s *Session) NamedImport(f *File, name string) (model.ImportTarget, bool)
- func (s *Session) Parent(d *Def) *Def
- func (s *Session) RepoContext() model.RepoContext
- func (s *Session) SameModule(a, b *File) bool
- func (s *Session) SetBatchState(lang model.Lang, v any)
- func (s *Session) SetState(lang model.Lang, v any)
- func (s *Session) Source(f *File) []byte
- func (s *Session) State(lang model.Lang) any
- func (s *Session) TextReceiver(site *Site) Receiver
- type Site
- type Stats
- type Tier
- type TypeResult
Constants ¶
const ( ConfImport uint8 = 100 // T1 ConfLexical uint8 = 90 // T2 ConfReceiver uint8 = 80 // T3 ConfUnique uint8 = 50 // T4 ConfAmbig uint8 = 30 // T5, divided by the number of candidates )
Confidence values of the tiers.
const ( DefaultBatchNames = 256 DefaultBatchFiles = 128 DefaultCommonNameLimit = 2000 DefaultMaxDependentFiles = 512 )
Defaults for Options.
const ( ConfImplicit uint8 = 70 // method sets match ConfAsserted uint8 = 85 // a compile-time assertion names the pair )
Confidence of derived implements rows.
const DispatchQual = "~dispatch"
DispatchQual marks method-level implements rows (see above).
const MaxAmbiguous = 5
MaxAmbiguous is the largest candidate set that still produces T5 edges.
const MaxImplementers = 5000
MaxImplementers bounds the implementers linked to one interface; above it the interface is skipped (only asserted pairs are linked).
const PendingDependentsPrefix = "pending_dependents:"
PendingDependentsPrefix starts the meta keys "pending_dependents:<names.id>" of names whose definitions kept their identity but whose types may read differently (a body-only edit of a language whose resolver reads other files' definition text, or a file whose import targets changed): the references of the name itself cannot resolve differently, but receiver typing through its definitions can, so only the name's dependents (see dependentGroups) are re-resolved.
const PendingFilePrefix = "pending_file:"
PendingFilePrefix starts the meta keys of the per-file resolution queue: "pending_file:<files.id>". A queued file has had its own references rewritten without any definition visible to other files changing (the indexer kept its definitions in place), so only that file's edges must be re-resolved: queueing their names instead would re-resolve every reference of those names in the repository. Like pending_names, the keys commit with the facts and are removed in the transaction that resolves the file. internal/index writes them with the same prefix.
const PendingRemovedPrefix = "pending_removed:"
PendingRemovedPrefix starts the meta keys "pending_removed:<names.id>" of names that only lost definitions. A reference resolved with high confidence (one target, T1-T3) to a surviving definition keeps its target when other candidates disappear: every tier filters the candidates and keeps the best, so a candidate outside the result cannot change it by leaving. Only the name's unresolved and low-confidence references (references into the removed definitions were unlinked with them) and its dependents are re-resolved.
Variables ¶
This section is empty.
Functions ¶
func PendingDependentsKey ¶
PendingDependentsKey returns the meta key queueing the dependents of name id.
func PendingFileKey ¶
PendingFileKey returns the meta key queueing file id.
func PendingRemovedKey ¶
PendingRemovedKey returns the meta key queueing name id as removed.
func Register ¶
func Register(lang model.Lang, r LangResolver)
Register installs the resolver for lang. It panics on an invalid language or a duplicate registration (programming errors caught at init time).
Types ¶
type Closer ¶
type Closer interface{ Close() }
Closer is implemented by per-language state stored with SetState that holds native resources.
type DeclarationResolver ¶
DeclarationResolver is implemented by resolvers of languages that keep declarations apart from definitions (C, C++). Definitions returns the definitions a declaration (model.FlagDeclaration) declares, which are the targets of its "declares" edge.
The indexer writes one unresolved declares edge per declaration; the framework resolves it through this interface (T2, or T5 when several definitions match).
type Def ¶
type Def struct {
ID int64
FileID int64
ParentID int64 // 0 = top level
NameID int64
Name string
Kind model.NodeKind
QName string
Sig string
Flags model.DefFlags
StartLine uint32
StartByte uint32
EndByte uint32
}
Def is a definition (nodes row).
type File ¶
type File struct {
ID int64
Path string
Lang model.Lang
Size int64
// contains filtered or unexported fields
}
File is a files row as seen by the resolver.
type Generic ¶
type Generic struct{}
Generic is the language-neutral resolver used for languages without a registered LangResolver. Language resolvers embed it and override hooks.
func (Generic) ImportBindings ¶
ImportBindings binds the alias, or else the last segment of the spec ("./utils/format" -> "format", "shop.models" -> "models").
func (Generic) MembersInScope ¶
MembersInScope is false: members need an explicit receiver.
func (Generic) Receiver ¶
Receiver recognizes member access by scanning the source text before the name for ".", "->" or "?.", and types the receivers this/self/$this (enclosing type) and super/parent (its ancestors). Other receivers are untyped.
func (Generic) ResolveType ¶
func (Generic) ResolveType(s *Session, from *File, qual, name string) TypeResult
ResolveType applies DefaultResolveType.
type Import ¶
type Import struct {
Spec string
Alias string
Names []string // imported member names; "*" marks a wildcard import
Line uint32
}
Import is an imports row of one file.
type InheritRef ¶
InheritRef is an inheritance edge (extends/implements/embeds) declared by a type, identified by its target name rather than its resolved dst so that resolution never depends on processing order.
type LangResolver ¶
type LangResolver interface {
// ImportBindings returns the local identifiers an import makes
// available as qualifier roots in the importing file (e.g. the package
// name of a Go import, an alias, a Python module name). Members listed
// in Import.Names are handled by the framework and need not be
// returned.
ImportBindings(imp Import) []string
// IsMember reports whether d belongs to a type (field, method, ...),
// so it is only reachable through a receiver or qualifier and never as
// a bare name from outside its type.
IsMember(s *Session, d *Def) bool
// OwnerName returns the name of the type a member belongs to when the
// member is not lexically nested in it (Go methods declared at top
// level), or "" otherwise. Nested members are matched by ParentID.
OwnerName(s *Session, d *Def) string
// MembersInScope reports whether members of the enclosing types are
// visible as bare names (implicit this in Java, C#, C++).
MembersInScope() bool
// ResolveType resolves a type name written in file from (with its
// textual qualifier, "" if none) to type definitions.
ResolveType(s *Session, from *File, qual, name string) TypeResult
// Receiver classifies the reference at site: whether it is a member
// access and, if so, what the receiver's type is (tier T3).
Receiver(s *Session, site *Site) Receiver
}
LangResolver customizes name resolution for one language. Implementations live in resolve/<lang>.go and self-register from init() with Register. Languages without one use Generic. Implementations usually embed Generic and override only what differs; the framework always calls hooks through the interface, so overrides take effect everywhere.
Hooks must be deterministic functions of the Session state: incremental resolution is only equivalent to a full build if every edge resolves the same way regardless of processing order.
func For ¶
func For(lang model.Lang) LangResolver
For returns the resolver registered for lang, or Generic.
type Options ¶
type Options struct {
// BatchNames is how many pending names are resolved per transaction
// (default 256).
BatchNames int
// CommonNameLimit: names with more definitions or references than this
// skip T4/T5 (default 2000).
CommonNameLimit int
// Source reads a repo-relative file for receiver typing (T3). Default:
// RepoContext.ReadFile. Content whose length differs from files.size is
// treated as stale and ignored.
Source func(path string) ([]byte, error)
// NoDependents disables re-resolving receiver-typed references in files
// that use a pending name (see ResolvePending). Full builds disable it
// automatically since every name is pending anyway.
NoDependents bool
// MaxDependentFiles bounds the files re-checked as dependents per batch
// (default 512); above it dependents are skipped for that batch.
MaxDependentFiles int
// BatchFiles is how many files ResolveAll resolves per transaction
// (default 128).
BatchFiles int
}
Options configure a resolution run. The zero value is valid.
type ReExporter ¶
type ReExporter interface {
ReExported(s *Session, t model.ImportTarget, name string, keep func(*Def) bool) []*Def
}
ReExporter may be implemented by a LangResolver whose modules re-export definitions of other modules (TypeScript/JavaScript barrel files: export * from, export { X } from). ReExported returns the definitions named name that import target t provides through re-exports and that keep accepts. The framework consults it for named imports and import bindings when no definition of t itself matches.
type RecvKind ¶
type RecvKind uint8
RecvKind classifies the receiver of a reference.
const ( // RecvNone: a bare (or qualifier-only) name, not a member access. RecvNone RecvKind = iota // RecvUnknownShape: the source is unavailable, so it is unknown whether // the reference is a member access. No member filtering is applied. RecvUnknownShape // RecvUntyped: a member access whose receiver type is unknown; // candidates are restricted to members (T4/T5). RecvUntyped // RecvExternal: the receiver type lives outside the repository (stdlib, // third party, builtin); the reference stays unresolved. RecvExternal // RecvTyped: Receiver.Types holds the receiver's type definitions. RecvTyped )
Receiver kinds.
type Resolver ¶
Resolver adapts the package functions to the indexer's Resolver interface (ResolvePending(ctx, st, rc) error).
func (*Resolver) CommonNameLimit ¶
CommonNameLimit returns the effective Options.CommonNameLimit, so the indexer can tell when a change of a name's reference count flips its common-name status (index.IncrementalHints).
func (*Resolver) ReadsForeignSource ¶
ReadsForeignSource reports whether resolving references of lang may read the source text of definitions in other files (index.IncrementalHints). Unknown languages answer true (conservative).
func (*Resolver) ResolveAll ¶
ResolveAll runs a full resolution with r's options.
func (*Resolver) ResolvePending ¶
ResolvePending implements the indexer's Resolver interface.
type ScopedRepoContext ¶
type ScopedRepoContext interface {
For(path string) model.RepoContext
}
ScopedRepoContext is implemented by a model.RepoContext that attributes the answers it gives to a file (internal/index.RepoCtx): For(path) returns the view to use while resolving on behalf of that file, so a later change of an answer only invalidates the files that relied on it.
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session holds the caches of one resolution run. Definitions, files and imports do not change while resolving (only edges do), so they are cached for the whole run; parsed trees are dropped after each batch, sources once they exceed maxSourceBytes. A Session is not safe for concurrent use.
func (*Session) Ancestors ¶
Ancestors resolves the direct supertypes of t from its inheritance references, in declaration order.
func (*Session) BatchState ¶
BatchState returns language state stored with SetBatchState.
func (*Session) Binding ¶
Binding finds the import of f that binds the local identifier root (through ImportBindings or an alias) and returns its target.
func (*Session) Common ¶
Common reports whether name is too common for global guessing (T4/T5): more than Options.CommonNameLimit definitions or references.
func (*Session) DefaultResolveType ¶
func (s *Session) DefaultResolveType(from *File, qual, name string) TypeResult
DefaultResolveType is the generic type-name resolution:
- qualified: an import-bound root resolves inside the import target (T1, External if the target is outside the repo); a fully qualified module path matches by module (T1); otherwise the qualifier names an enclosing type (Outer.Inner) and the type is chosen by locality.
- unqualified: named import (T1), lexical (same file, then module; T2), wildcard import (T1), then unique global type (T4) or T5.
func (*Session) EnclosingType ¶
EnclosingType returns the innermost type definition containing d (d itself if it is a type), or nil.
func (*Session) ImportTarget ¶
func (s *Session) ImportTarget(f *File, spec string) model.ImportTarget
ImportTarget resolves an import spec written in f through the language spec's ResolveImport (cached).
func (*Session) InTarget ¶
func (s *Session) InTarget(d *Def, t model.ImportTarget) bool
InTarget reports whether d is defined inside an import target.
func (*Session) Inherits ¶
func (s *Session) Inherits(d *Def) []InheritRef
Inherits returns the inheritance references declared by type d, sorted by position.
func (*Session) Members ¶
Members returns the members of type t called name: direct members first, otherwise the members of the nearest ancestors (extends/implements/embeds, breadth first). Members are nested definitions or, for languages that declare them outside the type (Go methods), definitions whose OwnerName is t's name in t's module.
func (*Session) Module ¶
Module returns the module identity of f (Go import path, Java package, file path for TS/JS, ...): the indexer's packages row when set, else the language spec's ModuleOf. "" means unknown.
func (*Session) NamedImport ¶
NamedImport finds an import of f that brings the member name into scope by name (Python from-import, JS named import, Java single-type import).
func (*Session) RepoContext ¶
func (s *Session) RepoContext() model.RepoContext
RepoContext returns the repository view used for module identity and import resolution.
func (*Session) SameModule ¶
SameModule reports whether two files belong to the same known module.
func (*Session) SetBatchState ¶
SetBatchState stores language state (e.g. parsed trees) until the end of the current batch; a Closer is closed then.
func (*Session) SetState ¶
SetState stores language state for the whole run; a Closer is closed at the end of the run.
func (*Session) Source ¶
Source returns the current content of f, or nil when it cannot be read or no longer matches the indexed size (the file changed after indexing).
func (*Session) TextReceiver ¶
TextReceiver implements Generic.Receiver; language resolvers may reuse it.
type Site ¶
type Site struct {
File *File
Src *Def // the enclosing definition the edge starts from
Kind model.EdgeKind
Name string
Qual string
Line uint32 // 1-based line of the referenced name
Col uint32 // 0-based byte column of the referenced name
}
Site is one reference being resolved: an edge group sharing (file, src, kind, name, qual, line, col).
type Stats ¶
type Stats struct {
Names int // pending names drained
Files int // queued files whose own references were re-resolved
Refs int // references (edge groups) resolved
Dependents int // of Refs, re-resolved as dependents of a pending name
// DependentsSkipped counts batches whose dependents exceeded
// Options.MaxDependentFiles and were not re-resolved.
DependentsSkipped int
Batches int // committed transactions
Unresolved int // references left with dst NULL
Edges int // edge rows written (updates + inserts + deletes)
ByTier [tierCount]int
// Implements counts derived Go implements rows written (see goimpl.go);
// ImplementsSkipped the interfaces left unlinked by the bounds there.
Implements int
ImplementsSkipped int
}
Stats summarizes a run.
func ResolveAll ¶
func ResolveAll(ctx context.Context, st *store.Store, rc model.RepoContext, opts Options) (Stats, error)
ResolveAll re-resolves every reference of the graph and empties the pending_names queue; use it after a full (bulk) build. It walks the edges file by file (Options.BatchFiles per transaction), so every source file is read and parsed about once, instead of once per name batch. The queue is filled first and cleared only when the walk is complete: an interrupted run leaves every name queued for ResolvePending.
func ResolvePending ¶
func ResolvePending(ctx context.Context, st *store.Store, rc model.RepoContext, opts Options) (Stats, error)
ResolvePending drains the per-file queue (see PendingFilePrefix), the removed-names and dependents queues (PendingRemovedPrefix, PendingDependentsPrefix), then pending_names, in batched transactions. For every queued file its own edges are re-resolved; for every pending name all edges carrying it are re-resolved, replacing their rows. Unless Options.NoDependents is set, receiver-shaped references in files that mention a pending name are re-resolved too, since their receiver typing (T3) may depend on a definition of that name (see dependentGroups for the bounds).
The caller must hold the store's write lock and must not have an open Writer. If a bulk build was left unfinished (meta bulk=1), FinishBulk runs first. On error or cancellation the current batch is rolled back and the remaining names stay queued.
type Tier ¶
type Tier uint8
Tier identifies which rule resolved a reference.
type TypeResult ¶
type TypeResult struct {
// Defs are the matching type definitions; empty if none.
Defs []*Def
// Conf is the confidence of the match (tier conf; 30/n if ambiguous).
Conf uint8
// External is set when the name provably refers to something outside
// the repository (or to a bound import without such a definition), so
// callers must not fall back to global guessing.
External bool
}
TypeResult is the outcome of resolving a type name.
func (TypeResult) Found ¶
func (t TypeResult) Found() bool
Found reports whether at least one definition matched.