GoPLS Viewer

Home|gopls/go/analysis/doc.go
1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5/*
6Package analysis defines the interface between a modular static
7analysis and an analysis driver program.
8
9# Background
10
11A static analysis is a function that inspects a package of Go code and
12reports a set of diagnostics (typically mistakes in the code), and
13perhaps produces other results as well, such as suggested refactorings
14or other facts. An analysis that reports mistakes is informally called a
15"checker". For example, the printf checker reports mistakes in
16fmt.Printf format strings.
17
18A "modular" analysis is one that inspects one package at a time but can
19save information from a lower-level package and use it when inspecting a
20higher-level package, analogous to separate compilation in a toolchain.
21The printf checker is modular: when it discovers that a function such as
22log.Fatalf delegates to fmt.Printf, it records this fact, and checks
23calls to that function too, including calls made from another package.
24
25By implementing a common interface, checkers from a variety of sources
26can be easily selected, incorporated, and reused in a wide range of
27driver programs including command-line tools (such as vet), text editors and
28IDEs, build and test systems (such as go build, Bazel, or Buck), test
29frameworks, code review tools, code-base indexers (such as SourceGraph),
30documentation viewers (such as godoc), batch pipelines for large code
31bases, and so on.
32
33# Analyzer
34
35The primary type in the API is Analyzer. An Analyzer statically
36describes an analysis function: its name, documentation, flags,
37relationship to other analyzers, and of course, its logic.
38
39To define an analysis, a user declares a (logically constant) variable
40of type Analyzer. Here is a typical example from one of the analyzers in
41the go/analysis/passes/ subdirectory:
42
43    package unusedresult
44
45    var Analyzer = &analysis.Analyzer{
46        Name: "unusedresult",
47        Doc:  "check for unused results of calls to some functions",
48        Run:  run,
49        ...
50    }
51
52    func run(pass *analysis.Pass) (interface{}, error) {
53        ...
54    }
55
56An analysis driver is a program such as vet that runs a set of
57analyses and prints the diagnostics that they report.
58The driver program must import the list of Analyzers it needs.
59Typically each Analyzer resides in a separate package.
60To add a new Analyzer to an existing driver, add another item to the list:
61
62    import ( "unusedresult"; "nilness"; "printf" )
63
64    var analyses = []*analysis.Analyzer{
65        unusedresult.Analyzer,
66        nilness.Analyzer,
67        printf.Analyzer,
68    }
69
70A driver may use the name, flags, and documentation to provide on-line
71help that describes the analyses it performs.
72The doc comment contains a brief one-line summary,
73optionally followed by paragraphs of explanation.
74
75The Analyzer type has more fields besides those shown above:
76
77    type Analyzer struct {
78        Name             string
79        Doc              string
80        Flags            flag.FlagSet
81        Run              func(*Pass) (interface{}, error)
82        RunDespiteErrors bool
83        ResultType       reflect.Type
84        Requires         []*Analyzer
85        FactTypes        []Fact
86    }
87
88The Flags field declares a set of named (global) flag variables that
89control analysis behavior. Unlike vet, analysis flags are not declared
90directly in the command line FlagSet; it is up to the driver to set the
91flag variables. A driver for a single analysis, a, might expose its flag
92f directly on the command line as -f, whereas a driver for multiple
93analyses might prefix the flag name by the analysis name (-a.f) to avoid
94ambiguity. An IDE might expose the flags through a graphical interface,
95and a batch pipeline might configure them from a config file.
96See the "findcall" analyzer for an example of flags in action.
97
98The RunDespiteErrors flag indicates whether the analysis is equipped to
99handle ill-typed code. If not, the driver will skip the analysis if
100there were parse or type errors.
101The optional ResultType field specifies the type of the result value
102computed by this analysis and made available to other analyses.
103The Requires field specifies a list of analyses upon which
104this one depends and whose results it may access, and it constrains the
105order in which a driver may run analyses.
106The FactTypes field is discussed in the section on Modularity.
107The analysis package provides a Validate function to perform basic
108sanity checks on an Analyzer, such as that its Requires graph is
109acyclic, its fact and result types are unique, and so on.
110
111Finally, the Run field contains a function to be called by the driver to
112execute the analysis on a single package. The driver passes it an
113instance of the Pass type.
114
115# Pass
116
117A Pass describes a single unit of work: the application of a particular
118Analyzer to a particular package of Go code.
119The Pass provides information to the Analyzer's Run function about the
120package being analyzed, and provides operations to the Run function for
121reporting diagnostics and other information back to the driver.
122
123    type Pass struct {
124        Fset         *token.FileSet
125        Files        []*ast.File
126        OtherFiles   []string
127        IgnoredFiles []string
128        Pkg          *types.Package
129        TypesInfo    *types.Info
130        ResultOf     map[*Analyzer]interface{}
131        Report       func(Diagnostic)
132        ...
133    }
134
135The Fset, Files, Pkg, and TypesInfo fields provide the syntax trees,
136type information, and source positions for a single package of Go code.
137
138The OtherFiles field provides the names, but not the contents, of non-Go
139files such as assembly that are part of this package. See the "asmdecl"
140or "buildtags" analyzers for examples of loading non-Go files and reporting
141diagnostics against them.
142
143The IgnoredFiles field provides the names, but not the contents,
144of ignored Go and non-Go source files that are not part of this package
145with the current build configuration but may be part of other build
146configurations. See the "buildtags" analyzer for an example of loading
147and checking IgnoredFiles.
148
149The ResultOf field provides the results computed by the analyzers
150required by this one, as expressed in its Analyzer.Requires field. The
151driver runs the required analyzers first and makes their results
152available in this map. Each Analyzer must return a value of the type
153described in its Analyzer.ResultType field.
154For example, the "ctrlflow" analyzer returns a *ctrlflow.CFGs, which
155provides a control-flow graph for each function in the package (see
156golang.org/x/tools/go/cfg); the "inspect" analyzer returns a value that
157enables other Analyzers to traverse the syntax trees of the package more
158efficiently; and the "buildssa" analyzer constructs an SSA-form
159intermediate representation.
160Each of these Analyzers extends the capabilities of later Analyzers
161without adding a dependency to the core API, so an analysis tool pays
162only for the extensions it needs.
163
164The Report function emits a diagnostic, a message associated with a
165source position. For most analyses, diagnostics are their primary
166result.
167For convenience, Pass provides a helper method, Reportf, to report a new
168diagnostic by formatting a string.
169Diagnostic is defined as:
170
171    type Diagnostic struct {
172        Pos      token.Pos
173        Category string // optional
174        Message  string
175    }
176
177The optional Category field is a short identifier that classifies the
178kind of message when an analysis produces several kinds of diagnostic.
179
180The Diagnostic struct does not have a field to indicate its severity
181because opinions about the relative importance of Analyzers and their
182diagnostics vary widely among users. The design of this framework does
183not hold each Analyzer responsible for identifying the severity of its
184diagnostics. Instead, we expect that drivers will allow the user to
185customize the filtering and prioritization of diagnostics based on the
186producing Analyzer and optional Category, according to the user's
187preferences.
188
189Most Analyzers inspect typed Go syntax trees, but a few, such as asmdecl
190and buildtag, inspect the raw text of Go source files or even non-Go
191files such as assembly. To report a diagnostic against a line of a
192raw text file, use the following sequence:
193
194    content, err := ioutil.ReadFile(filename)
195    if err != nil { ... }
196    tf := fset.AddFile(filename, -1, len(content))
197    tf.SetLinesForContent(content)
198    ...
199    pass.Reportf(tf.LineStart(line), "oops")
200
201# Modular analysis with Facts
202
203To improve efficiency and scalability, large programs are routinely
204built using separate compilation: units of the program are compiled
205separately, and recompiled only when one of their dependencies changes;
206independent modules may be compiled in parallel. The same technique may
207be applied to static analyses, for the same benefits. Such analyses are
208described as "modular".
209
210A compiler’s type checker is an example of a modular static analysis.
211Many other checkers we would like to apply to Go programs can be
212understood as alternative or non-standard type systems. For example,
213vet's printf checker infers whether a function has the "printf wrapper"
214type, and it applies stricter checks to calls of such functions. In
215addition, it records which functions are printf wrappers for use by
216later analysis passes to identify other printf wrappers by induction.
217A result such as “f is a printf wrapper” that is not interesting by
218itself but serves as a stepping stone to an interesting result (such as
219a diagnostic) is called a "fact".
220
221The analysis API allows an analysis to define new types of facts, to
222associate facts of these types with objects (named entities) declared
223within the current package, or with the package as a whole, and to query
224for an existing fact of a given type associated with an object or
225package.
226
227An Analyzer that uses facts must declare their types:
228
229    var Analyzer = &analysis.Analyzer{
230        Name:      "printf",
231        FactTypes: []analysis.Fact{new(isWrapper)},
232        ...
233    }
234
235    type isWrapper struct{} // => *types.Func f “is a printf wrapper”
236
237The driver program ensures that facts for a pass’s dependencies are
238generated before analyzing the package and is responsible for propagating
239facts from one package to another, possibly across address spaces.
240Consequently, Facts must be serializable. The API requires that drivers
241use the gob encoding, an efficient, robust, self-describing binary
242protocol. A fact type may implement the GobEncoder/GobDecoder interfaces
243if the default encoding is unsuitable. Facts should be stateless.
244Because serialized facts may appear within build outputs, the gob encoding
245of a fact must be deterministic, to avoid spurious cache misses in
246build systems that use content-addressable caches.
247The driver makes a single call to the gob encoder for all facts
248exported by a given analysis pass, so that the topology of
249shared data structures referenced by multiple facts is preserved.
250
251The Pass type has functions to import and export facts,
252associated either with an object or with a package:
253
254    type Pass struct {
255        ...
256        ExportObjectFact func(types.Object, Fact)
257        ImportObjectFact func(types.Object, Fact) bool
258
259        ExportPackageFact func(fact Fact)
260        ImportPackageFact func(*types.Package, Fact) bool
261    }
262
263An Analyzer may only export facts associated with the current package or
264its objects, though it may import facts from any package or object that
265is an import dependency of the current package.
266
267Conceptually, ExportObjectFact(obj, fact) inserts fact into a hidden map keyed by
268the pair (obj, TypeOf(fact)), and the ImportObjectFact function
269retrieves the entry from this map and copies its value into the variable
270pointed to by fact. This scheme assumes that the concrete type of fact
271is a pointer; this assumption is checked by the Validate function.
272See the "printf" analyzer for an example of object facts in action.
273
274Some driver implementations (such as those based on Bazel and Blaze) do
275not currently apply analyzers to packages of the standard library.
276Therefore, for best results, analyzer authors should not rely on
277analysis facts being available for standard packages.
278For example, although the printf checker is capable of deducing during
279analysis of the log package that log.Printf is a printf wrapper,
280this fact is built in to the analyzer so that it correctly checks
281calls to log.Printf even when run in a driver that does not apply
282it to standard packages. We would like to remove this limitation in future.
283
284# Testing an Analyzer
285
286The analysistest subpackage provides utilities for testing an Analyzer.
287In a few lines of code, it is possible to run an analyzer on a package
288of testdata files and check that it reported all the expected
289diagnostics and facts (and no more). Expectations are expressed using
290"// want ..." comments in the input code.
291
292# Standalone commands
293
294Analyzers are provided in the form of packages that a driver program is
295expected to import. The vet command imports a set of several analyzers,
296but users may wish to define their own analysis commands that perform
297additional checks. To simplify the task of creating an analysis command,
298either for a single analyzer or for a whole suite, we provide the
299singlechecker and multichecker subpackages.
300
301The singlechecker package provides the main function for a command that
302runs one analyzer. By convention, each analyzer such as
303go/analysis/passes/findcall should be accompanied by a singlechecker-based
304command such as go/analysis/passes/findcall/cmd/findcall, defined in its
305entirety as:
306
307    package main
308
309    import (
310        "golang.org/x/tools/go/analysis/passes/findcall"
311        "golang.org/x/tools/go/analysis/singlechecker"
312    )
313
314    func main() { singlechecker.Main(findcall.Analyzer) }
315
316A tool that provides multiple analyzers can use multichecker in a
317similar way, giving it the list of Analyzers.
318*/
319package analysis
320
MembersX
Members
X