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" "maps" "os" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/checker" "golang.org/x/tools/go/packages" "golang.org/x/tools/txtar" ) const testdata = ` -- go.mod -- module example.com go 1.21 -- a/a.go -- package a import _ "example.com/b" import _ "example.com/c" func A1() func A2() func A3() -- b/b.go -- package b func B1() func B2() -- c/c.go -- package c import _ "example.com/d" func C1() -- d/d.go -- package d func D1() func D2() func D3() func D4() ` func main() { // Extract a tree of Go source files. // (Avoid the standard library as it is always evolving.) dir, err := os.MkdirTemp("", "") if err != nil { log.Fatal(err) } fs, err := txtar.FS(txtar.Parse([]byte(testdata))) if err != nil { log.Fatal(err) } if err := os.CopyFS(dir, fs); err != nil { log.Fatal(err) } // Load packages: example.com/a + dependencies // cfg := &packages.Config{Mode: packages.LoadAllSyntax, Dir: dir} initial, err := packages.Load(cfg, "example.com/a") if err != nil { log.Fatal(err) // failure to enumerate packages } // 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. packages.PrintErrors(initial) if len(initial) == 0 { log.Fatalf("no initial packages") } // Run analyzers (just one) on example.com packages. analyzers := []*analysis.Analyzer{pkgdecls} graph, err := checker.Analyze(analyzers, initial, nil) if err != nil { log.Fatal(err) } // Inspect the result of each analysis action, // including those for all dependencies. // // A realistic client would use Result, Err, Diagnostics, // but for test stability, we just print the action string // ("analyzer@package"). for act := range graph.All() { fmt.Println("printing", act) } // Print the package fact for the sole initial package. root := graph.Roots[0] fact := new(pkgdeclsFact) if root.PackageFact(root.Package.Types, fact) { for k, v := range fact.numdecls { fmt.Printf("%s:\t%d decls\n", k, v) } } } // pkgdecls is a trivial example analyzer that uses package facts to // compute information from the entire dependency graph. var pkgdecls = &analysis.Analyzer{ Name: "pkgdecls", Doc: "Computes a package fact mapping each package to its number of declarations.", Run: run, FactTypes: []analysis.Fact{(*pkgdeclsFact)(nil)}, } type pkgdeclsFact struct{ numdecls map[string]int } func (*pkgdeclsFact) AFact() {} func run(pass *analysis.Pass) (any, error) { numdecls := map[string]int{ pass.Pkg.Path(): pass.Pkg.Scope().Len(), } // Compute the union across all dependencies. for _, imp := range pass.Pkg.Imports() { if depFact := new(pkgdeclsFact); pass.ImportPackageFact(imp, depFact) { maps.Copy(numdecls, depFact.numdecls) } } pass.ExportPackageFact(&pkgdeclsFact{numdecls}) return nil, nil }
Output: printing pkgdecls@example.com/a printing pkgdecls@example.com/b printing pkgdecls@example.com/c printing pkgdecls@example.com/d example.com/a: 3 decls example.com/b: 2 decls example.com/c: 1 decls example.com/d: 4 decls
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 ¶
All returns an iterator over the action graph in depth-first postorder.
Example:
for act := range graph.All() { ... }
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 // contains filtered or unexported fields }
Options specifies options that control the analysis driver.