Add staticcheck tool
This commit is contained in:
269
vendor/golang.org/x/tools/go/analysis/analysis.go
generated
vendored
Normal file
269
vendor/golang.org/x/tools/go/analysis/analysis.go
generated
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// An Analyzer describes an analysis function and its options.
|
||||
type Analyzer struct {
|
||||
// The Name of the analyzer must be a valid Go identifier
|
||||
// as it may appear in command-line flags, URLs, and so on.
|
||||
Name string
|
||||
|
||||
// Doc is the documentation for the analyzer.
|
||||
// The part before the first "\n\n" is the title
|
||||
// (no capital or period, max ~60 letters).
|
||||
Doc string
|
||||
|
||||
// URL holds an optional link to a web page with additional
|
||||
// documentation for this analyzer.
|
||||
URL string
|
||||
|
||||
// Flags defines any flags accepted by the analyzer.
|
||||
// The manner in which these flags are exposed to the user
|
||||
// depends on the driver which runs the analyzer.
|
||||
Flags flag.FlagSet
|
||||
|
||||
// Run applies the analyzer to a package.
|
||||
// It returns an error if the analyzer failed.
|
||||
//
|
||||
// On success, the Run function may return a result
|
||||
// computed by the Analyzer; its type must match ResultType.
|
||||
// The driver makes this result available as an input to
|
||||
// another Analyzer that depends directly on this one (see
|
||||
// Requires) when it analyzes the same package.
|
||||
//
|
||||
// To pass analysis results between packages (and thus
|
||||
// potentially between address spaces), use Facts, which are
|
||||
// serializable.
|
||||
Run func(*Pass) (any, error)
|
||||
|
||||
// RunDespiteErrors allows the driver to invoke
|
||||
// the Run method of this analyzer even on a
|
||||
// package that contains parse or type errors.
|
||||
// The [Pass.TypeErrors] field may consequently be non-empty.
|
||||
RunDespiteErrors bool
|
||||
|
||||
// Requires is a set of analyzers that must run successfully
|
||||
// before this one on a given package. This analyzer may inspect
|
||||
// the outputs produced by each analyzer in Requires.
|
||||
// The graph over analyzers implied by Requires edges must be acyclic.
|
||||
//
|
||||
// Requires establishes a "horizontal" dependency between
|
||||
// analysis passes (different analyzers, same package).
|
||||
Requires []*Analyzer
|
||||
|
||||
// ResultType is the type of the optional result of the Run function.
|
||||
ResultType reflect.Type
|
||||
|
||||
// FactTypes indicates that this analyzer imports and exports
|
||||
// Facts of the specified concrete types.
|
||||
// An analyzer that uses facts may assume that its import
|
||||
// dependencies have been similarly analyzed before it runs.
|
||||
// Facts must be pointers.
|
||||
//
|
||||
// FactTypes establishes a "vertical" dependency between
|
||||
// analysis passes (same analyzer, different packages).
|
||||
FactTypes []Fact
|
||||
}
|
||||
|
||||
func (a *Analyzer) String() string { return a.Name }
|
||||
|
||||
// A Pass provides information to the Run function that
|
||||
// applies a specific analyzer to a single Go package.
|
||||
//
|
||||
// It forms the interface between the analysis logic and the driver
|
||||
// program, and has both input and an output components.
|
||||
//
|
||||
// As in a compiler, one pass may depend on the result computed by another.
|
||||
//
|
||||
// The Run function should not call any of the Pass functions concurrently.
|
||||
type Pass struct {
|
||||
Analyzer *Analyzer // the identity of the current analyzer
|
||||
|
||||
// syntax and type information
|
||||
Fset *token.FileSet // file position information; Run may add new files
|
||||
Files []*ast.File // the abstract syntax tree of each file
|
||||
OtherFiles []string // names of non-Go files of this package
|
||||
IgnoredFiles []string // names of ignored source files in this package
|
||||
Pkg *types.Package // type information about the package
|
||||
TypesInfo *types.Info // type information about the syntax trees
|
||||
TypesSizes types.Sizes // function for computing sizes of types
|
||||
TypeErrors []types.Error // type errors (only if Analyzer.RunDespiteErrors)
|
||||
|
||||
Module *Module // the package's enclosing module (possibly nil in some drivers)
|
||||
|
||||
// Report reports a Diagnostic, a finding about a specific location
|
||||
// in the analyzed source code such as a potential mistake.
|
||||
// It may be called by the Run function.
|
||||
Report func(Diagnostic)
|
||||
|
||||
// ResultOf provides the inputs to this analysis pass, which are
|
||||
// the corresponding results of its prerequisite analyzers.
|
||||
// The map keys are the elements of Analysis.Required,
|
||||
// and the type of each corresponding value is the required
|
||||
// analysis's ResultType.
|
||||
ResultOf map[*Analyzer]any
|
||||
|
||||
// ReadFile returns the contents of the named file.
|
||||
//
|
||||
// The only valid file names are the elements of OtherFiles
|
||||
// and IgnoredFiles, and names returned by
|
||||
// Fset.File(f.FileStart).Name() for each f in Files.
|
||||
//
|
||||
// Analyzers must use this function (if provided) instead of
|
||||
// accessing the file system directly. This allows a driver to
|
||||
// provide a virtualized file tree (including, for example,
|
||||
// unsaved editor buffers) and to track dependencies precisely
|
||||
// to avoid unnecessary recomputation.
|
||||
ReadFile func(filename string) ([]byte, error)
|
||||
|
||||
// -- facts --
|
||||
|
||||
// ImportObjectFact retrieves a fact associated with obj.
|
||||
// Given a value ptr of type *T, where *T satisfies Fact,
|
||||
// ImportObjectFact copies the value to *ptr.
|
||||
//
|
||||
// ImportObjectFact panics if called after the pass is complete.
|
||||
// ImportObjectFact is not concurrency-safe.
|
||||
ImportObjectFact func(obj types.Object, fact Fact) bool
|
||||
|
||||
// ImportPackageFact retrieves a fact associated with package pkg,
|
||||
// which must be this package or one of its dependencies.
|
||||
// See comments for ImportObjectFact.
|
||||
ImportPackageFact func(pkg *types.Package, fact Fact) bool
|
||||
|
||||
// ExportObjectFact associates a fact of type *T with the obj,
|
||||
// replacing any previous fact of that type.
|
||||
//
|
||||
// ExportObjectFact panics if it is called after the pass is
|
||||
// complete, or if obj does not belong to the package being analyzed.
|
||||
// ExportObjectFact is not concurrency-safe.
|
||||
ExportObjectFact func(obj types.Object, fact Fact)
|
||||
|
||||
// ExportPackageFact associates a fact with the current package.
|
||||
// See comments for ExportObjectFact.
|
||||
ExportPackageFact func(fact Fact)
|
||||
|
||||
// AllPackageFacts returns a new slice containing all package
|
||||
// facts of the analysis's FactTypes in unspecified order.
|
||||
// See comments for AllObjectFacts.
|
||||
AllPackageFacts func() []PackageFact
|
||||
|
||||
// AllObjectFacts returns a new slice containing all object
|
||||
// facts of the analysis's FactTypes in unspecified order.
|
||||
//
|
||||
// The result includes all facts exported by packages
|
||||
// whose symbols are referenced by the current package
|
||||
// (by qualified identifiers or field/method selections).
|
||||
// And it includes all facts exported from the current
|
||||
// package by the current analysis pass.
|
||||
AllObjectFacts func() []ObjectFact
|
||||
|
||||
/* Further fields may be added in future. */
|
||||
}
|
||||
|
||||
// PackageFact is a package together with an associated fact.
|
||||
type PackageFact struct {
|
||||
Package *types.Package
|
||||
Fact Fact
|
||||
}
|
||||
|
||||
// ObjectFact is an object together with an associated fact.
|
||||
type ObjectFact struct {
|
||||
Object types.Object
|
||||
Fact Fact
|
||||
}
|
||||
|
||||
// Reportf is a helper function that reports a Diagnostic using the
|
||||
// specified position and formatted error message.
|
||||
func (pass *Pass) Reportf(pos token.Pos, format string, args ...any) {
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
pass.Report(Diagnostic{Pos: pos, Message: msg})
|
||||
}
|
||||
|
||||
// The Range interface provides a range. It's equivalent to and satisfied by
|
||||
// ast.Node.
|
||||
type Range interface {
|
||||
Pos() token.Pos // position of first character belonging to the node
|
||||
End() token.Pos // position of first character immediately after the node
|
||||
}
|
||||
|
||||
// ReportRangef is a helper function that reports a Diagnostic using the
|
||||
// range provided. ast.Node values can be passed in as the range because
|
||||
// they satisfy the Range interface.
|
||||
func (pass *Pass) ReportRangef(rng Range, format string, args ...any) {
|
||||
msg := fmt.Sprintf(format, args...)
|
||||
pass.Report(Diagnostic{Pos: rng.Pos(), End: rng.End(), Message: msg})
|
||||
}
|
||||
|
||||
func (pass *Pass) String() string {
|
||||
return fmt.Sprintf("%s@%s", pass.Analyzer.Name, pass.Pkg.Path())
|
||||
}
|
||||
|
||||
// A Fact is an intermediate fact produced during analysis.
|
||||
//
|
||||
// Each fact is associated with a named declaration (a types.Object) or
|
||||
// with a package as a whole. A single object or package may have
|
||||
// multiple associated facts, but only one of any particular fact type.
|
||||
//
|
||||
// A Fact represents a predicate such as "never returns", but does not
|
||||
// represent the subject of the predicate such as "function F" or "package P".
|
||||
//
|
||||
// Facts may be produced in one analysis pass and consumed by another
|
||||
// analysis pass even if these are in different address spaces.
|
||||
// If package P imports Q, all facts about Q produced during
|
||||
// analysis of that package will be available during later analysis of P.
|
||||
// Facts are analogous to type export data in a build system:
|
||||
// just as export data enables separate compilation of several passes,
|
||||
// facts enable "separate analysis".
|
||||
//
|
||||
// Each pass (a, p) starts with the set of facts produced by the
|
||||
// same analyzer a applied to the packages directly imported by p.
|
||||
// The analysis may add facts to the set, and they may be exported in turn.
|
||||
// An analysis's Run function may retrieve facts by calling
|
||||
// Pass.Import{Object,Package}Fact and update them using
|
||||
// Pass.Export{Object,Package}Fact.
|
||||
//
|
||||
// A fact is logically private to its Analysis. To pass values
|
||||
// between different analyzers, use the results mechanism;
|
||||
// see Analyzer.Requires, Analyzer.ResultType, and Pass.ResultOf.
|
||||
//
|
||||
// A Fact type must be a pointer.
|
||||
// Facts are encoded and decoded using encoding/gob.
|
||||
// A Fact may implement the GobEncoder/GobDecoder interfaces
|
||||
// to customize its encoding. Fact encoding should not fail.
|
||||
//
|
||||
// A Fact should not be modified once exported.
|
||||
type Fact interface {
|
||||
AFact() // dummy method to avoid type errors
|
||||
}
|
||||
|
||||
// A Module describes the module to which a package belongs.
|
||||
type Module struct {
|
||||
Path string // module path
|
||||
Version string // module version ("" if unknown, such as for workspace modules)
|
||||
Replace *Module // replaced by this module
|
||||
Time *time.Time // time version was created
|
||||
Main bool // is this the main module?
|
||||
Indirect bool // is this module only an indirect dependency of main module?
|
||||
Dir string // directory holding files for this module, if any
|
||||
GoMod string // path to go.mod file used when loading this module, if any
|
||||
GoVersion string // go version used in module (e.g. "go1.22.0")
|
||||
Error *ModuleError // error loading module
|
||||
}
|
||||
|
||||
// ModuleError holds errors loading a module.
|
||||
type ModuleError struct {
|
||||
Err string // the error itself
|
||||
}
|
||||
88
vendor/golang.org/x/tools/go/analysis/diagnostic.go
generated
vendored
Normal file
88
vendor/golang.org/x/tools/go/analysis/diagnostic.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2019 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
|
||||
|
||||
import "go/token"
|
||||
|
||||
// A Diagnostic is a message associated with a source location or range.
|
||||
//
|
||||
// An Analyzer may return a variety of diagnostics; the optional Category,
|
||||
// which should be a constant, may be used to classify them.
|
||||
// It is primarily intended to make it easy to look up documentation.
|
||||
//
|
||||
// All Pos values are interpreted relative to Pass.Fset. If End is
|
||||
// provided, the diagnostic is specified to apply to the range between
|
||||
// Pos and End.
|
||||
type Diagnostic struct {
|
||||
Pos token.Pos
|
||||
End token.Pos // optional
|
||||
Category string // optional
|
||||
Message string
|
||||
|
||||
// URL is the optional location of a web page that provides
|
||||
// additional documentation for this diagnostic.
|
||||
//
|
||||
// If URL is empty but a Category is specified, then the
|
||||
// Analysis driver should treat the URL as "#"+Category.
|
||||
//
|
||||
// The URL may be relative. If so, the base URL is that of the
|
||||
// Analyzer that produced the diagnostic;
|
||||
// see https://pkg.go.dev/net/url#URL.ResolveReference.
|
||||
URL string
|
||||
|
||||
// SuggestedFixes is an optional list of fixes to address the
|
||||
// problem described by the diagnostic. Each one represents an
|
||||
// alternative strategy, and should have a distinct and
|
||||
// descriptive message; at most one may be applied.
|
||||
//
|
||||
// Fixes for different diagnostics should be treated as
|
||||
// independent changes to the same baseline file state,
|
||||
// analogous to a set of git commits all with the same parent.
|
||||
// Combining fixes requires resolving any conflicts that
|
||||
// arise, analogous to a git merge.
|
||||
// Any conflicts that remain may be dealt with, depending on
|
||||
// the tool, by discarding fixes, consulting the user, or
|
||||
// aborting the operation.
|
||||
SuggestedFixes []SuggestedFix
|
||||
|
||||
// Related contains optional secondary positions and messages
|
||||
// related to the primary diagnostic.
|
||||
Related []RelatedInformation
|
||||
}
|
||||
|
||||
// RelatedInformation contains information related to a diagnostic.
|
||||
// For example, a diagnostic that flags duplicated declarations of a
|
||||
// variable may include one RelatedInformation per existing
|
||||
// declaration.
|
||||
type RelatedInformation struct {
|
||||
Pos token.Pos
|
||||
End token.Pos // optional
|
||||
Message string
|
||||
}
|
||||
|
||||
// A SuggestedFix is a code change associated with a Diagnostic that a
|
||||
// user can choose to apply to their code. Usually the SuggestedFix is
|
||||
// meant to fix the issue flagged by the diagnostic.
|
||||
//
|
||||
// The TextEdits must not overlap, nor contain edits for other
|
||||
// packages. Edits need not be totally ordered, but the order
|
||||
// determines how insertions at the same point will be applied.
|
||||
type SuggestedFix struct {
|
||||
// A verb phrase describing the fix, to be shown to
|
||||
// a user trying to decide whether to accept it.
|
||||
//
|
||||
// Example: "Remove the surplus argument"
|
||||
Message string
|
||||
TextEdits []TextEdit
|
||||
}
|
||||
|
||||
// A TextEdit represents the replacement of the code between Pos and End with the new text.
|
||||
// Each TextEdit should apply to a single file. End should not be earlier in the file than Pos.
|
||||
type TextEdit struct {
|
||||
// For a pure insertion, End can either be set to Pos or token.NoPos.
|
||||
Pos token.Pos
|
||||
End token.Pos
|
||||
NewText []byte
|
||||
}
|
||||
317
vendor/golang.org/x/tools/go/analysis/doc.go
generated
vendored
Normal file
317
vendor/golang.org/x/tools/go/analysis/doc.go
generated
vendored
Normal file
@@ -0,0 +1,317 @@
|
||||
// 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 of non-Go
|
||||
files such as assembly that are part of this package.
|
||||
Similarly, the IgnoredFiles field provides the names of 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.
|
||||
The contents of these files may be read using Pass.ReadFile;
|
||||
see the "asmdecl" or "buildtags" analyzers for examples of loading
|
||||
non-Go files and reporting diagnostics against them.
|
||||
|
||||
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.
|
||||
|
||||
The [Diagnostic] struct does not have a field to indicate its severity
|
||||
because opinions about the relative importance of Analyzers and their
|
||||
diagnostics vary widely among users. The design of this framework does
|
||||
not hold each Analyzer responsible for identifying the severity of its
|
||||
diagnostics. Instead, we expect that drivers will allow the user to
|
||||
customize the filtering and prioritization of diagnostics based on the
|
||||
producing Analyzer and optional Category, according to the user's
|
||||
preferences.
|
||||
|
||||
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 := pass.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.
|
||||
Because serialized facts may appear within build outputs, the gob encoding
|
||||
of a fact must be deterministic, to avoid spurious cache misses in
|
||||
build systems that use content-addressable caches.
|
||||
The driver makes a single call to the gob encoder for all facts
|
||||
exported by a given analysis pass, so that the topology of
|
||||
shared data structures referenced by multiple facts is preserved.
|
||||
|
||||
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/analysis/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
|
||||
278
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go
generated
vendored
Normal file
278
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go
generated
vendored
Normal file
@@ -0,0 +1,278 @@
|
||||
// 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 ctrlflow is an analysis that provides a syntactic
|
||||
// control-flow graph (CFG) for the body of a function.
|
||||
// It records whether a function cannot return.
|
||||
// By itself, it does not report any diagnostics.
|
||||
package ctrlflow
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"log"
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/go/cfg"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
"golang.org/x/tools/internal/typesinternal"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "ctrlflow",
|
||||
Doc: "build a control-flow graph",
|
||||
URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/ctrlflow",
|
||||
Run: run,
|
||||
ResultType: reflect.TypeFor[*CFGs](),
|
||||
FactTypes: []analysis.Fact{new(noReturn)},
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
}
|
||||
|
||||
// noReturn is a fact indicating that a function does not return.
|
||||
type noReturn struct{}
|
||||
|
||||
func (*noReturn) AFact() {}
|
||||
|
||||
func (*noReturn) String() string { return "noReturn" }
|
||||
|
||||
// A CFGs holds the control-flow graphs
|
||||
// for all the functions of the current package.
|
||||
type CFGs struct {
|
||||
defs map[*ast.Ident]types.Object // from Pass.TypesInfo.Defs
|
||||
funcDecls map[*types.Func]*declInfo
|
||||
funcLits map[*ast.FuncLit]*litInfo
|
||||
noReturn map[*types.Func]bool // functions lacking a reachable return statement
|
||||
pass *analysis.Pass // transient; nil after construction
|
||||
}
|
||||
|
||||
// NoReturn reports whether the specified control-flow graph cannot return normally.
|
||||
//
|
||||
// It is defined for at least all function symbols that appear as the static callee of a
|
||||
// CallExpr in the current package, even if the callee was imported from a dependency.
|
||||
//
|
||||
// The result may incorporate interprocedural information based on induction of
|
||||
// the "no return" property over the static call graph within the package.
|
||||
// For example, if f simply calls g and g always calls os.Exit, then both f and g may
|
||||
// be deemed never to return.
|
||||
func (c *CFGs) NoReturn(fn *types.Func) bool {
|
||||
return c.noReturn[fn]
|
||||
}
|
||||
|
||||
// CFGs has two maps: funcDecls for named functions and funcLits for
|
||||
// unnamed ones. Unlike funcLits, the funcDecls map is not keyed by its
|
||||
// syntax node, *ast.FuncDecl, because callMayReturn needs to do a
|
||||
// look-up by *types.Func, and you can get from an *ast.FuncDecl to a
|
||||
// *types.Func but not the other way.
|
||||
|
||||
type declInfo struct {
|
||||
decl *ast.FuncDecl
|
||||
cfg *cfg.CFG // iff decl.Body != nil
|
||||
started bool // to break cycles
|
||||
}
|
||||
|
||||
type litInfo struct {
|
||||
cfg *cfg.CFG
|
||||
noReturn bool // (currently unused)
|
||||
}
|
||||
|
||||
// FuncDecl returns the control-flow graph for a named function.
|
||||
// It returns nil if decl.Body==nil.
|
||||
func (c *CFGs) FuncDecl(decl *ast.FuncDecl) *cfg.CFG {
|
||||
if decl.Body == nil {
|
||||
return nil
|
||||
}
|
||||
fn := c.defs[decl.Name].(*types.Func)
|
||||
return c.funcDecls[fn].cfg
|
||||
}
|
||||
|
||||
// FuncLit returns the control-flow graph for a literal function.
|
||||
func (c *CFGs) FuncLit(lit *ast.FuncLit) *cfg.CFG {
|
||||
return c.funcLits[lit].cfg
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (any, error) {
|
||||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
|
||||
// Because CFG construction consumes and produces noReturn
|
||||
// facts, CFGs for exported FuncDecls must be built before 'run'
|
||||
// returns; we cannot construct them lazily.
|
||||
// (We could build CFGs for FuncLits lazily,
|
||||
// but the benefit is marginal.)
|
||||
|
||||
// Pass 1. Map types.Funcs to ast.FuncDecls in this package.
|
||||
funcDecls := make(map[*types.Func]*declInfo) // functions and methods
|
||||
funcLits := make(map[*ast.FuncLit]*litInfo)
|
||||
|
||||
var decls []*types.Func // keys(funcDecls), in order
|
||||
var lits []*ast.FuncLit // keys(funcLits), in order
|
||||
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.FuncDecl)(nil),
|
||||
(*ast.FuncLit)(nil),
|
||||
}
|
||||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
switch n := n.(type) {
|
||||
case *ast.FuncDecl:
|
||||
// Type information may be incomplete.
|
||||
if fn, ok := pass.TypesInfo.Defs[n.Name].(*types.Func); ok {
|
||||
funcDecls[fn] = &declInfo{decl: n}
|
||||
decls = append(decls, fn)
|
||||
}
|
||||
case *ast.FuncLit:
|
||||
funcLits[n] = new(litInfo)
|
||||
lits = append(lits, n)
|
||||
}
|
||||
})
|
||||
|
||||
c := &CFGs{
|
||||
defs: pass.TypesInfo.Defs,
|
||||
funcDecls: funcDecls,
|
||||
funcLits: funcLits,
|
||||
noReturn: make(map[*types.Func]bool),
|
||||
pass: pass,
|
||||
}
|
||||
|
||||
// Pass 2. Build CFGs.
|
||||
|
||||
// Build CFGs for named functions.
|
||||
// Cycles in the static call graph are broken
|
||||
// arbitrarily but deterministically.
|
||||
// We create noReturn facts as discovered.
|
||||
for _, fn := range decls {
|
||||
c.buildDecl(fn, funcDecls[fn])
|
||||
}
|
||||
|
||||
// Build CFGs for literal functions.
|
||||
// These aren't relevant to facts (since they aren't named)
|
||||
// but are required for the CFGs.FuncLit API.
|
||||
for _, lit := range lits {
|
||||
li := funcLits[lit]
|
||||
if li.cfg == nil {
|
||||
li.cfg = cfg.New(lit.Body, c.callMayReturn)
|
||||
if li.cfg.NoReturn() {
|
||||
li.noReturn = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All CFGs are now built.
|
||||
c.pass = nil
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// di.cfg may be nil on return.
|
||||
func (c *CFGs) buildDecl(fn *types.Func, di *declInfo) {
|
||||
// buildDecl may call itself recursively for the same function,
|
||||
// because cfg.New is passed the callMayReturn method, which
|
||||
// builds the CFG of the callee, leading to recursion.
|
||||
// The buildDecl call tree thus resembles the static call graph.
|
||||
// We mark each node when we start working on it to break cycles.
|
||||
|
||||
if di.started {
|
||||
return // break cycle
|
||||
}
|
||||
di.started = true
|
||||
|
||||
noreturn, known := knownIntrinsic(fn)
|
||||
if !known {
|
||||
if di.decl.Body != nil {
|
||||
di.cfg = cfg.New(di.decl.Body, c.callMayReturn)
|
||||
if di.cfg.NoReturn() {
|
||||
noreturn = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if noreturn {
|
||||
c.pass.ExportObjectFact(fn, new(noReturn))
|
||||
c.noReturn[fn] = true
|
||||
}
|
||||
|
||||
// debugging
|
||||
if false {
|
||||
log.Printf("CFG for %s:\n%s (noreturn=%t)\n", fn, di.cfg.Format(c.pass.Fset), noreturn)
|
||||
}
|
||||
}
|
||||
|
||||
// callMayReturn reports whether the called function may return.
|
||||
// It is passed to the CFG builder.
|
||||
func (c *CFGs) callMayReturn(call *ast.CallExpr) (r bool) {
|
||||
if id, ok := call.Fun.(*ast.Ident); ok && c.pass.TypesInfo.Uses[id] == panicBuiltin {
|
||||
return false // panic never returns
|
||||
}
|
||||
|
||||
// Is this a static call? Also includes static functions
|
||||
// parameterized by a type. Such functions may or may not
|
||||
// return depending on the parameter type, but in some
|
||||
// cases the answer is definite. We let ctrlflow figure
|
||||
// that out.
|
||||
fn := typeutil.StaticCallee(c.pass.TypesInfo, call)
|
||||
if fn == nil {
|
||||
return true // callee not statically known; be conservative
|
||||
}
|
||||
|
||||
// Function or method declared in this package?
|
||||
if di, ok := c.funcDecls[fn]; ok {
|
||||
c.buildDecl(fn, di)
|
||||
return !c.noReturn[fn]
|
||||
}
|
||||
|
||||
// Not declared in this package.
|
||||
// Is there a fact from another package?
|
||||
if c.pass.ImportObjectFact(fn, new(noReturn)) {
|
||||
c.noReturn[fn] = true
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
var panicBuiltin = types.Universe.Lookup("panic").(*types.Builtin)
|
||||
|
||||
// knownIntrinsic reports whether a function intrinsically never
|
||||
// returns because it stops execution of the calling thread, or does
|
||||
// in fact return, contrary to its apparent body, because it is
|
||||
// handled specially by the compiler.
|
||||
//
|
||||
// It is the base case in the recursion.
|
||||
func knownIntrinsic(fn *types.Func) (noreturn, known bool) {
|
||||
// Add functions here as the need arises, but don't allocate memory.
|
||||
|
||||
// Functions known intrinsically never to return.
|
||||
if typesinternal.IsFunctionNamed(fn, "syscall", "Exit", "ExitProcess", "ExitThread") ||
|
||||
typesinternal.IsFunctionNamed(fn, "runtime", "Goexit", "fatalthrow", "fatalpanic", "exit") ||
|
||||
// Following staticcheck (see go/ir/exits.go) we include functions
|
||||
// in several popular logging packages whose no-return status is
|
||||
// beyond the analysis to infer.
|
||||
// TODO(adonovan): make this list extensible.
|
||||
typesinternal.IsMethodNamed(fn, "go.uber.org/zap", "Logger", "Fatal", "Panic") ||
|
||||
typesinternal.IsMethodNamed(fn, "go.uber.org/zap", "SugaredLogger", "Fatal", "Fatalw", "Fatalf", "Panic", "Panicw", "Panicf") ||
|
||||
typesinternal.IsMethodNamed(fn, "github.com/sirupsen/logrus", "Logger", "Exit", "Panic", "Panicf", "Panicln") ||
|
||||
typesinternal.IsMethodNamed(fn, "github.com/sirupsen/logrus", "Entry", "Panicf", "Panicln") ||
|
||||
typesinternal.IsFunctionNamed(fn, "k8s.io/klog", "Exit", "ExitDepth", "Exitf", "Exitln", "Fatal", "FatalDepth", "Fatalf", "Fatalln") ||
|
||||
typesinternal.IsFunctionNamed(fn, "k8s.io/klog/v2", "Exit", "ExitDepth", "Exitf", "Exitln", "Fatal", "FatalDepth", "Fatalf", "Fatalln") {
|
||||
return true, true
|
||||
}
|
||||
|
||||
// Compiler intrinsics known to return, contrary to
|
||||
// what analysis of the function body would conclude.
|
||||
//
|
||||
// Not all such intrinsics must be listed here: ctrlflow
|
||||
// considers any function called for its value--such as
|
||||
// crypto/internal/constanttime.bool2Uint8--to potentially
|
||||
// return; only functions called as a statement, for effects,
|
||||
// are no-return candidates.
|
||||
//
|
||||
// Unfortunately this does sometimes mean peering into internals.
|
||||
// Where possible, use the nearest enclosing public API function.
|
||||
if typesinternal.IsFunctionNamed(fn, "internal/abi", "EscapeNonString") ||
|
||||
typesinternal.IsFunctionNamed(fn, "hash/maphash", "Comparable") {
|
||||
return false, true
|
||||
}
|
||||
|
||||
return // unknown
|
||||
}
|
||||
49
vendor/golang.org/x/tools/go/analysis/passes/inspect/inspect.go
generated
vendored
Normal file
49
vendor/golang.org/x/tools/go/analysis/passes/inspect/inspect.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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 inspect defines an Analyzer that provides an AST inspector
|
||||
// (golang.org/x/tools/go/ast/inspector.Inspector) for the syntax trees
|
||||
// of a package. It is only a building block for other analyzers.
|
||||
//
|
||||
// Example of use in another analysis:
|
||||
//
|
||||
// import (
|
||||
// "golang.org/x/tools/go/analysis"
|
||||
// "golang.org/x/tools/go/analysis/passes/inspect"
|
||||
// "golang.org/x/tools/go/ast/inspector"
|
||||
// )
|
||||
//
|
||||
// var Analyzer = &analysis.Analyzer{
|
||||
// ...
|
||||
// Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
// }
|
||||
//
|
||||
// func run(pass *analysis.Pass) (interface{}, error) {
|
||||
// inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
// inspect.Preorder(nil, func(n ast.Node) {
|
||||
// ...
|
||||
// })
|
||||
// return nil, nil
|
||||
// }
|
||||
package inspect
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
var Analyzer = &analysis.Analyzer{
|
||||
Name: "inspect",
|
||||
Doc: "optimize AST traversal for later passes",
|
||||
URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/inspect",
|
||||
Run: run,
|
||||
RunDespiteErrors: true,
|
||||
ResultType: reflect.TypeFor[*inspector.Inspector](),
|
||||
}
|
||||
|
||||
func run(pass *analysis.Pass) (any, error) {
|
||||
return inspector.New(pass.Files), nil
|
||||
}
|
||||
137
vendor/golang.org/x/tools/go/analysis/validate.go
generated
vendored
Normal file
137
vendor/golang.org/x/tools/go/analysis/validate.go
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Validate reports an error if any of the analyzers are misconfigured.
|
||||
// Checks include:
|
||||
// that the name is a valid identifier;
|
||||
// that the Doc is not empty;
|
||||
// that the Run is non-nil;
|
||||
// that the Requires graph is acyclic;
|
||||
// that analyzer fact types are unique;
|
||||
// that each fact type is a pointer.
|
||||
//
|
||||
// Analyzer names need not be unique, though this may be confusing.
|
||||
func Validate(analyzers []*Analyzer) error {
|
||||
// Map each fact type to its sole generating analyzer.
|
||||
factTypes := make(map[reflect.Type]*Analyzer)
|
||||
|
||||
// Traverse the Requires graph, depth first.
|
||||
const (
|
||||
white = iota
|
||||
grey
|
||||
black
|
||||
finished
|
||||
)
|
||||
color := make(map[*Analyzer]uint8)
|
||||
var visit func(a *Analyzer) error
|
||||
visit = func(a *Analyzer) error {
|
||||
if a == nil {
|
||||
return fmt.Errorf("nil *Analyzer")
|
||||
}
|
||||
if color[a] == white {
|
||||
color[a] = grey
|
||||
|
||||
// names
|
||||
if !validIdent(a.Name) {
|
||||
return fmt.Errorf("invalid analyzer name %q", a)
|
||||
}
|
||||
|
||||
if a.Doc == "" {
|
||||
return fmt.Errorf("analyzer %q is undocumented", a)
|
||||
}
|
||||
|
||||
if a.Run == nil {
|
||||
return fmt.Errorf("analyzer %q has nil Run", a)
|
||||
}
|
||||
// fact types
|
||||
for _, f := range a.FactTypes {
|
||||
if f == nil {
|
||||
return fmt.Errorf("analyzer %s has nil FactType", a)
|
||||
}
|
||||
t := reflect.TypeOf(f)
|
||||
if prev := factTypes[t]; prev != nil {
|
||||
return fmt.Errorf("fact type %s registered by two analyzers: %v, %v",
|
||||
t, a, prev)
|
||||
}
|
||||
if t.Kind() != reflect.Pointer {
|
||||
return fmt.Errorf("%s: fact type %s is not a pointer", a, t)
|
||||
}
|
||||
factTypes[t] = a
|
||||
}
|
||||
|
||||
// recursion
|
||||
for _, req := range a.Requires {
|
||||
if err := visit(req); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
color[a] = black
|
||||
}
|
||||
|
||||
if color[a] == grey {
|
||||
stack := []*Analyzer{a}
|
||||
inCycle := map[string]bool{}
|
||||
for len(stack) > 0 {
|
||||
current := stack[len(stack)-1]
|
||||
stack = stack[:len(stack)-1]
|
||||
if color[current] == grey && !inCycle[current.Name] {
|
||||
inCycle[current.Name] = true
|
||||
stack = append(stack, current.Requires...)
|
||||
}
|
||||
}
|
||||
return &CycleInRequiresGraphError{AnalyzerNames: inCycle}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
for _, a := range analyzers {
|
||||
if err := visit(a); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Reject duplicates among analyzers.
|
||||
// Precondition: color[a] == black.
|
||||
// Postcondition: color[a] == finished.
|
||||
for _, a := range analyzers {
|
||||
if color[a] == finished {
|
||||
return fmt.Errorf("duplicate analyzer: %s", a.Name)
|
||||
}
|
||||
color[a] = finished
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validIdent(name string) bool {
|
||||
for i, r := range name {
|
||||
if !(r == '_' || unicode.IsLetter(r) || i > 0 && unicode.IsDigit(r)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return name != ""
|
||||
}
|
||||
|
||||
type CycleInRequiresGraphError struct {
|
||||
AnalyzerNames map[string]bool
|
||||
}
|
||||
|
||||
func (e *CycleInRequiresGraphError) Error() string {
|
||||
var b strings.Builder
|
||||
b.WriteString("cycle detected involving the following analyzers:")
|
||||
for n := range e.AnalyzerNames {
|
||||
b.WriteByte(' ')
|
||||
b.WriteString(n)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
Reference in New Issue
Block a user