mirror of
				https://github.com/go-gitea/gitea
				synced 2025-10-30 10:58:27 +00: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
 |