checker

package
v0.28.0 Latest Latest
Warning

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

Go to latest
Published: Dec 4, 2024 License: BSD-3-Clause Imports: 18 Imported by: 0

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

func (act *Action) ObjectFact(obj types.Object, ptr analysis.Fact) bool

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

func (act *Action) PackageFact(pkg *types.Package, ptr analysis.Fact) bool

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.

func (*Action) String

func (act *Action) String() string

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 {
	...
})

func (*Graph) PrintJSON

func (g *Graph) PrintJSON(w io.Writer) error

PrintJSON emits diagnostics in JSON form to w. Diagnostics are shown only for the root nodes, but errors (if any) are shown for all dependencies.

func (*Graph) PrintText

func (g *Graph) PrintText(w io.Writer, contextLines int) error

PrintText emits diagnostics as plain text to w.

If contextLines is nonnegative, it also prints the offending line, plus that many lines of context before and after the line.

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.

Jump to

Keyboard shortcuts

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