mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 08:02:36 +09:00 
			
		
		
		
	* change to new code location * vendor * tagged version v0.2.0 * gitea-vet v0.2.1 Co-authored-by: techknowlogick <techknowlogick@gitea.io>
		
			
				
	
	
		
			311 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			311 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
/*
 | 
						||
 | 
						||
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
 | 
						||
		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 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
 |