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

View as plain text