goanalysis

package
v1.39.1 Latest Latest
Warning

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

Go to latest
Published: Jul 17, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package goanalysis provides Go type-aware analysis via go/types.

ConvertToCallGraph and MergeCallGraphs have moved to the callgraph package to avoid a circular dependency. Import callgraph directly.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func HasGoModule

func HasGoModule(dir string) bool

HasGoModule checks if dir contains a go.mod file.

func InvalidateCachedLoad added in v1.23.0

func InvalidateCachedLoad(root string)

InvalidateCachedLoad evicts any cached entry (success or failure) for root, forcing the next CachedLoadPackages(ctx, root) to run a fresh load.

Used before a load that must not be short-circuited by a stale negative-cached failure — e.g. callgraph's background GOCACHE-warm retry, which deliberately re-attempts with a much longer timeout after an earlier quick synchronous probe already failed (and got negative-cached under loadCacheNegativeTTL). Without eviction, the patient retry would immediately replay the stale failure from the cache instead of running the fresh, now-likely-to-succeed load it exists to perform.

Types

type LoadOpts

type LoadOpts struct {
	Patterns []string      // package patterns to load (default: "./...")
	Timeout  time.Duration // override default 60s timeout
}

LoadOpts configures package loading.

type LoadResult

type LoadResult struct {
	Packages []*packages.Package
	Errors   []string // non-fatal errors
}

LoadResult contains loaded packages with full type information.

func CachedLoadPackages added in v1.23.0

func CachedLoadPackages(ctx context.Context, root string) (*LoadResult, error)

CachedLoadPackages wraps LoadPackages with a small bounded LRU + short TTL cache keyed by root, coalesced with singleflight, so a full go/types NeedDeps load for a given repo is paid at most once per TTL window regardless of how many callers ask for it — concurrently or in sequence. This is what makes "load once, reuse for IMPLEMENTS+CALLS" a property of the cache rather than something callers coordinate: extractGoImplements (IMPLEMENTS) and tryGoTypesResolution (CALLS) both route through this function, so whichever runs first against a repo warms it for the other, and concurrent callers racing a cold cache share the single in-flight load instead of each paying for their own.

Failures are cached per cacheTTLFor: a genuine repo-level failure (no go.mod, unbuildable deps) is cached under loadCacheNegativeTTL so a persistently-broken repo doesn't retry-storm packages.Load on every enrichment call; a shared-load-budget-specific failure (the coalesced load's own bounded deadline elapsing) is never cached, so it can't poison a later, independently-budgeted attempt against the same root.

Concurrency: each caller gets its OWN ctx honored independently, even though at most one go/packages load is ever in flight per root. On a cache miss, the underlying load runs under loadGroup.DoChan in a dedicated goroutine (singleflight's own semantics — DoChan does not run the closure on any specific caller's goroutine), bounded by a budget the closure allocates for ITSELF (defaultTimeout, fully decoupled from every caller's ctx — see the closure body). Every caller — whichever happened to trigger the load and every later joiner racing the same cold root — then independently selects between that shared result and its own ctx.Done(): a short-budget caller (e.g. the 10s synchronous warm probe in EnrichWithTypedResolution) returns its own ctx.Err() at its own deadline without waiting out a slower shared load (e.g. the 15-minute background GOCACHE warm, or a longer sibling caller's own load), and a long-budget caller (e.g. the 30s extractGoImplements IMPLEMENTS pass) is never handed a short-budget sibling's premature timeout — it either gets the shared load's real result or, on true failure, its own independent ctx.Err(). The shared load itself is unaffected by any caller giving up early: it keeps running (up to its own bounded budget) and still populates the cache for the next caller, since it was never tied to a specific caller's cancellation in the first place.

This replaces an earlier version of this function that ran the coalesced load directly under whichever caller's ctx happened to start it (the "leader") and fanned that leader's ctx and result to every other waiter unconditionally — see the go-code PR #294 pr-review-council round-2 report (reviews/pr-council/pr-294-2026-07-01.md) for the two failure directions that caused: a short-budget leader's premature ctx-cancel error silently starving a long-budget follower's edges, and a short-budget follower blocking for a slow leader's FULL duration instead of its own deadline.

func LoadPackages

func LoadPackages(ctx context.Context, dir string, opts LoadOpts) (*LoadResult, error)

type Satisfaction added in v1.21.0

type Satisfaction struct {
	Type      string // concrete (non-interface) named type, e.g. "GitHubForge"
	TypeFile  string // absolute path of the concrete type's declaration
	Interface string // interface named type, e.g. "Forge"
}

Satisfaction is one structural interface-satisfaction fact: the named concrete type Type (declared in TypeFile) satisfies the named interface Interface. Computed via go/types, which Go's structural typing makes invisible to tree-sitter (no `implements` keyword).

Names are bare (package qualifier stripped), matching how the codegraph Symbol vertices are keyed (name + file). TypeFile is an absolute path from the FileSet; the caller maps it to repo-relative form for the graph key. The interface endpoint is resolved by the codegraph caller via the symbol table (name lookup in buildRelationshipEdges), so the interface's declaration file is not carried here.

func ComputeSatisfactions added in v1.21.0

func ComputeSatisfactions(pkgs []*packages.Package) []Satisfaction

ComputeSatisfactions returns every (concrete type, interface) pair across the loaded package set where the type — by value OR by pointer — implements the interface. The empty interface (zero methods) is skipped: every type trivially satisfies it, so emitting those edges would be pure noise and O(types) blow-up.

Scope is bounded to the loaded packages (the module's own ./...), NOT the transitive dependency universe: gatherNamedNonInterface and the interface walk both iterate only pkgs, so a type T is checked only against interfaces declared in the same loaded set. This keeps the satisfaction loop at O(localTypes × localInterfaces) rather than O(types × universe).

The pairing logic mirrors mapInterfaceImpls (which builds the call-resolution concreteTypes map and discards the type→interface direction); both share the types.Implements(T, I) || types.Implements(*T, I) test.

type TypedEdge

type TypedEdge struct {
	CallerName   string // function containing the call
	CallerFile   string // absolute path
	CallerLine   uint32 // line of the caller function definition
	CalleeName   string // called function/method name
	CalleeFile   string // absolute path (empty if external)
	CalleePkg    string // package path of callee
	ReceiverType string // receiver type for method calls (empty for functions)
	Line         uint32 // line of the call site
	IsInterface  bool   // true if this is interface dispatch
}

TypedEdge is a call edge with full type information from the Go compiler.

func Resolve

func Resolve(pkgs []*packages.Package) []TypedEdge

Resolve walks loaded packages and extracts type-aware call edges.

Jump to

Keyboard shortcuts

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