Documentation

Overview ¶

Background

Background

Analyzer

Analyzer

Pass

Pass

Modular analysis with Facts

Modular analysis with Facts

Testing an Analyzer

Testing an Analyzer

Standalone commands

Standalone commands

Package analysis defines the interface between a modular static

analysis and an analysis driver program.

Background ¶

A static analysis is a function that inspects a package of Go code and

reports a set of diagnostics (typically mistakes in the code), and

perhaps produces other results as well, such as suggested refactorings

or other facts. An analysis that reports mistakes is informally called a

"checker". For example, the printf checker reports mistakes in

fmt.Printf format strings.

A "modular" analysis is one that inspects one package at a time but can

save information from a lower-level package and use it when inspecting a

higher-level package, analogous to separate compilation in a toolchain.

The printf checker is modular: when it discovers that a function such as

log.Fatalf delegates to fmt.Printf, it records this fact, and checks

calls to that function too, including calls made from another package.

By implementing a common interface, checkers from a variety of sources

can be easily selected, incorporated, and reused in a wide range of

driver programs including command-line tools (such as vet), text editors and

IDEs, build and test systems (such as go build, Bazel, or Buck), test

frameworks, code review tools, code-base indexers (such as SourceGraph),

documentation viewers (such as godoc), batch pipelines for large code

bases, and so on.

Analyzer ¶

The primary type in the API is Analyzer. An Analyzer statically

describes an analysis function: its name, documentation, flags,

relationship to other analyzers, and of course, its logic.

Analyzer

To define an analysis, a user declares a (logically constant) variable

of type Analyzer. Here is a typical example from one of the analyzers in

the go/analysis/passes/ subdirectory:

An analysis driver is a program such as vet that runs a set of

analyses and prints the diagnostics that they report.

The driver program must import the list of Analyzers it needs.

Typically each Analyzer resides in a separate package.

To add a new Analyzer to an existing driver, add another item to the list:

A driver may use the name, flags, and documentation to provide on-line

help that describes the analyses it performs.

The doc comment contains a brief one-line summary,

optionally followed by paragraphs of explanation.

The Analyzer type has more fields besides those shown above:

Analyzer

The Flags field declares a set of named (global) flag variables that

control analysis behavior. Unlike vet, analysis flags are not declared

directly in the command line FlagSet; it is up to the driver to set the

flag variables. A driver for a single analysis, a, might expose its flag

f directly on the command line as -f, whereas a driver for multiple

analyses might prefix the flag name by the analysis name (-a.f) to avoid

ambiguity. An IDE might expose the flags through a graphical interface,

and a batch pipeline might configure them from a config file.

See the "findcall" analyzer for an example of flags in action.

The RunDespiteErrors flag indicates whether the analysis is equipped to

handle ill-typed code. If not, the driver will skip the analysis if

there were parse or type errors.

The optional ResultType field specifies the type of the result value

computed by this analysis and made available to other analyses.

The Requires field specifies a list of analyses upon which

this one depends and whose results it may access, and it constrains the

order in which a driver may run analyses.

The FactTypes field is discussed in the section on Modularity.

The analysis package provides a Validate function to perform basic

sanity checks on an Analyzer, such as that its Requires graph is

acyclic, its fact and result types are unique, and so on.

Finally, the Run field contains a function to be called by the driver to

execute the analysis on a single package. The driver passes it an

instance of the Pass type.

Pass ¶

A Pass describes a single unit of work: the application of a particular

Analyzer to a particular package of Go code.

The Pass provides information to the Analyzer's Run function about the

package being analyzed, and provides operations to the Run function for

reporting diagnostics and other information back to the driver.

Pass

The Fset, Files, Pkg, and TypesInfo fields provide the syntax trees,

type information, and source positions for a single package of Go code.

The OtherFiles field provides the names of non-Go

files such as assembly that are part of this package.

Similarly, the IgnoredFiles field provides the names of Go and non-Go

source files that are not part of this package with the current build

configuration but may be part of other build configurations.

The contents of these files may be read using Pass.ReadFile;

see the "asmdecl" or "buildtags" analyzers for examples of loading

non-Go files and reporting diagnostics against them.

The ResultOf field provides the results computed by the analyzers

required by this one, as expressed in its Analyzer.Requires field. The

driver runs the required analyzers first and makes their results

available in this map. Each Analyzer must return a value of the type

described in its Analyzer.ResultType field.

For example, the "ctrlflow" analyzer returns a *ctrlflow.CFGs, which

provides a control-flow graph for each function in the package (see

golang.org/x/tools/go/cfg); the "inspect" analyzer returns a value that

enables other Analyzers to traverse the syntax trees of the package more

efficiently; and the "buildssa" analyzer constructs an SSA-form

intermediate representation.

Each of these Analyzers extends the capabilities of later Analyzers

without adding a dependency to the core API, so an analysis tool pays

only for the extensions it needs.

The Report function emits a diagnostic, a message associated with a

source position. For most analyses, diagnostics are their primary

result.

For convenience, Pass provides a helper method, Reportf, to report a new

diagnostic by formatting a string.

Diagnostic is defined as:

The optional Category field is a short identifier that classifies the

kind of message when an analysis produces several kinds of diagnostic.

The Diagnostic struct does not have a field to indicate its severity

because opinions about the relative importance of Analyzers and their

diagnostics vary widely among users. The design of this framework does

not hold each Analyzer responsible for identifying the severity of its

diagnostics. Instead, we expect that drivers will allow the user to

customize the filtering and prioritization of diagnostics based on the

producing Analyzer and optional Category, according to the user's

preferences.

Diagnostic

Most Analyzers inspect typed Go syntax trees, but a few, such as asmdecl

and buildtag, inspect the raw text of Go source files or even non-Go

files such as assembly. To report a diagnostic against a line of a

raw text file, use the following sequence:

Modular analysis with Facts ¶

To improve efficiency and scalability, large programs are routinely

built using separate compilation: units of the program are compiled

separately, and recompiled only when one of their dependencies changes;

independent modules may be compiled in parallel. The same technique may

be applied to static analyses, for the same benefits. Such analyses are

described as "modular".

A compiler’s type checker is an example of a modular static analysis.

Many other checkers we would like to apply to Go programs can be

understood as alternative or non-standard type systems. For example,

vet's printf checker infers whether a function has the "printf wrapper"

type, and it applies stricter checks to calls of such functions. In

addition, it records which functions are printf wrappers for use by

later analysis passes to identify other printf wrappers by induction.

A result such as “f is a printf wrapper” that is not interesting by

itself but serves as a stepping stone to an interesting result (such as

a diagnostic) is called a Fact.

Fact

The analysis API allows an analysis to define new types of facts, to

associate facts of these types with objects (named entities) declared

within the current package, or with the package as a whole, and to query

for an existing fact of a given type associated with an object or

package.

An Analyzer that uses facts must declare their types:

The driver program ensures that facts for a pass’s dependencies are

generated before analyzing the package and is responsible for propagating

facts from one package to another, possibly across address spaces.

Consequently, Facts must be serializable. The API requires that drivers

use the gob encoding, an efficient, robust, self-describing binary

protocol. A fact type may implement the GobEncoder/GobDecoder interfaces

if the default encoding is unsuitable. Facts should be stateless.

Because serialized facts may appear within build outputs, the gob encoding

of a fact must be deterministic, to avoid spurious cache misses in

build systems that use content-addressable caches.

The driver makes a single call to the gob encoder for all facts

exported by a given analysis pass, so that the topology of

shared data structures referenced by multiple facts is preserved.

The Pass type has functions to import and export facts,

associated either with an object or with a package:

An Analyzer may only export facts associated with the current package or

its objects, though it may import facts from any package or object that

is an import dependency of the current package.

Conceptually, ExportObjectFact(obj, fact) inserts fact into a hidden map keyed by

the pair (obj, TypeOf(fact)), and the ImportObjectFact function

retrieves the entry from this map and copies its value into the variable

pointed to by fact. This scheme assumes that the concrete type of fact

is a pointer; this assumption is checked by the Validate function.

See the "printf" analyzer for an example of object facts in action.

Some driver implementations (such as those based on Bazel and Blaze) do

not currently apply analyzers to packages of the standard library.

Therefore, for best results, analyzer authors should not rely on

analysis facts being available for standard packages.

For example, although the printf checker is capable of deducing during

analysis of the log package that log.Printf is a printf wrapper,

this fact is built in to the analyzer so that it correctly checks

calls to log.Printf even when run in a driver that does not apply

it to standard packages. We would like to remove this limitation in future.

Testing an Analyzer ¶

The analysistest subpackage provides utilities for testing an Analyzer.

In a few lines of code, it is possible to run an analyzer on a package

of testdata files and check that it reported all the expected

diagnostics and facts (and no more). Expectations are expressed using

"// want ..." comments in the input code.

Standalone commands ¶

Analyzers are provided in the form of packages that a driver program is

expected to import. The vet command imports a set of several analyzers,

but users may wish to define their own analysis commands that perform

additional checks. To simplify the task of creating an analysis command,

either for a single analyzer or for a whole suite, we provide the

singlechecker and multichecker subpackages.

The singlechecker package provides the main function for a command that

runs one analyzer. By convention, each analyzer such as

go/analysis/passes/findcall should be accompanied by a singlechecker-based

command such as go/analysis/passes/findcall/cmd/findcall, defined in its

entirety as:

A tool that provides multiple analyzers can use multichecker in a

similar way, giving it the list of Analyzers.

Index ¶

func Validate(analyzers []*Analyzer) error

[func Validate(analyzers []*Analyzer) error](#Validate)

type Analyzer

type Analyzer

func (a *Analyzer) String() string

func (a *Analyzer) String() string

func (a *Analyzer) String() string

type CycleInRequiresGraphError

type CycleInRequiresGraphError

func (e *CycleInRequiresGraphError) Error() string

func (e *CycleInRequiresGraphError) Error() string

func (e *CycleInRequiresGraphError) Error() string

type Diagnostic

type Diagnostic

type Fact

type Fact

type Module

type Module

type ModuleError

type ModuleError

type ObjectFact

type ObjectFact

type PackageFact

type PackageFact

type Pass

type Pass

func (pass *Pass) ReportRangef(rng Range, format string, args ...any)

func (pass *Pass) Reportf(pos token.Pos, format string, args ...any)

func (pass *Pass) String() string

func (pass *Pass) ReportRangef(rng Range, format string, args ...any)

func (pass *Pass) ReportRangef(rng Range, format string, args ...any)

func (pass *Pass) Reportf(pos token.Pos, format string, args ...any)

func (pass *Pass) Reportf(pos token.Pos, format string, args ...any)

func (pass *Pass) String() string

func (pass *Pass) String() string

type Range

type Range

type RelatedInformation

type RelatedInformation

type SuggestedFix

type SuggestedFix

type TextEdit

type TextEdit

Constants ¶

This section is empty.

Variables ¶

This section is empty.

Functions ¶

func Validate ¶

Validate

Analyzer

error

Validate reports an error if any of the analyzers are misconfigured.

Checks include:

that the name is a valid identifier;

that the Doc is not empty;

that the Run is non-nil;

that the Requires graph is acyclic;

that analyzer fact types are unique;

that each fact type is a pointer.

Analyzer names need not be unique, though this may be confusing.

Types ¶

type Analyzer ¶

Analyzer

string

string

string

flag

FlagSet

Pass

any

error

bool

Analyzer

reflect

Type

Fact

An Analyzer describes an analysis function and its options.

func (*Analyzer) String ¶

String

Analyzer

string

type CycleInRequiresGraphError ¶

CycleInRequiresGraphError

string

bool

func (*CycleInRequiresGraphError) Error ¶

Error

CycleInRequiresGraphError

string

type Diagnostic ¶

Diagnostic

token

Pos

token

Pos

string

string

https://pkg.go.dev/net/url#URL.ResolveReference

string

SuggestedFix

RelatedInformation

A Diagnostic is a message associated with a source location or range.

An Analyzer may return a variety of diagnostics; the optional Category,

which should be a constant, may be used to classify them.

It is primarily intended to make it easy to look up documentation.

All Pos values are interpreted relative to Pass.Fset. If End is

provided, the diagnostic is specified to apply to the range between

Pos and End.

type Fact ¶

Fact

A Fact is an intermediate fact produced during analysis.

Each fact is associated with a named declaration (a types.Object) or

with a package as a whole. A single object or package may have

multiple associated facts, but only one of any particular fact type.

A Fact represents a predicate such as "never returns", but does not

represent the subject of the predicate such as "function F" or "package P".

Facts may be produced in one analysis pass and consumed by another

analysis pass even if these are in different address spaces.

If package P imports Q, all facts about Q produced during

analysis of that package will be available during later analysis of P.

Facts are analogous to type export data in a build system:

just as export data enables separate compilation of several passes,

facts enable "separate analysis".

Each pass (a, p) starts with the set of facts produced by the

same analyzer a applied to the packages directly imported by p.

The analysis may add facts to the set, and they may be exported in turn.

An analysis's Run function may retrieve facts by calling

Pass.Import{Object,Package}Fact and update them using

Pass.Export{Object,Package}Fact.

A fact is logically private to its Analysis. To pass values

between different analyzers, use the results mechanism;

see Analyzer.Requires, Analyzer.ResultType, and Pass.ResultOf.

A Fact type must be a pointer.

Facts are encoded and decoded using encoding/gob.

A Fact may implement the GobEncoder/GobDecoder interfaces

to customize its encoding. Fact encoding should not fail.

A Fact should not be modified once exported.

type Module ¶

added in

v0.24.0

Module

string

string

Module

time

Time

bool

bool

string

string

string

ModuleError

A Module describes the module to which a package belongs.

type ModuleError ¶

added in

v0.43.0

ModuleError

string

ModuleError holds errors loading a module.

type ObjectFact ¶

ObjectFact

types

Object

Fact

ObjectFact is an object together with an associated fact.

type PackageFact ¶

PackageFact

types

Package

Fact

PackageFact is a package together with an associated fact.

type Pass ¶

Pass

Analyzer

token

FileSet

ast

File

string

string

types

Package

types

Info

types

Sizes

types

Error

Module

Diagnostic

Analyzer

any

string

byte

error

types

Object

Fact

bool

types

Package

Fact

bool

types

Object

Fact

Fact

PackageFact

ObjectFact

A Pass provides information to the Run function that

applies a specific analyzer to a single Go package.

It forms the interface between the analysis logic and the driver

program, and has both input and an output components.

As in a compiler, one pass may depend on the result computed by another.

The Run function should not call any of the Pass functions concurrently.

func (*Pass) ReportRangef ¶

ReportRangef

Pass

Range

string

any

ReportRangef is a helper function that reports a Diagnostic using the

range provided. ast.Node values can be passed in as the range because

they satisfy the Range interface.

func (*Pass) Reportf ¶

Reportf

Pass

token

Pos

string

any

Reportf is a helper function that reports a Diagnostic using the

specified position and formatted error message.

func (*Pass) String ¶

String

Pass

string

type Range ¶

Range

token

Pos

token

Pos

The Range interface provides a range. It's equivalent to and satisfied by

ast.Node.

type RelatedInformation ¶

RelatedInformation

token

Pos

token

Pos

string

RelatedInformation contains information related to a diagnostic.

For example, a diagnostic that flags duplicated declarations of a

variable may include one RelatedInformation per existing

declaration.

type SuggestedFix ¶

SuggestedFix

string

TextEdit

A SuggestedFix is a code change associated with a Diagnostic that a

user can choose to apply to their code. Usually the SuggestedFix is

meant to fix the issue flagged by the diagnostic.

The TextEdits must not overlap, nor contain edits for other

packages. Edits need not be totally ordered, but the order

determines how insertions at the same point will be applied.

type TextEdit ¶

TextEdit

token

Pos

token

Pos

byte

A TextEdit represents the replacement of the code between Pos and End with the new text.

Each TextEdit should apply to a single file. End should not be earlier in the file than Pos.

Source Files

View all Source files

analysis.go

analysis.go

diagnostic.go

diagnostic.go

doc.go

doc.go

validate.go

validate.go

Directories

analysistest

checker

internal

analysisflags

checker

multichecker

appends

asmdecl

assign

atomic

atomicalign

bools

buildssa

buildtag

cgocall

composite

copylock

ctrlflow

deepequalerrors

defers

defers/cmd/defers

directive

errorsas

fieldalignment

fieldalignment/cmd/fieldalignment

findcall

findcall/cmd/findcall

framepointer

gofix

hostport

httpmux

httpmux/cmd/httpmux

httpresponse

ifaceassert

ifaceassert/cmd/ifaceassert

inline

inline/cmd/inline

inspect

internal/gofixdirective

loopclosure

lostcancel

lostcancel/cmd/lostcancel

modernize

modernize/cmd/modernize

nilfunc

nilness

nilness/cmd/nilness

pkgfact

printf

reflectvaluecompare

reflectvaluecompare/cmd/reflectvaluecompare

scannererr

shadow

shadow/cmd/shadow

shift

sigchanyzer

slog

sortslice

sqlrowserr

stdmethods

stdversion

stringintconv

stringintconv/cmd/stringintconv

structtag

testinggoroutine

tests

timeformat

unmarshal

unmarshal/cmd/unmarshal

unreachable

unsafeptr

unusedresult

unusedresult/cmd/unusedresult

unusedwrite

usesgenerics

waitgroup

singlechecker

fix

vet

unitchecker