Documentation ¶
Overview ¶
Package checker provides an analysis driver based on the golang.org/x/tools/go/packages representation of a set of packages and all their dependencies, as produced by packages.Load.
It is the core of multichecker (the multi-analyzer driver), singlechecker (the single-analyzer driver often used to provide a convenient command alongside each analyzer), and analysistest, the test driver.
By contrast, the 'go vet' command is based on unitchecker, an analysis driver that uses separate analysis--analogous to separate compilation--with file-based intermediate results. Like separate compilation, it is more scalable, especially for incremental analysis of large code bases. Commands based on multichecker and singlechecker are capable of detecting when they are being invoked by "go vet -vettool=exe" and instead dispatching to unitchecker.
Programs built using this package will, in general, not be usable in that way. This package is intended only for use in applications that invoke the analysis driver as a subroutine, and need to insert additional steps before or after the analysis.
See the Example of how to build a complete analysis driver program.
Example ¶
//go:build !wasm // The example command demonstrates a simple go/packages-based // analysis driver program. package main import ( "fmt" "log" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/checker" "golang.org/x/tools/go/packages" ) func main() { // Load packages: just this one. // // There may be parse or type errors among the // initial packages or their dependencies, // but the analysis driver can handle faulty inputs, // as can some analyzers. cfg := &packages.Config{Mode: packages.LoadAllSyntax} initial, err := packages.Load(cfg, ".") if err != nil { log.Fatal(err) // failure to enumerate packages } if len(initial) == 0 { log.Fatalf("no initial packages") } // Run analyzers (just one) on packages. analyzers := []*analysis.Analyzer{minmaxpkg} graph, err := checker.Analyze(analyzers, initial, nil) if err != nil { log.Fatal(err) } // Print information about the results of each // analysis action, including all dependencies. // // Clients using Go 1.23 can say: // for act := range graph.All() { ... } graph.All()(func(act *checker.Action) bool { // Print information about the Action, e.g. // // act.String() // act.Result // act.Err // act.Diagnostics // // (We don't actually print anything here // as the output would vary over time, // which is unsuitable for a test.) return true }) // Print the minmaxpkg package fact computed for this package. root := graph.Roots[0] fact := new(minmaxpkgFact) if root.PackageFact(root.Package.Types, fact) { fmt.Printf("min=%s max=%s", fact.min, fact.max) } } // minmaxpkg is a trival example analyzer that uses package facts to // compute information from the entire dependency graph. var minmaxpkg = &analysis.Analyzer{ Name: "minmaxpkg", Doc: "Finds the min- and max-named packages among our dependencies.", Run: run, FactTypes: []analysis.Fact{(*minmaxpkgFact)(nil)}, } // A package fact that records the alphabetically min and max-named // packages among the dependencies of this package. // (This property was chosen because it is relatively stable // as the codebase evolves, avoiding frequent test breakage.) type minmaxpkgFact struct{ min, max string } func (*minmaxpkgFact) AFact() {} func run(pass *analysis.Pass) (any, error) { // Compute the min and max of the facts from our direct imports. f := &minmaxpkgFact{min: pass.Pkg.Path(), max: pass.Pkg.Path()} for _, imp := range pass.Pkg.Imports() { if f2 := new(minmaxpkgFact); pass.ImportPackageFact(imp, f2) { if f2.min < f.min { f.min = f2.min } if f2.max > f.max { f.max = f2.max } } } pass.ExportPackageFact(f) return nil, nil }
Output: min=bufio max=unsafe
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Action ¶
type Action struct { Analyzer *analysis.Analyzer Package *packages.Package IsRoot bool // whether this is a root node of the graph Deps []*Action Result any // computed result of Analyzer.run, if any (and if IsRoot) Err error // error result of Analyzer.run Diagnostics []analysis.Diagnostic Duration time.Duration // execution time of this step // contains filtered or unexported fields }
An Action represents one unit of analysis work by the driver: the application of one analysis to one package. It provides the inputs to and records the outputs of a single analysis.Pass.
Actions form a DAG, both within a package (as different analyzers are applied, either in sequence or parallel), and across packages (as dependencies are analyzed).
func (*Action) AllObjectFacts ¶
func (act *Action) AllObjectFacts() []analysis.ObjectFact
AllObjectFacts returns a new slice containing all object facts of the analysis's FactTypes in unspecified order.
See documentation at AllObjectFacts field of analysis.Pass.
func (*Action) AllPackageFacts ¶
func (act *Action) AllPackageFacts() []analysis.PackageFact
AllPackageFacts returns a new slice containing all package facts of the analysis's FactTypes in unspecified order.
See documentation at AllPackageFacts field of analysis.Pass.
func (*Action) ObjectFact ¶
ObjectFact retrieves a fact associated with obj, and returns true if one was found. Given a value ptr of type *T, where *T satisfies Fact, ObjectFact copies the value to *ptr.
See documentation at ImportObjectFact field of analysis.Pass.
func (*Action) PackageFact ¶
PackageFact retrieves a fact associated with package pkg, which must be this package or one of its dependencies.
See documentation at ImportObjectFact field of analysis.Pass.
type Graph ¶
type Graph struct { // Roots contains the roots of the action graph. // Each node (a, p) in the action graph represents the // application of one analyzer a to one package p. // (A node thus corresponds to one analysis.Pass instance.) // Roots holds one action per element of the product // of the analyzers × packages arguments to Analyze, // in unspecified order. // // Each element of Action.Deps represents an edge in the // action graph: a dependency from one action to another. // An edge of the form (a, p) -> (a, p2) indicates that the // analysis of package p requires information ("facts") from // the same analyzer applied to one of p's dependencies, p2. // An edge of the form (a, p) -> (a2, p) indicates that the // analysis of package p requires information ("results") // from a different analyzer a2 applied to the same package. // These two kind of edges are called "vertical" and "horizontal", // respectively. Roots []*Action }
Graph holds the results of a round of analysis, including the graph of requested actions (analyzers applied to packages) plus any dependent actions that it was necessary to compute.
func Analyze ¶
func Analyze(analyzers []*analysis.Analyzer, pkgs []*packages.Package, opts *Options) (*Graph, error)
Analyze runs the specified analyzers on the initial packages.
The initial packages and all dependencies must have been loaded using the packages.LoadAllSyntax flag, Analyze may need to run some analyzer (those that consume and produce facts) on dependencies too.
On success, it returns a Graph of actions whose Roots hold one item per (a, p) in the cross-product of analyzers and pkgs.
If opts is nil, it is equivalent to new(Options).
func (*Graph) All ¶
func (g *Graph) All() actionSeq
All returns an iterator over the action graph in depth-first postorder.
Example:
for act := range graph.All() { ... }
Clients using go1.22 should iterate using the code below and may not assume anything else about the result:
graph.All()(func (act *Action) bool { ... })
type Options ¶
type Options struct { // These options correspond to existing flags exposed by multichecker: Sequential bool // disable parallelism SanityCheck bool // check fact encoding is ok and deterministic FactLog io.Writer // if non-nil, log each exported fact to it }
Options specifies options that control the analysis driver.