codeintel

package
v0.13.1 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package codeintel holds the static-analysis engines behind Atlas's code-intelligence tools.

Everything here parses Go source with go/ast and go/parser only -- no type checking, no build step, no module download. That makes the analyses fast and usable on a tree that does not currently compile, which is exactly the state an agent most often finds a repository in. The trade-off is that results are heuristics rather than proofs, and each analysis documents where it can be wrong.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIPackage

type APIPackage struct {
	Name    string
	Dir     string
	Symbols []APISymbol
	// Undocumented counts exported symbols with no doc comment.
	Undocumented int
}

APIPackage is one package's exported surface.

type APIResult

type APIResult struct {
	Packages     []APIPackage
	FilesScanned int
}

APIResult is one scan.

func APISurface

func APISurface(root string, includeTests bool) (APIResult, error)

APISurface reports the exported declarations of every package under root.

This is what a consumer of the package can actually reach, which is a different and usually much smaller thing than what the package contains. It is also the part that cannot be changed without breaking somebody, so it is the right thing to look at before a rename, a signature change, or a release.

Struct fields are included because an exported field on an exported struct is part of the contract just as much as a method -- changing its type breaks callers exactly the same way.

type APISymbol

type APISymbol struct {
	Name string
	// Kind is "func", "method", "type", "const", "var", or "field".
	Kind string
	// Recv is the receiver type for a method, or the owning struct for a
	// field.
	Recv string
	// Signature is the rendered declaration, e.g.
	// "func New(cfg Config) (*Client, error)".
	Signature string
	// Doc is the first line of the doc comment, which is where Go
	// convention puts the summary.
	Doc string
	// Deprecated reports a "Deprecated:" paragraph in the doc comment.
	Deprecated bool
	File       string
	Line       int
}

APISymbol is one exported declaration.

type AntiPatternFinding

type AntiPatternFinding struct {
	// Kind is one of "swallowed-error", "context-not-first",
	// "panic-in-library".
	Kind    string
	File    string
	Line    int
	Func    string
	Message string
	Snippet string
}

AntiPatternFinding is one syntactic smell found in a Go source file.

type AntiPatternOptions

type AntiPatternOptions struct {
	// IncludeTests scans _test.go files too. Off by default: a test
	// deliberately panicking on a bad fixture, or discarding an error
	// from a throwaway helper, is normal and not worth flagging.
	IncludeTests bool
}

AntiPatternOptions narrows a scan.

type AntiPatternResult

type AntiPatternResult struct {
	Findings     []AntiPatternFinding
	FilesScanned int
	ByKind       map[string]int
}

AntiPatternResult is the outcome of a scan.

func ScanAntiPatterns

func ScanAntiPatterns(root string, opts AntiPatternOptions) (AntiPatternResult, error)

ScanAntiPatterns walks root's Go source for three syntactic smells:

  • swallowed-error: an `if err != nil { }` whose body does nothing, or an error-like value explicitly discarded with `_ = err`. Both compile cleanly and both can hide a real failure.
  • context-not-first: a function with a context.Context parameter that isn't the first one, which breaks the convention every caller and linter expects.
  • panic-in-library: a panic outside package main, a test file, or a function named MustXxx (where panicking is the documented contract). A library that panics instead of returning an error takes the decision to crash away from its caller.

This is name- and shape-based, not type-checked: it can flag a `_ =` discard on a value that only looks like an error, and it cannot see through an error wrapped in a different variable name. Findings are candidates for review, not a list of confirmed bugs.

type Caller

type Caller struct {
	Func  FuncRef
	Depth int
	// Via names the function it calls that leads to the target, so a
	// multi-hop result reads as a chain instead of a flat list.
	Via string
}

Caller is one function that calls the symbol under analysis, together with how many hops away it is.

type CodeSymbol

type CodeSymbol struct {
	Name      string
	Kind      string // "func", "method", "type", "const", "var"
	Recv      string
	Signature string
	// Doc is the full doc comment text, not just its first line -- a
	// search wants every word available to match against.
	Doc  string
	File string
	Line int
}

CodeSymbol is one top-level declaration -- exported or not, unlike APISurface, because the target of a search across an unfamiliar codebase is just as often an unexported helper as a public entry point.

type ConcreteType

type ConcreteType struct {
	Name    string
	Package string
	File    string
	Line    int
	Methods []Method
	// PointerOnly is true when at least one method has a pointer
	// receiver, meaning only *T satisfies an interface, not T.
	PointerOnly bool
}

ConcreteType is a named type together with the methods declared on it.

type DeadCodeResult

type DeadCodeResult struct {
	Symbols      []DeadSymbol
	FilesScanned int
	// SkippedTests reports whether _test.go files were excluded. When they
	// are, a symbol used only by tests still counts as dead, which is
	// usually what a cleanup pass wants but is worth stating.
	SkippedTests bool
}

DeadCodeResult is the outcome of a dead-code scan.

func FindDeadCode

func FindDeadCode(root string, includeTests bool, includeUnexported bool) (DeadCodeResult, error)

FindDeadCode reports declarations in root that nothing else references.

The analysis is deliberately syntactic: it collects every top-level declaration, then counts identifier occurrences across the whole tree. A declaration whose name is never mentioned outside its own declaration site is reported.

It is a heuristic, and it is conservative in one direction and not the other:

  • It never misses a genuine use through a plain call or reference, because any mention of the name counts.
  • It CAN report a false positive for a symbol reached only through reflection, a struct tag, code generation, a build-tagged file that was skipped, or an interface satisfied implicitly (a method that exists only to satisfy an interface is never named at the call site).
  • It CAN also report a false positive for an exported symbol consumed by a downstream module, since only the given tree is scanned.

Callers must present the output as candidates to review, not as a list safe to delete blindly.

type DeadSymbol

type DeadSymbol struct {
	Name     string
	Kind     string // "func", "method", "type", "const", "var"
	File     string
	Line     int
	Exported bool
}

DeadSymbol is one declaration that no other file in the scanned tree appears to reference.

type DocstringOptions

type DocstringOptions struct {
	// IncludeTests scans _test.go files too. Off by default: exported
	// names in test files are rarely part of anything's public surface.
	IncludeTests bool
	// Symbol restricts suggestions to a single exported name. Empty
	// scans every undocumented exported declaration.
	Symbol string
}

DocstringOptions narrows a scan.

type DocstringResult

type DocstringResult struct {
	Suggestions  []DocstringSuggestion
	FilesScanned int
}

DocstringResult is the outcome of a scan.

func GenerateDocstrings

func GenerateDocstrings(root string, opts DocstringOptions) (DocstringResult, error)

GenerateDocstrings finds exported declarations under root that have no doc comment and proposes a stub for each, shaped from the declaration's own signature: a parameter is listed by name and type, and a non-error return is called out, but the description text is left as a TODO for a human or a follow-up model call to fill in -- this tool reads syntax, it does not know what the code is for.

type DocstringSuggestion

type DocstringSuggestion struct {
	Name string
	// Kind is "func", "method", or "type".
	Kind string
	// Recv is the receiver type for a method.
	Recv      string
	Signature string
	// Stub is the suggested comment block, following Go's convention of
	// starting the comment with the declared name. The prose is a
	// placeholder -- describing what the name already says would add
	// nothing -- but the shape (summary line, parameter list, return
	// note) is filled in from the signature so only the wording is left
	// to do.
	Stub string
	File string
	Line int
}

DocstringSuggestion is a ready-to-paste doc comment for one exported declaration that currently has none.

type EnvAuditResult

type EnvAuditResult struct {
	Usages []EnvVarUsage
	// Undocumented lists the distinct literal names read but not found
	// in an example env file, sorted. Empty when no such file exists to
	// compare against -- see EnvFileFound.
	Undocumented []string
	// EnvFileFound reports whether a .env.example / .env.sample was
	// found to compare against at all. When false, Undocumented is not
	// meaningful -- there was nothing to check documentation against.
	EnvFileFound bool
	EnvFilePath  string
	FilesScanned int
}

EnvAuditResult is the outcome of a scan.

func AuditEnvVars

func AuditEnvVars(root string, includeTests bool) (EnvAuditResult, error)

AuditEnvVars walks root for Go source and lists every os.Getenv, os.LookupEnv, and os.Setenv call, then cross-checks the variables read against an example env file (.env.example, .env.sample, or .env.dist, whichever is found first at root) if one exists.

type EnvVarUsage

type EnvVarUsage struct {
	// Name is the literal environment variable name, or "(dynamic)" when
	// the call built its key from something other than a string
	// literal -- a format string, a variable, string concatenation.
	Name string
	// Kind is "read" (Getenv, LookupEnv) or "write" (Setenv).
	Kind string
	File string
	Line int
}

EnvVarUsage is one os.Getenv/LookupEnv/Setenv call site.

type FileMetrics

type FileMetrics struct {
	Path      string
	Lines     int
	Functions int
}

FileMetrics aggregates one file.

type FuncMetrics

type FuncMetrics struct {
	Name string
	Recv string
	File string
	Line int
	// Complexity is cyclomatic complexity: the number of linearly
	// independent paths through the body. One plus a branch point count.
	Complexity int
	// Lines is the span of the declaration in source lines, braces
	// included.
	Lines int
	// Params and Results count the signature's arity. A long parameter
	// list is a design smell the complexity number does not capture.
	Params  int
	Results int
	// Nesting is the deepest block nesting inside the body. Two functions
	// can share a complexity score while one is flat and the other is a
	// staircase, and the staircase is the harder one to read.
	Nesting int
	// Returns counts return statements, which is what makes a function
	// hard to reason about when combined with deep nesting.
	Returns int
}

FuncMetrics is the measured shape of one function.

type FuncRef

type FuncRef struct {
	Name string
	// Recv is the receiver type name for a method, empty for a plain
	// function.
	Recv    string
	Package string
	File    string
	Line    int
}

FuncRef locates one function or method declaration.

func (FuncRef) Key

func (f FuncRef) Key() string

Key is the name the call graph is indexed by. Methods are indexed by bare method name rather than receiver.method, because a call site written as x.Close() carries no type information without a type checker -- so any Close() is a possible target. Over-approximating here is the safe direction for an impact question: it can name callers that are not really affected, never miss one that is.

type HierarchyResult

type HierarchyResult struct {
	Interfaces      []Interface
	Types           []ConcreteType
	Implementations []Implementation
	FilesScanned    int
}

HierarchyResult is the outcome of a type-hierarchy scan.

func TypeHierarchy

func TypeHierarchy(root string, includeTests bool) (HierarchyResult, error)

TypeHierarchy reports which named types in root satisfy which interfaces declared in the same tree.

Like the rest of this package the analysis is syntactic: method sets are matched by name and by the printed text of the signature. That is exact for the common case and wrong in three specific ways, all of which callers must surface rather than hide:

  • Two identical types spelled differently (an alias, a dot-import, a package qualifier present in one file and absent in the other) will not match, so a real implementation can be missed.
  • Interfaces declared outside the scanned tree -- io.Reader, or anything from a dependency -- are not known, so satisfying them is invisible here.
  • Generic type parameters are compared as written, so two instantiations that are identical after substitution may not match.

Embedded interfaces are flattened when the embedded interface is also in the tree; when it is not, its name is kept in Embeds and its methods are simply unknown, which can only cause a missed match, never a false one.

type ImpactResult

type ImpactResult struct {
	Target FuncRef
	// Ambiguous holds the other declarations sharing the target's name.
	// Their presence means callers may belong to any of them.
	Ambiguous []FuncRef
	Callers   []Caller
	// Reached counts distinct functions in the transitive caller set.
	Reached      int
	MaxDepth     int
	FilesScanned int
	// Truncated reports that the walk stopped at the depth limit and the
	// real caller set is larger.
	Truncated bool
}

ImpactResult is the outcome of a reverse-call-graph walk.

func ImpactAnalysis

func ImpactAnalysis(root, symbol string, maxDepth int, includeTests bool) (ImpactResult, error)

ImpactAnalysis reports which functions transitively call symbol, out to maxDepth hops, so the blast radius of changing it is visible before the change is made.

Call sites are matched on the identifier alone. Without a type checker there is no way to tell x.Close() on one type from x.Close() on another, so every declaration named Close is a candidate and every Close() call site is an edge. This over-approximates: the result may name a caller that is not truly affected. That is deliberate -- for "what breaks if I change this", a false alarm costs a moment's reading and a miss costs a broken build. Ambiguous names are reported alongside the result so the caller knows when to distrust the breadth.

type Implementation

type Implementation struct {
	Type      ConcreteType
	Interface Interface
	// ViaPointer records that the satisfying type is *T rather than T.
	ViaPointer bool
}

Implementation pairs a concrete type with an interface it satisfies.

type ImportGraphResult

type ImportGraphResult struct {
	Module   string
	Packages []PackageNode
	// Cycles holds each import cycle found among internal packages, as an
	// ordered list of import paths where the last element imports the
	// first.
	Cycles [][]string
}

ImportGraphResult is the outcome of an import-graph scan.

func ImportGraph

func ImportGraph(root string, includeTests bool) (ImportGraphResult, error)

ImportGraph builds the internal package dependency graph rooted at root and reports any import cycles in it.

Package identity is the directory, which is how Go itself defines it. Import paths are resolved through the module path in go.mod when one is found at or above root; without a go.mod, packages are identified by their path relative to root and only same-tree edges are recognised.

Only static import declarations are read. An import injected by build tags in a file excluded from the scan, or a dependency expressed through a plugin or reflection, is not visible -- so the graph can be missing an edge, and a cycle it does not report may still exist.

type Interface

type Interface struct {
	Name    string
	Package string
	File    string
	Line    int
	Methods []Method
	// Embeds names interfaces this one embeds. They are resolved when
	// the embedded interface is also in the scanned tree, and left here
	// for reporting either way.
	Embeds []string
}

Interface is one interface declaration found in the scanned tree.

type Method

type Method struct {
	Name string
	// Sig is the rendered parameter and result list, e.g.
	// "(ctx context.Context) (int, error)". It is compared textually,
	// which is why the analysis is a heuristic -- see TypeHierarchy.
	Sig string
}

Method is one method in an interface's method set or on a concrete type, reduced to the shape that decides assignability.

type MetricIndexResult

type MetricIndexResult struct {
	Metrics      []PromMetric
	FilesScanned int
}

MetricIndexResult is the outcome of a scan.

func IndexMetrics

func IndexMetrics(root string, includeTests bool) (MetricIndexResult, error)

IndexMetrics walks root for Go source and lists every Prometheus metric constructed with prometheus.NewXxx or promauto.NewXxx (with or without a preceding .With(...) registerer call).

A metric whose Opts argument isn't a literal at the call site -- built in a variable, a helper function, or a loop -- is still counted, but with its name reported as "(unknown)" and its Help and Labels empty, since reading through an arbitrary expression back to its literal values is not something syntax alone can promise.

type MetricsResult

type MetricsResult struct {
	Functions    []FuncMetrics
	Files        []FileMetrics
	FilesScanned int
	TotalLines   int
}

MetricsResult is the outcome of a metrics scan.

func Metrics

func Metrics(root string, includeTests bool) (MetricsResult, error)

Metrics measures every function under root.

Cyclomatic complexity is counted the standard way: one, plus one for each if, for, range, case, branching comm clause, and each && or ||. That matches gocyclo and golangci-lint's cyclop, so a number here is comparable to a number from those.

The measurements are exact -- unlike the rest of this package there is no approximation, because counting syntax needs no type information. What is a judgement call is what the numbers mean, and that belongs to the caller: a 30-branch switch over an enum is fine, a 30-branch nest of conditionals is not, and no threshold tells them apart.

type PackageNode

type PackageNode struct {
	// ImportPath is the module-qualified path, e.g.
	// "example.com/mod/internal/agent". It falls back to the directory
	// path relative to the root when no module can be determined.
	ImportPath string
	Dir        string
	Name       string
	// Internal holds imports that resolve to another package in this
	// same module, already normalised to import paths.
	Internal []string
	// External holds third-party and standard-library imports.
	External []string
	Files    int
}

PackageNode is one directory of Go source, with the imports its files declare.

type PromMetric

type PromMetric struct {
	// Name is Namespace_Subsystem_Name joined per Prometheus convention,
	// or "(unknown)" when the opts weren't a literal this tool could
	// read.
	Name string
	// Type is "counter", "gauge", "histogram", or "summary".
	Type   string
	Help   string
	Labels []string
	File   string
	Line   int
}

PromMetric is one Prometheus metric registration found in source.

type SearchOptions

type SearchOptions struct {
	IncludeTests bool
	// Limit caps how many matches are returned. Zero means 10.
	Limit int
}

SearchOptions narrows a search.

type SearchResult

type SearchResult struct {
	Matches      []SymbolMatch
	FilesScanned int
}

SearchResult is the outcome of a search.

func SemanticSearch

func SemanticSearch(root, query string, opts SearchOptions) (SearchResult, error)

SemanticSearch indexes every top-level declaration under root and ranks them against query by matching words in the declaration's name and doc comment -- not an embedding, just tokenised keyword overlap, weighted so a name match counts for more than a doc match. That is a real limitation: it finds "which symbol's name or comment mentions these words", not "which symbol does what you mean" -- a function with no doc comment and a name that doesn't share vocabulary with the query will not be found even if it's exactly the right one.

type SecurityFinding

type SecurityFinding struct {
	// Kind is one of "hardcoded-credential", "weak-crypto", "insecure-tls",
	// "sql-injection-risk", "command-injection-risk".
	Kind    string
	File    string
	Line    int
	Func    string
	Message string
	Snippet string
}

SecurityFinding is one syntactic security smell found in a Go source file.

type SecurityScanOptions

type SecurityScanOptions struct {
	// IncludeTests scans _test.go files too. Off by default: fixtures and
	// mock secrets in tests are normal and not worth flagging.
	IncludeTests bool
}

SecurityScanOptions narrows a scan.

type SecurityScanResult

type SecurityScanResult struct {
	Findings     []SecurityFinding
	FilesScanned int
	ByKind       map[string]int
}

SecurityScanResult is the outcome of a scan.

func SecurityScan

func SecurityScan(root string, opts SecurityScanOptions) (SecurityScanResult, error)

SecurityScan walks root's Go source and flags syntactic security smells: hardcoded credentials, use of broken cryptographic primitives, disabled TLS verification, and string-built SQL or shell commands.

type SymbolMatch

type SymbolMatch struct {
	CodeSymbol
	Score int
	// MatchedTerms lists which query words contributed to the score, so
	// a result can be explained rather than trusted blindly.
	MatchedTerms []string
}

SymbolMatch is one search result.

type TestSkeleton

type TestSkeleton struct {
	FuncName string
	Skeleton string
	// Imports lists the packages the skeleton references, so the caller
	// knows what to add alongside it -- this is a snippet to paste into
	// an existing test file, not a standalone one, so it deliberately
	// does not print its own import block.
	Imports []string
	File    string
	Line    int
}

TestSkeleton is a generated table-driven test for one function.

func GenerateTestSkeleton

func GenerateTestSkeleton(root, symbol string) (TestSkeleton, error)

GenerateTestSkeleton finds symbol -- a package-level function, not a method -- under root and generates a table-driven test skeleton shaped from its parameters and return values.

Methods are not supported: constructing a receiver generically isn't possible from syntax alone, and a wrong guess (a zero value, say) would produce a skeleton that compiles but tests nothing meaningful. A plain function's parameters can all become table fields instead.

type Todo

type Todo struct {
	// Kind is the normalised marker: TODO, FIXME, HACK, XXX, BUG,
	// NOTE, OPTIMIZE, DEPRECATED.
	Kind string
	// Owner is the name in "TODO(alice):" when there is one. An owned
	// marker is one somebody can be asked about; an unowned one usually
	// belongs to nobody.
	Owner string
	// Ticket is an issue reference found in the text, e.g. "#1234" or
	// "PROJ-42".
	Ticket  string
	File    string
	Line    int
	Text    string
	Context string
}

Todo is one marker found in a comment.

type TodoOptions

type TodoOptions struct {
	// Kinds, when non-empty, restricts to these markers (case
	// insensitive).
	Kinds []string
	// MaxResults caps findings. Zero means 500.
	MaxResults int
	// IncludeTests scans _test.go and similar files too.
	IncludeTests bool
	// Extensions, when non-empty, restricts to these file extensions
	// (with the leading dot).
	Extensions []string
}

TodoOptions narrows a scan.

type TodoResult

type TodoResult struct {
	Todos        []Todo
	FilesScanned int
	// ByKind counts each marker type.
	ByKind map[string]int
	// Truncated reports that the scan stopped at its limit.
	Truncated bool
}

TodoResult is one scan.

func FindTodos

func FindTodos(root string, opts TodoOptions) (TodoResult, error)

FindTodos scans root for markers left in comments.

Jump to

Keyboard shortcuts

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