forked from golang/tools
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
go/analysis/checker: a go/packages-based driver library
The {single,multi}checker packages provide the main function for a complete application, as a black box. Many users want the ability to customize the analyzer behavior with additional logic, as described in the attached issues. This change creates a new package, go/analysis/checker, that exposes an Analyze pure function---one that avoids global flags, os.Exit, logging, profiling, and other side effects---that runs a set of analyzers on a set of packages loaded (by the client) using go/packages, and presents the graph of results in a form that allows postprocessing. This is just a sketch. API feedback welcome. DO NOT SUBMIT Updates golang/go#30231 Updates golang/go#30219 Updates golang/go#31007 Updates golang/go#31897 Updates golang/go#50265 Updates golang/go#53215 Updates golang/go#53336 Change-Id: I745d319a587dca506564a4624b52a7f1eb5f4751
- Loading branch information
1 parent
21861e6
commit a349c62
Showing
3 changed files
with
180 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Package checker provides functions for loading a Go program using | ||
// go/packages and running moduler analyses on it. | ||
package checker | ||
|
||
import ( | ||
"golang.org/x/tools/go/analysis" | ||
internalchecker "golang.org/x/tools/go/analysis/internal/checker" | ||
"golang.org/x/tools/go/packages" | ||
) | ||
|
||
// A Result holds the results of an analysis pass: the application of | ||
// a particular analyzer to a particular package. | ||
type Result = internalchecker.Result | ||
|
||
// Analyze runs the specified analyzers on the initial packages. | ||
// It also runs any analyzer that makes use of Facts on all the | ||
// dependencies of the initial packages. (In this case the program | ||
// must have been loaded using the packages.LoadAllSyntax flag.) | ||
// | ||
// The elements of the results slice correspond to those of the the | ||
// initial packages slice. If analyses were applied to dependencies, | ||
// these may be found by traversing the Result.Deps edges. | ||
func Analyze(initial []*packages.Package, analyzers []*analysis.Analyzer) []*Result { | ||
return internalchecker.Analyze(initial, analyzers) | ||
} | ||
|
||
// TODO(adonovan): API questions to resolve before we set this in stone: | ||
// - Provide some or all of the functionality of internalchecker.printDiagnostics, | ||
// including JSON printing? Currently this API makes it the client's responsibility. | ||
// - Move the definition of Result into this package, for clarity? | ||
// - Expose a function to apply one or more of Result.Diagnostics.SuggestedFixes? | ||
// - Expose Debug flags? | ||
// - Add an Options struct to give us latitude? |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// The example command demonstrates a simple go/packages-based | ||
// analysis driver program. | ||
package checker_test | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"reflect" | ||
|
||
"golang.org/x/tools/go/analysis" | ||
"golang.org/x/tools/go/analysis/checker" | ||
"golang.org/x/tools/go/packages" | ||
) | ||
|
||
func Example() { | ||
// Gather analyzers. | ||
analyzers := []*analysis.Analyzer{dummy} | ||
if err := analysis.Validate(analyzers); err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
// Load packages. | ||
// We stop if there are any parse/type errors, | ||
// but that isn't strictly necessary. | ||
cfg := &packages.Config{Mode: packages.LoadAllSyntax} | ||
initial, err := packages.Load(cfg, ".") // (this package) | ||
if err != nil { | ||
log.Fatal(err) // parse/type error | ||
} | ||
if len(initial) == 0 { | ||
log.Fatalf("no initial packages") | ||
} | ||
|
||
// Run analyzers. | ||
results := checker.Analyze(initial, analyzers) | ||
|
||
// Print graph of results. | ||
seen := make(map[*checker.Result]bool) | ||
var printAll func(results []*checker.Result) | ||
printAll = func(results []*checker.Result) { | ||
for _, r := range results { | ||
if !seen[r] { | ||
seen[r] = true | ||
printAll(r.Deps) | ||
|
||
// Print the Result and any diagnostics. | ||
fmt.Printf("%v:", r) // name | ||
if r.Result != nil { | ||
fmt.Printf(" (result=%v)", r.Result) | ||
} | ||
if r.Err != nil { | ||
fmt.Printf("%v\n", r.Err) | ||
} else { | ||
fmt.Println() | ||
for _, diag := range r.Diagnostics { | ||
fmt.Printf("- %s: %s\n", r.Package.Fset.Position(diag.Pos), diag.Message) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
printAll(results) | ||
|
||
// Output: | ||
// | ||
// TODO: think of a simple analyzer that produces predictable | ||
// results that we could use in an example test. e.g. | ||
// compute the largest package among the transitive deps. | ||
} | ||
|
||
var dummy = &analysis.Analyzer{ | ||
Name: "dummy", | ||
Doc: "An trivial analyzer that reports the number of files in a package.", | ||
Run: dummyRun, | ||
ResultType: reflect.TypeOf(0), // (number of source lines in package) | ||
FactTypes: []analysis.Fact{(*dummyFact)(nil)}, | ||
} | ||
|
||
// A dummy fact that causes analysis of all dependencies | ||
type dummyFact struct{} | ||
|
||
func (dummyFact) AFact() {} | ||
|
||
func dummyRun(pass *analysis.Pass) (interface{}, error) { | ||
nlines := 0 | ||
for _, f := range pass.Files { | ||
nlines += pass.Fset.Position(f.End()).Line | ||
} | ||
if nfiles := len(pass.Files); nfiles > 0 { | ||
pass.Reportf(pass.Files[0].Package, "package %s has %d files", pass.Pkg.Name(), nfiles) | ||
} | ||
return nlines, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters