analysis.go raw

   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  package analysis
   6  
   7  import (
   8  	"flag"
   9  	"fmt"
  10  	"go/ast"
  11  	"go/token"
  12  	"go/types"
  13  	"reflect"
  14  )
  15  
  16  // An Analyzer describes an analysis function and its options.
  17  type Analyzer struct {
  18  	// The Name of the analyzer must be a valid Go identifier
  19  	// as it may appear in command-line flags, URLs, and so on.
  20  	Name string
  21  
  22  	// Doc is the documentation for the analyzer.
  23  	// The part before the first "\n\n" is the title
  24  	// (no capital or period, max ~60 letters).
  25  	Doc string
  26  
  27  	// URL holds an optional link to a web page with additional
  28  	// documentation for this analyzer.
  29  	URL string
  30  
  31  	// Flags defines any flags accepted by the analyzer.
  32  	// The manner in which these flags are exposed to the user
  33  	// depends on the driver which runs the analyzer.
  34  	Flags flag.FlagSet
  35  
  36  	// Run applies the analyzer to a package.
  37  	// It returns an error if the analyzer failed.
  38  	//
  39  	// On success, the Run function may return a result
  40  	// computed by the Analyzer; its type must match ResultType.
  41  	// The driver makes this result available as an input to
  42  	// another Analyzer that depends directly on this one (see
  43  	// Requires) when it analyzes the same package.
  44  	//
  45  	// To pass analysis results between packages (and thus
  46  	// potentially between address spaces), use Facts, which are
  47  	// serializable.
  48  	Run func(*Pass) (any, error)
  49  
  50  	// RunDespiteErrors allows the driver to invoke
  51  	// the Run method of this analyzer even on a
  52  	// package that contains parse or type errors.
  53  	// The [Pass.TypeErrors] field may consequently be non-empty.
  54  	RunDespiteErrors bool
  55  
  56  	// Requires is a set of analyzers that must run successfully
  57  	// before this one on a given package. This analyzer may inspect
  58  	// the outputs produced by each analyzer in Requires.
  59  	// The graph over analyzers implied by Requires edges must be acyclic.
  60  	//
  61  	// Requires establishes a "horizontal" dependency between
  62  	// analysis passes (different analyzers, same package).
  63  	Requires []*Analyzer
  64  
  65  	// ResultType is the type of the optional result of the Run function.
  66  	ResultType reflect.Type
  67  
  68  	// FactTypes indicates that this analyzer imports and exports
  69  	// Facts of the specified concrete types.
  70  	// An analyzer that uses facts may assume that its import
  71  	// dependencies have been similarly analyzed before it runs.
  72  	// Facts must be pointers.
  73  	//
  74  	// FactTypes establishes a "vertical" dependency between
  75  	// analysis passes (same analyzer, different packages).
  76  	FactTypes []Fact
  77  }
  78  
  79  func (a *Analyzer) String() string { return a.Name }
  80  
  81  // A Pass provides information to the Run function that
  82  // applies a specific analyzer to a single Go package.
  83  //
  84  // It forms the interface between the analysis logic and the driver
  85  // program, and has both input and an output components.
  86  //
  87  // As in a compiler, one pass may depend on the result computed by another.
  88  //
  89  // The Run function should not call any of the Pass functions concurrently.
  90  type Pass struct {
  91  	Analyzer *Analyzer // the identity of the current analyzer
  92  
  93  	// syntax and type information
  94  	Fset         *token.FileSet // file position information; Run may add new files
  95  	Files        []*ast.File    // the abstract syntax tree of each file
  96  	OtherFiles   []string       // names of non-Go files of this package
  97  	IgnoredFiles []string       // names of ignored source files in this package
  98  	Pkg          *types.Package // type information about the package
  99  	TypesInfo    *types.Info    // type information about the syntax trees
 100  	TypesSizes   types.Sizes    // function for computing sizes of types
 101  	TypeErrors   []types.Error  // type errors (only if Analyzer.RunDespiteErrors)
 102  
 103  	Module *Module // the package's enclosing module (possibly nil in some drivers)
 104  
 105  	// Report reports a Diagnostic, a finding about a specific location
 106  	// in the analyzed source code such as a potential mistake.
 107  	// It may be called by the Run function.
 108  	Report func(Diagnostic)
 109  
 110  	// ResultOf provides the inputs to this analysis pass, which are
 111  	// the corresponding results of its prerequisite analyzers.
 112  	// The map keys are the elements of Analysis.Required,
 113  	// and the type of each corresponding value is the required
 114  	// analysis's ResultType.
 115  	ResultOf map[*Analyzer]any
 116  
 117  	// ReadFile returns the contents of the named file.
 118  	//
 119  	// The only valid file names are the elements of OtherFiles
 120  	// and IgnoredFiles, and names returned by
 121  	// Fset.File(f.FileStart).Name() for each f in Files.
 122  	//
 123  	// Analyzers must use this function (if provided) instead of
 124  	// accessing the file system directly. This allows a driver to
 125  	// provide a virtualized file tree (including, for example,
 126  	// unsaved editor buffers) and to track dependencies precisely
 127  	// to avoid unnecessary recomputation.
 128  	ReadFile func(filename string) ([]byte, error)
 129  
 130  	// -- facts --
 131  
 132  	// ImportObjectFact retrieves a fact associated with obj.
 133  	// Given a value ptr of type *T, where *T satisfies Fact,
 134  	// ImportObjectFact copies the value to *ptr.
 135  	//
 136  	// ImportObjectFact panics if called after the pass is complete.
 137  	// ImportObjectFact is not concurrency-safe.
 138  	ImportObjectFact func(obj types.Object, fact Fact) bool
 139  
 140  	// ImportPackageFact retrieves a fact associated with package pkg,
 141  	// which must be this package or one of its dependencies.
 142  	// See comments for ImportObjectFact.
 143  	ImportPackageFact func(pkg *types.Package, fact Fact) bool
 144  
 145  	// ExportObjectFact associates a fact of type *T with the obj,
 146  	// replacing any previous fact of that type.
 147  	//
 148  	// ExportObjectFact panics if it is called after the pass is
 149  	// complete, or if obj does not belong to the package being analyzed.
 150  	// ExportObjectFact is not concurrency-safe.
 151  	ExportObjectFact func(obj types.Object, fact Fact)
 152  
 153  	// ExportPackageFact associates a fact with the current package.
 154  	// See comments for ExportObjectFact.
 155  	ExportPackageFact func(fact Fact)
 156  
 157  	// AllPackageFacts returns a new slice containing all package
 158  	// facts of the analysis's FactTypes in unspecified order.
 159  	// See comments for AllObjectFacts.
 160  	AllPackageFacts func() []PackageFact
 161  
 162  	// AllObjectFacts returns a new slice containing all object
 163  	// facts of the analysis's FactTypes in unspecified order.
 164  	//
 165  	// The result includes all facts exported by packages
 166  	// whose symbols are referenced by the current package
 167  	// (by qualified identifiers or field/method selections).
 168  	// And it includes all facts exported from the current
 169  	// package by the current analysis pass.
 170  	AllObjectFacts func() []ObjectFact
 171  
 172  	/* Further fields may be added in future. */
 173  }
 174  
 175  // PackageFact is a package together with an associated fact.
 176  type PackageFact struct {
 177  	Package *types.Package
 178  	Fact    Fact
 179  }
 180  
 181  // ObjectFact is an object together with an associated fact.
 182  type ObjectFact struct {
 183  	Object types.Object
 184  	Fact   Fact
 185  }
 186  
 187  // Reportf is a helper function that reports a Diagnostic using the
 188  // specified position and formatted error message.
 189  func (pass *Pass) Reportf(pos token.Pos, format string, args ...any) {
 190  	msg := fmt.Sprintf(format, args...)
 191  	pass.Report(Diagnostic{Pos: pos, Message: msg})
 192  }
 193  
 194  // The Range interface provides a range. It's equivalent to and satisfied by
 195  // ast.Node.
 196  type Range interface {
 197  	Pos() token.Pos // position of first character belonging to the node
 198  	End() token.Pos // position of first character immediately after the node
 199  }
 200  
 201  // ReportRangef is a helper function that reports a Diagnostic using the
 202  // range provided. ast.Node values can be passed in as the range because
 203  // they satisfy the Range interface.
 204  func (pass *Pass) ReportRangef(rng Range, format string, args ...any) {
 205  	msg := fmt.Sprintf(format, args...)
 206  	pass.Report(Diagnostic{Pos: rng.Pos(), End: rng.End(), Message: msg})
 207  }
 208  
 209  func (pass *Pass) String() string {
 210  	return fmt.Sprintf("%s@%s", pass.Analyzer.Name, pass.Pkg.Path())
 211  }
 212  
 213  // A Fact is an intermediate fact produced during analysis.
 214  //
 215  // Each fact is associated with a named declaration (a types.Object) or
 216  // with a package as a whole. A single object or package may have
 217  // multiple associated facts, but only one of any particular fact type.
 218  //
 219  // A Fact represents a predicate such as "never returns", but does not
 220  // represent the subject of the predicate such as "function F" or "package P".
 221  //
 222  // Facts may be produced in one analysis pass and consumed by another
 223  // analysis pass even if these are in different address spaces.
 224  // If package P imports Q, all facts about Q produced during
 225  // analysis of that package will be available during later analysis of P.
 226  // Facts are analogous to type export data in a build system:
 227  // just as export data enables separate compilation of several passes,
 228  // facts enable "separate analysis".
 229  //
 230  // Each pass (a, p) starts with the set of facts produced by the
 231  // same analyzer a applied to the packages directly imported by p.
 232  // The analysis may add facts to the set, and they may be exported in turn.
 233  // An analysis's Run function may retrieve facts by calling
 234  // Pass.Import{Object,Package}Fact and update them using
 235  // Pass.Export{Object,Package}Fact.
 236  //
 237  // A fact is logically private to its Analysis. To pass values
 238  // between different analyzers, use the results mechanism;
 239  // see Analyzer.Requires, Analyzer.ResultType, and Pass.ResultOf.
 240  //
 241  // A Fact type must be a pointer.
 242  // Facts are encoded and decoded using encoding/gob.
 243  // A Fact may implement the GobEncoder/GobDecoder interfaces
 244  // to customize its encoding. Fact encoding should not fail.
 245  //
 246  // A Fact should not be modified once exported.
 247  type Fact interface {
 248  	AFact() // dummy method to avoid type errors
 249  }
 250  
 251  // A Module describes the module to which a package belongs.
 252  type Module struct {
 253  	Path      string // module path
 254  	Version   string // module version ("" if unknown, such as for workspace modules)
 255  	GoVersion string // go version used in module (e.g. "go1.22.0")
 256  }
 257