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