summaryrefslogtreecommitdiff
path: root/src/cmd/vendor/golang.org/x/tools/go/analysis/doc.go
blob: 7679bda435989edcbb01b0a0f256ecbc6a46b5ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
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.

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:

	package unusedresult

	var Analyzer = &analysis.Analyzer{
		Name: "unusedresult",
		Doc:  "check for unused results of calls to some functions",
		Run:  run,
		...
	}

	func run(pass *analysis.Pass) (interface{}, error) {
		...
	}

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:

	import ( "unusedresult"; "nilness"; "printf" )

	var analyses = []*analysis.Analyzer{
		unusedresult.Analyzer,
		nilness.Analyzer,
		printf.Analyzer,
	}

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:

	type Analyzer struct {
		Name             string
		Doc              string
		Flags            flag.FlagSet
		Run              func(*Pass) (interface{}, error)
		RunDespiteErrors bool
		ResultType       reflect.Type
		Requires         []*Analyzer
		FactTypes        []Fact
	}

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.

	type Pass struct {
		Fset         *token.FileSet
		Files        []*ast.File
		OtherFiles   []string
		IgnoredFiles []string
		Pkg          *types.Package
		TypesInfo    *types.Info
		ResultOf     map[*Analyzer]interface{}
		Report       func(Diagnostic)
		...
	}

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, but not the contents, of non-Go
files such as assembly that are part of this package. See the "asmdecl"
or "buildtags" analyzers for examples of loading non-Go files and reporting
diagnostics against them.

The IgnoredFiles field provides the names, but not the contents,
of ignored 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. See the "buildtags" analyzer for an example of loading
and checking IgnoredFiles.

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:

	type Diagnostic struct {
		Pos      token.Pos
		Category string // optional
		Message  string
	}

The optional Category field is a short identifier that classifies the
kind of message when an analysis produces several kinds of diagnostic.

Many analyses want to associate diagnostics with a severity level.
Because Diagnostic does not have a severity level field, an Analyzer's
diagnostics effectively all have the same severity level. To separate which
diagnostics are high severity and which are low severity, expose multiple
Analyzers instead. Analyzers should also be separated when their
diagnostics belong in different groups, or could be tagged differently
before being shown to the end user. Analyzers should document their severity
level to help downstream tools surface diagnostics properly.

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:

	content, err := ioutil.ReadFile(filename)
	if err != nil { ... }
	tf := fset.AddFile(filename, -1, len(content))
	tf.SetLinesForContent(content)
	...
	pass.Reportf(tf.LineStart(line), "oops")

# 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".

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:

	var Analyzer = &analysis.Analyzer{
		Name:      "printf",
		FactTypes: []analysis.Fact{new(isWrapper)},
		...
	}

	type isWrapper struct{} // => *types.Func f “is a printf wrapper”

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.

The Pass type has functions to import and export facts,
associated either with an object or with a package:

	type Pass struct {
		...
		ExportObjectFact func(types.Object, Fact)
		ImportObjectFact func(types.Object, Fact) bool

		ExportPackageFact func(fact Fact)
		ImportPackageFact func(*types.Package, Fact) bool
	}

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/passes/findcall should be accompanied by a singlechecker-based
command such as go/analysis/passes/findcall/cmd/findcall, defined in its
entirety as:

	package main

	import (
		"golang.org/x/tools/go/analysis/passes/findcall"
		"golang.org/x/tools/go/analysis/singlechecker"
	)

	func main() { singlechecker.Main(findcall.Analyzer) }

A tool that provides multiple analyzers can use multichecker in a
similar way, giving it the list of Analyzers.
*/
package analysis