Documentation
¶
Overview ¶
Package index defines the indexing pipeline: walk -> hash -> parse (per language) -> resolve cross-language bindings -> populate the graph.
Parser backends are pluggable behind LanguageParser. The target picture is cgo-free: Go is parsed with the stdlib go/parser (never tree-sitter), Plan 9 asm with a custom scanner (internal/plan9, shared with internal/resolve), and C/C++/CUDA start on cgo tree-sitter bindings in M1/M2 with a planned migration to tree-sitter grammars compiled to WASM executed via wazero (pure Go). See docs/architecture.md §2 and docs/roadmap.md.
Index ¶
- func LangOf(path string) graph.Lang
- func MatchIgnore(globs []string, rel string) bool
- func ResolveTypedCalls(ctx context.Context, g graph.Graph, root string, s store.Store) (int, error)
- type AsmParser
- type CacheVersioner
- type CudaParser
- type GoParser
- type Indexer
- type LanguageParser
- type ParseResult
- type Stats
- type TypedPassError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func MatchIgnore ¶
MatchIgnore reports whether a repo-relative path matches any of the config ignore globs ("**/" prefixes match any directory depth, including zero).
Types ¶
type AsmParser ¶
type AsmParser struct{}
AsmParser is the Plan 9 assembly LanguageParser. It wraps plan9.Scan to extract TEXT (KAsmProc) and GLOBL (KVar) symbol definitions; it emits no same-language edges (the Go<->asm binding is resolve.Plan9AsmResolver's job, edge kind graph.EAsm).
func (AsmParser) CacheVersion ¶
CacheVersion implements index.CacheVersioner.
func (AsmParser) Extensions ¶
Extensions are the file suffixes this parser claims.
func (AsmParser) Parse ¶
func (AsmParser) Parse(path string, src []byte) (ParseResult, error)
Parse turns one Plan 9 asm file into symbol nodes. Symbols are qualified by their containing directory's base name (the Go package convention: a mat_amd64.s file living beside package mat mints "mat.mulVec"), matching the qualified names GoParser mints for the corresponding Go package so the resolver can pair them by ID alone.
type CacheVersioner ¶
type CacheVersioner interface {
CacheVersion() string
}
CacheVersioner is the optional half of LanguageParser that keeps the parse cache honest across parser upgrades. Determinism only says identical bytes yield identical results *for one parser build*; the cached blob outlives the parser that produced it, so a parser whose output changed must also change this string or the cache serves the old graph forever (B-0007). A parser that does not implement it keys exactly as before.
type CudaParser ¶
type CudaParser struct{}
CudaParser is a line-oriented regex scanner for .cu/.cuh files (no cgo tree-sitter dependency yet — see docs/architecture.md §2 for the planned wazero-hosted grammar migration). It extracts two symbol shapes:
- `__global__` kernel definitions -> KKernel nodes with IDs "cu:<name>".
- `extern "C"` host wrapper functions -> KFunc nodes with IDs "c:<name>". The wrapper IS the C symbol that resolve.CgoResolver's Go -> c:<name> edge already targets as a stub; this parser upgrades that stub to a real, located node.
No edges are emitted here — the kernel launch (ELaunch) is a cross-file, cross-symbol relation and belongs to resolve.CudaResolver. Parsing is deterministic: identical bytes yield identical results (SPX-GRA-001).
func (CudaParser) CacheVersion ¶
func (CudaParser) CacheVersion() string
CacheVersion implements CacheVersioner.
func (CudaParser) Extensions ¶
func (CudaParser) Extensions() []string
Extensions are the file suffixes this parser claims.
func (CudaParser) Parse ¶
func (CudaParser) Parse(path string, src []byte) (ParseResult, error)
Parse scans one .cu/.cuh file line by line for kernel and extern "C" wrapper definitions.
type GoParser ¶
type GoParser struct{}
GoParser is the stdlib-backed Go language parser (never tree-sitter for Go: go/parser has strictly better fidelity and keeps the target binary cgo-free). It extracts top-level func/method/type/var symbols with 1-based Line/EndLine spans (the drift anchor needs EndLine) and best-effort same/cross-package call edges resolved by naming convention. The `import "C"` boundary is left to resolve.CgoResolver — a C.<ident> selector is deliberately NOT emitted as a Go call edge here.
func (GoParser) CacheVersion ¶
CacheVersion implements CacheVersioner.
func (GoParser) Extensions ¶
Extensions are the file suffixes this parser claims.
func (GoParser) Parse ¶
func (GoParser) Parse(path string, src []byte) (ParseResult, error)
Parse turns one Go file into symbol nodes and candidate call edges. Call edges carry the intended callee ID by convention (go:<pkg>.<fn> for an unqualified call, go:<x>.<sel> for a qualified X.Sel call); the indexer prunes any whose destination node does not exist after the whole tree is indexed. Parsing is deterministic: identical bytes yield identical results (SPX-GRA-001).
type Indexer ¶
type Indexer interface {
// IndexAll walks root, parses every recognized file (cache-accelerated)
// and runs all binding resolvers.
IndexAll(ctx context.Context, root string) (Stats, error)
// IndexPaths re-indexes only the given files and re-runs the resolvers
// whose languages intersect the changed set.
IndexPaths(ctx context.Context, paths []string) (Stats, error)
}
Indexer builds and refreshes the graph.
func New ¶
func New(g graph.Graph, s store.Store, parsers []LanguageParser, resolvers []resolve.BindingResolver, ignore ...string) Indexer
New wires the pipeline with a set of parsers and binding resolvers. The trailing ignore globs are config.yaml-style patterns (see MatchIgnore) that IndexAll consults in addition to the built-in directory skips (see workspace.DefaultSkipName / workspace.IsNestedGitBoundary); omitting them is backward compatible with existing callers.
type LanguageParser ¶
type LanguageParser interface {
Lang() graph.Lang
Extensions() []string // e.g. [".cu", ".cuh"]
Parse(path string, src []byte) (ParseResult, error)
}
LanguageParser turns one source file into nodes and same-language edges. Implementations must be deterministic: identical bytes yield identical results (SPX-GRA-001 depends on it).
type ParseResult ¶
ParseResult is the per-file output of a LanguageParser, cacheable by content hash.
type Stats ¶
type Stats struct {
Files, Nodes, Edges, Skipped int
}
Stats summarizes one indexing run.
type TypedPassError ¶ added in v0.2.0
type TypedPassError struct {
// Packages is the number of packages that individually failed to load or
// type-check. It is 0 when the whole-module load itself failed before any
// per-package breakdown existed (e.g. packages.Load's own driver command
// erroring) — there is no count to report in that case, only the cause.
Packages int
// contains filtered or unexported fields
}
ResolveTypedCalls is the go/types-based call-edge upgrade pass (M3 slice 1, docs/design-go-types-calls.md). It loads and type-checks the Go module rooted at root with golang.org/x/tools/go/packages, resolves every *ast.CallExpr's callee through *types.Info (which flattens chained and embedded selectors — s.cd.Sweep() — to the concrete method regardless of how many field hops sit in between), and adds any ECall edge the fast syntactic pass in GoParser.callEdges missed or mis-minted.
This is a two-tier upgrade, not a replacement (design doc §4): it never removes or duplicates edges the syntactic pass already produced. It is intended to run once per IndexAll, not per file change — packages.Load type-checks the whole module and is 10-100x slower than a bare go/parser.ParseFile per file.
An edge is added only when both endpoints already exist as nodes in g (mirroring the syntactic pass's dangling-edge pruning), the edge is not a self-loop, and it is not already present — either newly discovered earlier in this same pass, or already an ECall edge out of the same source node from a prior run (memGraph.Upsert appends rather than replaces, so callers running ResolveTypedCalls repeatedly must not re-add the same edge).
ID minting mirrors GoParser/ids exactly: go:<pkg>.<recv>.<name> (recv omitted for a plain function), pkg = fn.Pkg().Name() — the declaring package's source name, not its import path — so resolved edges land on the very same node IDs the syntactic pass and every other tool already key on.
A call resolving into the cgo pseudo-package (import "C") is skipped: that boundary is owned by resolve.CgoResolver. In practice cmd/cgo rewrites C.foo(...) call sites to a wrapped identifier before go/types ever sees them, so this rarely fires; it is kept as a defensive, explicit guard rather than relying on that incidentally.
Interface-typed call sites resolve to the interface method node itself (not to every implementer) — whole-program Implements-closure resolution is out of scope for this slice (design doc §3, "Interface dispatch").
packages.Load failures (a broken module, a bad build) and any non-empty Package.Errors are returned as a *TypedPassError; the caller (the BuildGraph orchestrator, internal/mcpserver) treats a failed upgrade pass as non-fatal to the syntactic index it already has — but see TypedPassError's doc comment for why the failure must not stop there (issue 28).
s is the same content-hash store the syntactic pass uses to skip re-parsing unchanged files (design doc §Performance, mitigation (c)); a nil s reproduces the pre-cache behavior exactly (always packages.Load, never cached). When s is non-nil, ResolveTypedCalls first computes a module hash key — sha256 over go.mod's contents plus every tracked .go file's (relpath, sha256(contents)) pair, walked the same way IndexAll walks the tree (same ignoreDirs) — and looks it up under the sentinel path typedCallsCacheKey. A hit decodes the cached edge list and applies it through the exact same node-existence/self-loop/dedupe gate a fresh packages.Load result goes through, so packages.Load is never called on a cache hit. A miss runs the full type-checking pass as before and, on success, best-effort Puts the resulting edge list back under that key — a cache-write failure must never fail the pass.
TypedPassError is what a failed pass returns. Before issue 28 this was a plain fmt.Errorf: BuildGraph logged it and moved on, so a resident server driven over stdio/HTTP (or the `call` subcommand) never learned the graph it just got "ok" back from has zero typed call edges — exactly the capability the server's own instructions cite as the reason to prefer `get depth` over a shell search (cross-language impact radius, what-calls- X). TypedPassError implements error so ResolveTypedCalls' signature is unchanged for existing callers, but carries the fields a caller needs to turn "the pass errored" into a real record instead of re-parsing Error()'s text: Packages, so state/check can name how much of the module is affected without guessing from a string.
func (*TypedPassError) Error ¶ added in v0.2.0
func (e *TypedPassError) Error() string
func (*TypedPassError) Unwrap ¶ added in v0.2.0
func (e *TypedPassError) Unwrap() error