Documentation
¶
Overview ¶
Package callgraph builds and queries call relationships between functions.
Index ¶
- Constants
- func EagerWarmRepos(ctx context.Context, dirs []string)
- func InjectHookEdges(cg *CallGraph, hookRoutes []HookRoute)
- func InvalidateBuildCache()
- func ResolveSpeculative(ctx context.Context, client *oxcodes.Client, root, language 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 SpeculativeCall
- 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 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"` // empty = normal; CrossLanguageFetchKind for synthetic nodes
Depth int `json:"depth,omitempty"` // only set on synthetic cross-language nodes
}
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.
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.
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 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 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.