Help us shape the future of Go! Take the 2026 Go Developer Survey

resolve

package
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 18, 2026 License: MIT Imports: 26 Imported by: 0

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

View Source
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.

View Source
const (
	DefaultBatchNames        = 256
	DefaultBatchFiles        = 128
	DefaultCommonNameLimit   = 2000
	DefaultMaxDependentFiles = 512
)

Defaults for Options.

View Source
const (
	ConfImplicit uint8 = 70 // method sets match
	ConfAsserted uint8 = 85 // a compile-time assertion names the pair
)

Confidence of derived implements rows.

View Source
const DispatchQual = "~dispatch"

DispatchQual marks method-level implements rows (see above).

View Source
const MaxAmbiguous = 5

MaxAmbiguous is the largest candidate set that still produces T5 edges.

View Source
const MaxImplementers = 5000

MaxImplementers bounds the implementers linked to one interface; above it the interface is skipped (only asserted pairs are linked).

View Source
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.

View Source
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.

View Source
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

func PendingDependentsKey(id int64) string

PendingDependentsKey returns the meta key queueing the dependents of name id.

func PendingFileKey

func PendingFileKey(id int64) string

PendingFileKey returns the meta key queueing file id.

func PendingRemovedKey

func PendingRemovedKey(id int64) string

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

type DeclarationResolver interface {
	Definitions(s *Session, decl *Def) []*Def
}

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

func (Generic) ImportBindings(imp Import) []string

ImportBindings binds the alias, or else the last segment of the spec ("./utils/format" -> "format", "shop.models" -> "models").

func (Generic) IsMember

func (Generic) IsMember(s *Session, d *Def) bool

IsMember reports whether d is nested in a type.

func (Generic) MembersInScope

func (Generic) MembersInScope() bool

MembersInScope is false: members need an explicit receiver.

func (Generic) OwnerName

func (Generic) OwnerName(*Session, *Def) string

OwnerName is "" for the generic resolver: members are lexically nested.

func (Generic) Receiver

func (Generic) Receiver(s *Session, site *Site) 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.

func (Import) Wildcard

func (im Import) Wildcard() bool

Wildcard reports whether the import brings all members of its target into scope (Java a.b.*, C# using, C++ using namespace, Python star import).

type InheritRef

type InheritRef struct {
	Kind model.EdgeKind
	Name string
	Qual string
	Line uint32
	Col  uint32
}

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 Receiver

type Receiver struct {
	Kind  RecvKind
	Types []*Def
}

Receiver is the outcome of LangResolver.Receiver.

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

type Resolver struct {
	Options Options
	// Last holds the stats of the most recent run.
	Last Stats
}

Resolver adapts the package functions to the indexer's Resolver interface (ResolvePending(ctx, st, rc) error).

func (*Resolver) CommonNameLimit

func (r *Resolver) CommonNameLimit() int

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

func (r *Resolver) ReadsForeignSource(lang model.Lang) bool

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

func (r *Resolver) ResolveAll(ctx context.Context, st *store.Store, rc model.RepoContext) error

ResolveAll runs a full resolution with r's options.

func (*Resolver) ResolvePending

func (r *Resolver) ResolvePending(ctx context.Context, st *store.Store, rc model.RepoContext) error

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

func (s *Session) Ancestors(t *Def) []*Def

Ancestors resolves the direct supertypes of t from its inheritance references, in declaration order.

func (*Session) BatchState

func (s *Session) BatchState(lang model.Lang) any

BatchState returns language state stored with SetBatchState.

func (*Session) Binding

func (s *Session) Binding(f *File, root string) (model.ImportTarget, bool)

Binding finds the import of f that binds the local identifier root (through ImportBindings or an alias) and returns its target.

func (*Session) Children

func (s *Session) Children(d *Def) []*Def

Children returns the definitions lexically nested directly in d.

func (*Session) Common

func (s *Session) Common(name string) bool

Common reports whether name is too common for global guessing (T4/T5): more than Options.CommonNameLimit definitions or references.

func (*Session) Context

func (s *Session) Context() context.Context

Context returns the run's context.

func (*Session) Def

func (s *Session) Def(id int64) *Def

Def returns the definition id, or nil.

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) DefsNamed

func (s *Session) DefsNamed(name string) []*Def

DefsNamed returns every definition called name, in id order.

func (*Session) EnclosingType

func (s *Session) EnclosingType(d *Def) *Def

EnclosingType returns the innermost type definition containing d (d itself if it is a type), or nil.

func (*Session) File

func (s *Session) File(id int64) *File

File returns the files row id, 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) Imports

func (s *Session) Imports(f *File) []Import

Imports returns the imports of f in source order.

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

func (s *Session) Members(t *Def, name string) []*Def

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

func (s *Session) Module(f *File) string

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

func (s *Session) NamedImport(f *File, name string) (model.ImportTarget, bool)

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) Parent

func (s *Session) Parent(d *Def) *Def

Parent returns d's lexical parent, or nil.

func (*Session) RepoContext

func (s *Session) RepoContext() model.RepoContext

RepoContext returns the repository view used for module identity and import resolution.

func (*Session) SameModule

func (s *Session) SameModule(a, b *File) bool

SameModule reports whether two files belong to the same known module.

func (*Session) SetBatchState

func (s *Session) SetBatchState(lang model.Lang, v any)

SetBatchState stores language state (e.g. parsed trees) until the end of the current batch; a Closer is closed then.

func (*Session) SetState

func (s *Session) SetState(lang model.Lang, v any)

SetState stores language state for the whole run; a Closer is closed at the end of the run.

func (*Session) Source

func (s *Session) Source(f *File) []byte

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) State

func (s *Session) State(lang model.Lang) any

State returns language state stored with SetState for the whole run.

func (*Session) TextReceiver

func (s *Session) TextReceiver(site *Site) Receiver

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.

const (
	TierNone Tier = iota // unresolved
	Tier1Import
	Tier2Lexical
	Tier3Receiver
	Tier4Unique
	Tier5Ambiguous
)

Tiers.

func (Tier) String

func (t Tier) String() string

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL