Documentation
¶
Overview ¶
Package callgraph builds and queries call relationships between functions.
Index ¶
- Constants
- func EagerWarmRepos(ctx context.Context, dirs []string)
- func ExtractGoImplements(ctx context.Context, root string) []parser.TypeRelationship
- func InjectHookEdges(cg *CallGraph, hookRoutes []HookRoute)
- func InvalidateBuildCache()
- func ResolveSpeculative(ctx context.Context, client *oxcodes.Client, root, language string, ...)
- func SetL2(redisURL string)
- type BuildOpts
- type CallChainNode
- type CallEdge
- type CallGraph
- func BuildCallGraph(symbols []*parser.Symbol, calls []parser.CallSite) *CallGraph
- func BuildCallGraphWithOpts(symbols []*parser.Symbol, calls []parser.CallSite, opts BuildOpts) *CallGraph
- func BuildFromRepo(ctx context.Context, input TraceRepoInput) (*CallGraph, error)
- func ConvertToCallGraph(typedEdges []goanalysis.TypedEdge, tsSymbols []*parser.Symbol) *CallGraph
- func EnrichWithTypedResolution(ctx context.Context, root string, base *CallGraph, symbols []*parser.Symbol, ...) *CallGraph
- func MergeCallGraphs(tsGraph, typedGraph *CallGraph) *CallGraph
- type HookRoute
- type PipelineOpts
- type PipelineResult
- type SpeculativeCall
- type TemplateRef
- type TemplateUsage
- type TraceOpts
- type TraceRepoInput
- type TraceResult
Constants ¶
const ( BackendTreeSitter = "tree-sitter" BackendGoTypes = "tree-sitter+go/types" BackendSCIP = "tree-sitter+scip" )
Backend identifies which resolution pass(es) contributed edges to a CallGraph. Plain strings by design (see docs/adr, "cut tier/backend provenance stamping" — internal/tier is orphaned and neither AGE-graph fixture needs a richer vocabulary); named as consts here only to keep the literal that SETS CallGraph.Backend and the literal that later COMPARES against it from drifting apart. Exported (not package-private) because codegraph/index.go's buildAGECallGraph also stamps and compares against these exact values — a second unexported copy in that package would be the same literal-drift risk these consts exist to prevent, just moved one package over.
const ( // CrossLanguageFetchKind is the node Kind set on synthetic nodes injected // by the cross-language enrichment pass (HANDLES → FETCHES traversal). CrossLanguageFetchKind = "cross_language_fetch" )
Variables ¶
This section is empty.
Functions ¶
func EagerWarmRepos ¶
EagerWarmRepos enumerates immediate subdirectories of each path in dirs that contain a go.mod, then runs the GOCACHE prewarm for each in parallel (bounded by eagerWarmParallelism). Returns when all warmups have settled.
Intended to be invoked once at process startup from a goroutine so it does not block the MCP server bring-up. The caller is responsible for goroutine dispatch — this function blocks until completion to make tests deterministic.
func ExtractGoImplements ¶ added in v1.37.1
func ExtractGoImplements(ctx context.Context, root string) []parser.TypeRelationship
ExtractGoImplements computes structural interface-satisfaction relationships for a Go module via go/types and returns them as parser.TypeRelationship values of kind RelImplements, ready to flow through buildRelationshipEdges (code_graph) or be appended to cg.TypeRels (call_trace). One edge is produced per (concrete type T, interface I) where T or *T implements I.
Go-only and best-effort: returns nil (not an error) when the repo has no go.mod, when go/packages fails or times out, or when no satisfaction exists. Failures are non-fatal — they bump a counter and log, mirroring the graceful-degradation contract of the rest of the pipeline. The returned relationships' File field is the concrete type's ABSOLUTE declaration path, so buildRelationshipEdges keys the IMPLEMENTS edge's subject endpoint onto the same Symbol vertex (name + repo-relative file) that buildSymbolGraph created.
Called from EnrichWithTypedResolution (the shared composition seam) so both BuildFromRepo (call_trace/impact_analysis) and buildAGECallGraph (code_graph) get Go IMPLEMENTS edges. See issue #467.
func InjectHookEdges ¶
InjectHookEdges adds synthetic call edges for WordPress hook connections.
For each hook name that has both a registration (add_action/add_filter, Side="server") and an invocation (do_action/apply_filters, Side="client"), it creates a CallEdge from the invoking function to the callback function.
hookRoutes must come from routes.ExtractAll("php", ...) and symbols from the same repository's parsed symbol table.
func InvalidateBuildCache ¶
func InvalidateBuildCache()
InvalidateBuildCache clears the entire BuildFromRepo cache. Used in tests and when a rebuild is explicitly requested.
func ResolveSpeculative ¶
func ResolveSpeculative(ctx context.Context, client *oxcodes.Client, root, language string, nodes []CallChainNode)
ResolveSpeculative walks the call tree and fills Speculative candidates for unresolved nodes (Symbol.Kind == "external") using ox-codes text search. At most maxSpeculativePerTrace resolutions are attempted for performance.
Types ¶
type BuildOpts ¶
type BuildOpts struct {
// IncludeFieldAccess keeps heuristic argref/field-access call sites even
// when they don't resolve to a known function symbol. Default false —
// unresolved CallSite.IsArgRef entries are dropped to avoid reporting
// vars (`ctx`, `localPath`) and member access (`opts.Slug`) as callees.
IncludeFieldAccess bool
}
BuildOpts controls how parser.CallSite entries are converted into call graph edges.
type CallChainNode ¶
type CallChainNode struct {
Symbol *parser.Symbol `json:"symbol"`
Children []CallChainNode `json:"children,omitempty"`
CallLine uint32 `json:"callLine,omitempty"`
Cycle bool `json:"cycle,omitempty"`
Speculative []SpeculativeCall `json:"speculative,omitempty"` // candidates for unresolved calls
Kind string `json:"kind,omitempty"` // internal: empty = normal; CrossLanguageFetchKind for synthetic nodes
Depth int `json:"depth,omitempty"` // only set on synthetic cross-language nodes
CallerKind string `json:"caller_kind,omitempty"` // production | test | example | benchmark
}
CallChainNode is a single node in the call chain tree.
type CallEdge ¶
type CallEdge struct {
Caller *parser.Symbol // function containing the call
Callee *parser.Symbol // target function (nil if unresolved)
CalleeName string // original name from source
Receiver string // qualifier if method call
Line uint32 // 1-based call site line
IsInterface bool // true when resolved via interface dispatch (go/types or SCIP)
}
CallEdge is a resolved (or unresolved) call from one function to another.
func FilterStdlibCalls ¶ added in v1.37.1
FilterStdlibCalls removes unresolved call edges whose callee name is a known standard-library method/builtin (clone, unwrap, to_string, iter, map, len, push, format, …). These edges never resolve to a project node and only pollute call traces with unresolved "external" nodes.
Only edges with a nil Callee (unresolved) are filtered — a project function that happens to share a name with a stdlib method (e.g. a user-defined `Clone`) is kept because its Callee symbol is set.
Applied after EnrichWithTypedResolution in both BuildFromRepo (the call_trace/impact_analysis path) and buildAGECallGraph (the code_graph indexing path), so the tree-sitter pipeline gets the same stdlib noise filter the SCIP conversion path already applies in convert.go. The SCIP-level filter is kept too (defence in depth — fewer edges to convert). See issue #466.
type CallGraph ¶
type CallGraph struct {
Edges []CallEdge
Symbols []*parser.Symbol
TypeRels []parser.TypeRelationship // interface/extends/embeds relationships
HookCallbacks []string // function names registered as hook callbacks
Tier string // "basic" (tree-sitter), "enhanced" (go/types merged), "full" (future)
Backend string // resolution backend: "tree-sitter", "tree-sitter+go/types", "tree-sitter+scip"
// UsesIndex maps a target file's relative path to a list of relative paths
// of Astro files that render it as a component (<Foo />). Populated by
// ResolveTemplateRefs during BuildFromRepo. Enables impact_analysis to
// report file-level USES callers for Astro components.
UsesIndex map[string][]string
}
CallGraph holds all call relationships for a repository.
func BuildCallGraph ¶
BuildCallGraph resolves call sites against the symbol table. Resolution: same-file -> same-package (directory) -> global name match.
Heuristic argref sites (parser.CallSite.IsArgRef==true) are dropped when they don't resolve to a function/method symbol — this filters noise like member access (`opts.Slug`) and local variable references (`ctx`, `localPath`) that the parser captures inside argument lists.
func BuildCallGraphWithOpts ¶
func BuildCallGraphWithOpts(symbols []*parser.Symbol, calls []parser.CallSite, opts BuildOpts) *CallGraph
BuildCallGraphWithOpts is BuildCallGraph with explicit options.
func BuildFromRepo ¶
func BuildFromRepo(ctx context.Context, input TraceRepoInput) (*CallGraph, error)
BuildFromRepo ingests a repo, parses files, and returns the call graph without tracing a specific symbol.
Delegates to BuildAndEnrich (the unified pipeline, issue #463) and caches the result. The background go/types warm-up is kept here because it is call_trace-specific (it targets the cgCache entry, not the pipeline result).
func ConvertToCallGraph ¶
func ConvertToCallGraph(typedEdges []goanalysis.TypedEdge, tsSymbols []*parser.Symbol) *CallGraph
ConvertToCallGraph converts typed edges to the existing CallGraph format by matching callers/callees against tree-sitter symbols by name and file.
func EnrichWithTypedResolution ¶ added in v1.23.0
func EnrichWithTypedResolution(ctx context.Context, root string, base *CallGraph, symbols []*parser.Symbol, files []*ingest.File) *CallGraph
EnrichWithTypedResolution is the single shared composition seam for typed call-edge enrichment: given a base (tree-sitter-only) CallGraph, it attempts go/types resolution for Go modules, then — only if that made no progress — SCIP resolution for non-Go languages, merging any successful pass additively via MergeCallGraphs. Route ANY new typed-edge source through this seam (and MergeCallGraphs) rather than composing typed enrichment ad hoc at a second call site; BuildFromRepo is the reference caller.
Both passes are bounded and non-fatal: on any failure (no go.mod, cold GOCACHE, no indexer, timeout) base is returned with Tier/Backend unchanged, exactly the tree-sitter-only degrade contract callers already depend on. root and files are needed independently of base/symbols because SCIP resolution walks the raw ingested file set for the dominant language, not the already-parsed symbol table.
func MergeCallGraphs ¶
MergeCallGraphs merges a tree-sitter call graph with a typed call graph. Typed edges take priority; unmatched tree-sitter edges are appended. HookCallbacks are preserved from the tree-sitter graph. Returns nil only if both inputs are nil.
type HookRoute ¶
type HookRoute struct {
Method string // "ACTION" or "FILTER"
Path string // hook name
Handler string // callback function name (empty for client-side)
Side string // "server" or "client"
Line uint32
}
HookRoute is a minimal representation of a WordPress hook route used by InjectHookEdges. It mirrors the fields needed from routes.Route without importing the routes package (to avoid circular dependencies).
type PipelineOpts ¶ added in v1.37.2
type PipelineOpts struct {
Root string
Focus string
Language string
IncludeFieldAccess bool
MaxFileBytes int64
ExcludeTests bool
// TypedEnrich controls whether EnrichWithTypedResolution runs.
// When false, the pipeline returns a tree-sitter-only graph (used by
// code_graph when CODEGRAPH_TYPED_ENRICH is unset).
TypedEnrich bool
}
PipelineOpts controls the unified pipeline behaviour.
type PipelineResult ¶ added in v1.37.2
type PipelineResult struct {
CG *CallGraph
Symbols []*parser.Symbol
Files []*ingest.File
Rels []parser.TypeRelationship
FileImports map[string][]string
TemplateRefs []TemplateRef
HookRoutes []HookRoute
}
PipelineResult is the output of the unified ingest→parse→build→enrich pipeline (BuildAndEnrich). It carries everything a consumer needs:
- CG: the enriched call graph (tree-sitter + go/types + SCIP + IMPLEMENTS)
- Symbols: all parsed symbols (for analyze, embeddings)
- Files: the ingested file list (for explore, compare, codesearch)
- Rels: type relationships (IMPLEMENTS + INHERITS) from enrichment
- FileImports: per-file import paths (for code_graph's import edges)
- TemplateRefs: resolved Astro component tag refs (for code_graph's USES edges)
- HookRoutes: WordPress hook routes (for code_graph's HOOK edges)
See issue #463. This is the single shared pipeline that subsumes the previously-duplicated ingest→parse→build→enrich sequences in BuildFromRepo (call_trace) and ingestAndParse+buildAGECallGraph (code_graph).
func BuildAndEnrich ¶ added in v1.37.2
func BuildAndEnrich(ctx context.Context, opts PipelineOpts) (*PipelineResult, error)
BuildAndEnrich is the single shared ingest→parse→build→enrich pipeline.
It replaces the duplicated sequences in BuildFromRepo (call_trace) and ingestAndParse+buildAGECallGraph (code_graph) with one code path:
ingest.IngestRepo (cached — #464) → parseFilesParallel (unified — #469) → BuildCallGraphWithOpts → EnrichWithTypedResolution (go/types + SCIP + IMPLEMENTS — #467) → return *PipelineResult
Consumers:
- call_trace: traces over result.CG in memory
- code_graph: persists result to AGE via buildGraph
- analyze: reads result.Symbols + result.Files
See issue #463.
type SpeculativeCall ¶
type SpeculativeCall struct {
Name string `json:"name"`
File string `json:"file"`
Line int `json:"line"`
Confidence float64 `json:"confidence"` // 0.0-1.0
}
SpeculativeCall represents a candidate callee found via text search.
type TemplateRef ¶ added in v1.37.2
type TemplateRef struct {
RelFile string // Astro file that contains the tag usage
ResolvedTo string // relative path of the imported component file
Line uint32
}
TemplateRef is a resolved Astro USES relationship ready for AGE insertion. Resolution (tag name → file path) is performed in BuildAndEnrich via ResolveTemplateRefs; unresolved refs are dropped before storage.
type TemplateUsage ¶ added in v1.22.0
type TemplateUsage struct {
// From is the relative path of the file that contains the template ref.
From string
// To is the relative path of the imported component file.
To string
// Line is the 1-based line number of the tag usage in the source file.
Line uint32
}
TemplateUsage represents a resolved USES relationship from a component file (Astro or Svelte) to the file it renders as a child component.
func ResolveTemplateRefs ¶
func ResolveTemplateRefs(src []byte, refs []preproc.TemplateRef, fileRel, root string) []TemplateUsage
ResolveTemplateRefs joins TemplateRefs against a file's import bindings to produce file-level USES relationships.
The import-binding source depends on the file kind:
- Astro (.astro and any non-.svelte): imports declared in the leading --- frontmatter block (scanFrontmatterBindings).
- Svelte (.svelte): imports declared in the component's <script> blocks (scanSvelteScriptBindings).
The binding→ref join and path resolution below are generic over both kinds.
Resolution order:
- Relative imports (./…, ../…) — resolved directly against the file's directory.
- Alias imports (~/…, @/…, any non-relative path) — looked up in the repo's tsconfig.json compilerOptions.paths (and astro.config vite.resolve.alias as fallback). The alias map is loaded once per root and cached process-wide.
- Unresolved — either (a) no alias key matched (bare/scoped npm package such as 'svelte', '@guide/core', 'astro:transitions') — silently dropped without incrementing the counter; or (b) an alias prefix matched but the resolved path does not exist on disk — the gocode_parser_unresolved_alias_total counter is incremented and the ref is dropped (broken declared alias).
type TraceOpts ¶
type TraceOpts struct {
Direction string // "callees" (default) or "callers"
MaxDepth int // default 5, max 10
// CrossRefs, when non-nil, extends the trace through HANDLES/FETCHES
// edges at +1 depth beyond the existing call graph. Noop when nil.
CrossRefs graphx.CrossRefs
// Repo is the repo key used for CrossRefs lookups. Required when CrossRefs is set.
Repo string
}
TraceOpts controls the call chain traversal.
type TraceRepoInput ¶
type TraceRepoInput struct {
Root string
Symbol string
Focus string
Language string
Opts TraceOpts
// IncludeFieldAccess keeps heuristic argref/field-access call sites even
// when they don't resolve to a known function symbol. Default false —
// unresolved argref captures (`opts.Slug`, `ctx`, `localPath`) are
// dropped. Set via the `field_access=true` MCP tool flag for legacy
// permissive behaviour.
IncludeFieldAccess bool
// Refresh forces a cache bypass — re-parses the repo and re-runs
// SCIP/go/types enrichment instead of returning the cached call graph.
// Use when the repo has changed (git checkout, new commit) and the
// in-memory cgCache is stale.
Refresh bool
}
TraceRepoInput configures a full repo call chain trace.
type TraceResult ¶
type TraceResult struct {
Root *parser.Symbol `json:"root,omitempty"`
Tree []CallChainNode `json:"tree"`
MaxDepth int `json:"maxDepth"`
TotalNodes int `json:"totalNodes"`
Resolved int `json:"resolved"`
Unresolved int `json:"unresolved"`
Tier string `json:"tier,omitempty"`
}
TraceResult holds the complete call chain traversal output.
func Trace ¶
Trace walks the call graph from the named symbol, building a tree of call chains. Direction "callees" follows outgoing calls; "callers" follows incoming calls.
When opts.CrossRefs is non-nil and opts.Repo is non-empty, a single extra pass runs after the main BFS/DFS: for each discovered node at depth <= MaxDepth-1 it checks whether the symbol HANDLES an HTTP route, then queries FetchedBy for upstream callers. Results are appended as synthetic CallChainNode values with Kind == CrossLanguageFetchKind. The pass never recurses. CrossRefs errors are swallowed (slog.Debug) — the feature is purely opportunistic.
func TraceRepo ¶
func TraceRepo(ctx context.Context, input TraceRepoInput) (*TraceResult, error)
TraceRepo ingests a repo, extracts symbols and calls, builds call graph, traces from symbol.