modify goanalysis
This commit is contained in:
parent
926e99aed0
commit
7289a90245
@ -1,344 +0,0 @@
|
||||
// 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 analysisflags defines helpers for processing flags of
|
||||
// analysis driver tools.
|
||||
package analysisflags
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
)
|
||||
|
||||
// flags common to all {single,multi,unit}checkers.
|
||||
var (
|
||||
JSON = false // -json
|
||||
Context = -1 // -c=N: if N>0, display offending line plus N lines of context
|
||||
)
|
||||
|
||||
// Parse creates a flag for each of the analyzer's flags,
|
||||
// including (in multi mode) a flag named after the analyzer,
|
||||
// parses the flags, then filters and returns the list of
|
||||
// analyzers enabled by flags.
|
||||
func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer {
|
||||
// Connect each analysis flag to the command line as -analysis.flag.
|
||||
enabled := make(map[*analysis.Analyzer]*triState)
|
||||
for _, a := range analyzers {
|
||||
var prefix string
|
||||
|
||||
// Add -NAME flag to enable it.
|
||||
if multi {
|
||||
prefix = a.Name + "."
|
||||
|
||||
enable := new(triState)
|
||||
enableUsage := "enable " + a.Name + " analysis"
|
||||
flag.Var(enable, a.Name, enableUsage)
|
||||
enabled[a] = enable
|
||||
}
|
||||
|
||||
a.Flags.VisitAll(func(f *flag.Flag) {
|
||||
if !multi && flag.Lookup(f.Name) != nil {
|
||||
log.Printf("%s flag -%s would conflict with driver; skipping", a.Name, f.Name)
|
||||
return
|
||||
}
|
||||
|
||||
name := prefix + f.Name
|
||||
flag.Var(f.Value, name, f.Usage)
|
||||
})
|
||||
}
|
||||
|
||||
// standard flags: -flags, -V.
|
||||
printflags := flag.Bool("flags", false, "print analyzer flags in JSON")
|
||||
addVersionFlag()
|
||||
|
||||
// flags common to all checkers
|
||||
flag.BoolVar(&JSON, "json", JSON, "emit JSON output")
|
||||
flag.IntVar(&Context, "c", Context, `display offending line with this many lines of context`)
|
||||
|
||||
// Add shims for legacy vet flags to enable existing
|
||||
// scripts that run vet to continue to work.
|
||||
_ = flag.Bool("source", false, "no effect (deprecated)")
|
||||
_ = flag.Bool("v", false, "no effect (deprecated)")
|
||||
_ = flag.Bool("all", false, "no effect (deprecated)")
|
||||
_ = flag.String("tags", "", "no effect (deprecated)")
|
||||
for old, new := range vetLegacyFlags {
|
||||
newFlag := flag.Lookup(new)
|
||||
if newFlag != nil && flag.Lookup(old) == nil {
|
||||
flag.Var(newFlag.Value, old, "deprecated alias for -"+new)
|
||||
}
|
||||
}
|
||||
|
||||
flag.Parse() // (ExitOnError)
|
||||
|
||||
// -flags: print flags so that go vet knows which ones are legitimate.
|
||||
if *printflags {
|
||||
printFlags()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// If any -NAME flag is true, run only those analyzers. Otherwise,
|
||||
// if any -NAME flag is false, run all but those analyzers.
|
||||
if multi {
|
||||
var hasTrue, hasFalse bool
|
||||
for _, ts := range enabled {
|
||||
switch *ts {
|
||||
case setTrue:
|
||||
hasTrue = true
|
||||
case setFalse:
|
||||
hasFalse = true
|
||||
}
|
||||
}
|
||||
|
||||
var keep []*analysis.Analyzer
|
||||
if hasTrue {
|
||||
for _, a := range analyzers {
|
||||
if *enabled[a] == setTrue {
|
||||
keep = append(keep, a)
|
||||
}
|
||||
}
|
||||
analyzers = keep
|
||||
} else if hasFalse {
|
||||
for _, a := range analyzers {
|
||||
if *enabled[a] != setFalse {
|
||||
keep = append(keep, a)
|
||||
}
|
||||
}
|
||||
analyzers = keep
|
||||
}
|
||||
}
|
||||
|
||||
return analyzers
|
||||
}
|
||||
|
||||
func printFlags() {
|
||||
type jsonFlag struct {
|
||||
Name string
|
||||
Bool bool
|
||||
Usage string
|
||||
}
|
||||
var flags []jsonFlag = nil
|
||||
flag.VisitAll(func(f *flag.Flag) {
|
||||
// Don't report {single,multi}checker debugging
|
||||
// flags as these have no effect on unitchecker
|
||||
// (as invoked by 'go vet').
|
||||
switch f.Name {
|
||||
case "debug", "cpuprofile", "memprofile", "trace":
|
||||
return
|
||||
}
|
||||
|
||||
b, ok := f.Value.(interface{ IsBoolFlag() bool })
|
||||
isBool := ok && b.IsBoolFlag()
|
||||
flags = append(flags, jsonFlag{f.Name, isBool, f.Usage})
|
||||
})
|
||||
data, err := json.MarshalIndent(flags, "", "\t")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.Stdout.Write(data)
|
||||
}
|
||||
|
||||
// addVersionFlag registers a -V flag that, if set,
|
||||
// prints the executable version and exits 0.
|
||||
//
|
||||
// If the -V flag already exists — for example, because it was already
|
||||
// registered by a call to cmd/internal/objabi.AddVersionFlag — then
|
||||
// addVersionFlag does nothing.
|
||||
func addVersionFlag() {
|
||||
if flag.Lookup("V") == nil {
|
||||
flag.Var(versionFlag{}, "V", "print version and exit")
|
||||
}
|
||||
}
|
||||
|
||||
// versionFlag minimally complies with the -V protocol required by "go vet".
|
||||
type versionFlag struct{}
|
||||
|
||||
func (versionFlag) IsBoolFlag() bool { return true }
|
||||
func (versionFlag) Get() interface{} { return nil }
|
||||
func (versionFlag) String() string { return "" }
|
||||
func (versionFlag) Set(s string) error {
|
||||
if s != "full" {
|
||||
log.Fatalf("unsupported flag value: -V=%s", s)
|
||||
}
|
||||
|
||||
// This replicates the miminal subset of
|
||||
// cmd/internal/objabi.AddVersionFlag, which is private to the
|
||||
// go tool yet forms part of our command-line interface.
|
||||
// TODO(adonovan): clarify the contract.
|
||||
|
||||
// Print the tool version so the build system can track changes.
|
||||
// Formats:
|
||||
// $progname version devel ... buildID=...
|
||||
// $progname version go1.9.1
|
||||
progname := os.Args[0]
|
||||
f, err := os.Open(progname)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
f.Close()
|
||||
fmt.Printf("%s version devel comments-go-here buildID=%02x\n",
|
||||
progname, string(h.Sum(nil)))
|
||||
os.Exit(0)
|
||||
return nil
|
||||
}
|
||||
|
||||
// A triState is a boolean that knows whether
|
||||
// it has been set to either true or false.
|
||||
// It is used to identify whether a flag appears;
|
||||
// the standard boolean flag cannot
|
||||
// distinguish missing from unset.
|
||||
// It also satisfies flag.Value.
|
||||
type triState int
|
||||
|
||||
const (
|
||||
unset triState = iota
|
||||
setTrue
|
||||
setFalse
|
||||
)
|
||||
|
||||
func triStateFlag(name string, value triState, usage string) *triState {
|
||||
flag.Var(&value, name, usage)
|
||||
return &value
|
||||
}
|
||||
|
||||
// triState implements flag.Value, flag.Getter, and flag.boolFlag.
|
||||
// They work like boolean flags: we can say vet -printf as well as vet -printf=true
|
||||
func (ts *triState) Get() interface{} {
|
||||
return *ts == setTrue
|
||||
}
|
||||
|
||||
func (ts triState) isTrue() bool {
|
||||
return ts == setTrue
|
||||
}
|
||||
|
||||
func (ts *triState) Set(value string) error {
|
||||
b, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
// This error message looks poor but package "flag" adds
|
||||
// "invalid boolean value %q for -NAME: %s"
|
||||
return fmt.Errorf("want true or false")
|
||||
}
|
||||
if b {
|
||||
*ts = setTrue
|
||||
} else {
|
||||
*ts = setFalse
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ts *triState) String() string {
|
||||
switch *ts {
|
||||
case unset:
|
||||
return "true"
|
||||
case setTrue:
|
||||
return "true"
|
||||
case setFalse:
|
||||
return "false"
|
||||
}
|
||||
panic("not reached")
|
||||
}
|
||||
|
||||
func (ts triState) IsBoolFlag() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Legacy flag support
|
||||
|
||||
// vetLegacyFlags maps flags used by legacy vet to their corresponding
|
||||
// new names. The old names will continue to work.
|
||||
var vetLegacyFlags = map[string]string{
|
||||
// Analyzer name changes
|
||||
"bool": "bools",
|
||||
"buildtags": "buildtag",
|
||||
"methods": "stdmethods",
|
||||
"rangeloops": "loopclosure",
|
||||
|
||||
// Analyzer flags
|
||||
"compositewhitelist": "composites.whitelist",
|
||||
"printfuncs": "printf.funcs",
|
||||
"shadowstrict": "shadow.strict",
|
||||
"unusedfuncs": "unusedresult.funcs",
|
||||
"unusedstringmethods": "unusedresult.stringmethods",
|
||||
}
|
||||
|
||||
// ---- output helpers common to all drivers ----
|
||||
|
||||
// PrintPlain prints a diagnostic in plain text form,
|
||||
// with context specified by the -c flag.
|
||||
func PrintPlain(fset *token.FileSet, diag analysis.Diagnostic) {
|
||||
posn := fset.Position(diag.Pos)
|
||||
fmt.Fprintf(os.Stderr, "%s: %s\n", posn, diag.Message)
|
||||
|
||||
// -c=N: show offending line plus N lines of context.
|
||||
if Context >= 0 {
|
||||
data, _ := ioutil.ReadFile(posn.Filename)
|
||||
lines := strings.Split(string(data), "\n")
|
||||
for i := posn.Line - Context; i <= posn.Line+Context; i++ {
|
||||
if 1 <= i && i <= len(lines) {
|
||||
fmt.Fprintf(os.Stderr, "%d\t%s\n", i, lines[i-1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A JSONTree is a mapping from package ID to analysis name to result.
|
||||
// Each result is either a jsonError or a list of jsonDiagnostic.
|
||||
type JSONTree map[string]map[string]interface{}
|
||||
|
||||
// Add adds the result of analysis 'name' on package 'id'.
|
||||
// The result is either a list of diagnostics or an error.
|
||||
func (tree JSONTree) Add(fset *token.FileSet, id, name string, diags []analysis.Diagnostic, err error) {
|
||||
var v interface{}
|
||||
if err != nil {
|
||||
type jsonError struct {
|
||||
Err string `json:"error"`
|
||||
}
|
||||
v = jsonError{err.Error()}
|
||||
} else if len(diags) > 0 {
|
||||
type jsonDiagnostic struct {
|
||||
Category string `json:"category,omitempty"`
|
||||
Posn string `json:"posn"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
var diagnostics []jsonDiagnostic
|
||||
for _, f := range diags {
|
||||
diagnostics = append(diagnostics, jsonDiagnostic{
|
||||
Category: f.Category,
|
||||
Posn: fset.Position(f.Pos).String(),
|
||||
Message: f.Message,
|
||||
})
|
||||
}
|
||||
v = diagnostics
|
||||
}
|
||||
if v != nil {
|
||||
m, ok := tree[id]
|
||||
if !ok {
|
||||
m = make(map[string]interface{})
|
||||
tree[id] = m
|
||||
}
|
||||
m[name] = v
|
||||
}
|
||||
}
|
||||
|
||||
func (tree JSONTree) Print() {
|
||||
data, err := json.MarshalIndent(tree, "", "\t")
|
||||
if err != nil {
|
||||
log.Panicf("internal error: JSON marshalling failed: %v", err)
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
}
|
@ -12,7 +12,6 @@ package checker
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
@ -27,10 +26,10 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/packages"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis/analysisflags"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -42,22 +41,16 @@ var (
|
||||
// t show [t]iming info (NB: use 'p' flag to avoid GC/scheduler noise)
|
||||
// v show [v]erbose logging
|
||||
//
|
||||
Debug = ""
|
||||
Debug = os.Getenv("GL_DEBUG_GO_ANALYSIS")
|
||||
|
||||
// Log files for optional performance tracing.
|
||||
CPUProfile, MemProfile, Trace string
|
||||
)
|
||||
|
||||
// RegisterFlags registers command-line flags used by the analysis driver.
|
||||
func RegisterFlags() {
|
||||
// When adding flags here, remember to update
|
||||
// the list of suppressed flags in analysisflags.
|
||||
|
||||
flag.StringVar(&Debug, "debug", Debug, `debug flags, any subset of "fpstv"`)
|
||||
|
||||
flag.StringVar(&CPUProfile, "cpuprofile", "", "write CPU profile to this file")
|
||||
flag.StringVar(&MemProfile, "memprofile", "", "write memory profile to this file")
|
||||
flag.StringVar(&Trace, "trace", "", "write trace log to this file")
|
||||
type Diagnostic struct {
|
||||
analysis.Diagnostic
|
||||
AnalyzerName string
|
||||
Position token.Position
|
||||
}
|
||||
|
||||
// Run loads the packages specified by args using go/packages,
|
||||
@ -66,7 +59,8 @@ func RegisterFlags() {
|
||||
// It provides most of the logic for the main functions of both the
|
||||
// singlechecker and the multi-analysis commands.
|
||||
// It returns the appropriate exit code.
|
||||
func Run(args []string, analyzers []*analysis.Analyzer) (exitcode int) {
|
||||
//nolint:gocyclo
|
||||
func Run(analyzers []*analysis.Analyzer, initialPackages []*packages.Package) ([]Diagnostic, []error) {
|
||||
if CPUProfile != "" {
|
||||
f, err := os.Create(CPUProfile)
|
||||
if err != nil {
|
||||
@ -113,81 +107,13 @@ func Run(args []string, analyzers []*analysis.Analyzer) (exitcode int) {
|
||||
if dbg('v') {
|
||||
log.SetPrefix("")
|
||||
log.SetFlags(log.Lmicroseconds) // display timing
|
||||
log.Printf("load %s", args)
|
||||
}
|
||||
|
||||
// Optimization: if the selected analyzers don't produce/consume
|
||||
// facts, we need source only for the initial packages.
|
||||
allSyntax := needFacts(analyzers)
|
||||
initial, err := load(args, allSyntax)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
return 1 // load errors
|
||||
log.Printf("load %d packages", len(initialPackages))
|
||||
}
|
||||
|
||||
// Print the results.
|
||||
roots := analyze(initial, analyzers)
|
||||
roots := analyze(initialPackages, analyzers)
|
||||
|
||||
return printDiagnostics(roots)
|
||||
}
|
||||
|
||||
// load loads the initial packages.
|
||||
func load(patterns []string, allSyntax bool) ([]*packages.Package, error) {
|
||||
mode := packages.LoadSyntax
|
||||
if allSyntax {
|
||||
mode = packages.LoadAllSyntax
|
||||
}
|
||||
conf := packages.Config{
|
||||
Mode: mode,
|
||||
Tests: true,
|
||||
}
|
||||
initial, err := packages.Load(&conf, patterns...)
|
||||
if err == nil {
|
||||
if n := packages.PrintErrors(initial); n > 1 {
|
||||
err = fmt.Errorf("%d errors during loading", n)
|
||||
} else if n == 1 {
|
||||
err = fmt.Errorf("error during loading")
|
||||
} else if len(initial) == 0 {
|
||||
err = fmt.Errorf("%s matched no packages", strings.Join(patterns, " "))
|
||||
}
|
||||
}
|
||||
|
||||
return initial, err
|
||||
}
|
||||
|
||||
// TestAnalyzer applies an analysis to a set of packages (and their
|
||||
// dependencies if necessary) and returns the results.
|
||||
//
|
||||
// Facts about pkg are returned in a map keyed by object; package facts
|
||||
// have a nil key.
|
||||
//
|
||||
// This entry point is used only by analysistest.
|
||||
func TestAnalyzer(a *analysis.Analyzer, pkgs []*packages.Package) []*TestAnalyzerResult {
|
||||
var results []*TestAnalyzerResult
|
||||
for _, act := range analyze(pkgs, []*analysis.Analyzer{a}) {
|
||||
facts := make(map[types.Object][]analysis.Fact)
|
||||
for key, fact := range act.objectFacts {
|
||||
if key.obj.Pkg() == act.pass.Pkg {
|
||||
facts[key.obj] = append(facts[key.obj], fact)
|
||||
}
|
||||
}
|
||||
for key, fact := range act.packageFacts {
|
||||
if key.pkg == act.pass.Pkg {
|
||||
facts[nil] = append(facts[nil], fact)
|
||||
}
|
||||
}
|
||||
|
||||
results = append(results, &TestAnalyzerResult{act.pass, act.diagnostics, facts, act.result, act.err})
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
type TestAnalyzerResult struct {
|
||||
Pass *analysis.Pass
|
||||
Diagnostics []analysis.Diagnostic
|
||||
Facts map[types.Object][]analysis.Fact
|
||||
Result interface{}
|
||||
Err error
|
||||
return extractDiagnostics(roots)
|
||||
}
|
||||
|
||||
func analyze(pkgs []*packages.Package, analyzers []*analysis.Analyzer) []*action {
|
||||
@ -252,118 +178,59 @@ func analyze(pkgs []*packages.Package, analyzers []*analysis.Analyzer) []*action
|
||||
return roots
|
||||
}
|
||||
|
||||
// printDiagnostics prints the diagnostics for the root packages in either
|
||||
// plain text or JSON format. JSON format also includes errors for any
|
||||
// dependencies.
|
||||
//
|
||||
// It returns the exitcode: in plain mode, 0 for success, 1 for analysis
|
||||
// errors, and 3 for diagnostics. We avoid 2 since the flag package uses
|
||||
// it. JSON mode always succeeds at printing errors and diagnostics in a
|
||||
// structured form to stdout.
|
||||
func printDiagnostics(roots []*action) (exitcode int) {
|
||||
// Print the output.
|
||||
//
|
||||
// Print diagnostics only for root packages,
|
||||
// but errors for all packages.
|
||||
printed := make(map[*action]bool)
|
||||
var print func(*action)
|
||||
func extractDiagnostics(roots []*action) (retDiags []Diagnostic, retErrors []error) {
|
||||
extracted := make(map[*action]bool)
|
||||
var extract func(*action)
|
||||
var visitAll func(actions []*action)
|
||||
visitAll = func(actions []*action) {
|
||||
for _, act := range actions {
|
||||
if !printed[act] {
|
||||
printed[act] = true
|
||||
if !extracted[act] {
|
||||
extracted[act] = true
|
||||
visitAll(act.deps)
|
||||
print(act)
|
||||
extract(act)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if analysisflags.JSON {
|
||||
// JSON output
|
||||
tree := make(analysisflags.JSONTree)
|
||||
print = func(act *action) {
|
||||
var diags []analysis.Diagnostic
|
||||
if act.isroot {
|
||||
diags = act.diagnostics
|
||||
}
|
||||
tree.Add(act.pkg.Fset, act.pkg.ID, act.a.Name, diags, act.err)
|
||||
// De-duplicate diagnostics by position (not token.Pos) to
|
||||
// avoid double-reporting in source files that belong to
|
||||
// multiple packages, such as foo and foo.test.
|
||||
type key struct {
|
||||
token.Position
|
||||
*analysis.Analyzer
|
||||
message string
|
||||
}
|
||||
seen := make(map[key]bool)
|
||||
|
||||
extract = func(act *action) {
|
||||
if act.err != nil {
|
||||
retErrors = append(retErrors, errors.Wrap(act.err, act.a.Name))
|
||||
return
|
||||
}
|
||||
visitAll(roots)
|
||||
tree.Print()
|
||||
} else {
|
||||
// plain text output
|
||||
|
||||
// De-duplicate diagnostics by position (not token.Pos) to
|
||||
// avoid double-reporting in source files that belong to
|
||||
// multiple packages, such as foo and foo.test.
|
||||
type key struct {
|
||||
token.Position
|
||||
*analysis.Analyzer
|
||||
message string
|
||||
}
|
||||
seen := make(map[key]bool)
|
||||
if act.isroot {
|
||||
for _, diag := range act.diagnostics {
|
||||
// We don't display a.Name/f.Category
|
||||
// as most users don't care.
|
||||
|
||||
print = func(act *action) {
|
||||
if act.err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: %v\n", act.a.Name, act.err)
|
||||
exitcode = 1 // analysis failed, at least partially
|
||||
return
|
||||
}
|
||||
if act.isroot {
|
||||
for _, diag := range act.diagnostics {
|
||||
// We don't display a.Name/f.Category
|
||||
// as most users don't care.
|
||||
|
||||
posn := act.pkg.Fset.Position(diag.Pos)
|
||||
k := key{posn, act.a, diag.Message}
|
||||
if seen[k] {
|
||||
continue // duplicate
|
||||
}
|
||||
seen[k] = true
|
||||
|
||||
analysisflags.PrintPlain(act.pkg.Fset, diag)
|
||||
posn := act.pkg.Fset.Position(diag.Pos)
|
||||
k := key{posn, act.a, diag.Message}
|
||||
if seen[k] {
|
||||
continue // duplicate
|
||||
}
|
||||
}
|
||||
}
|
||||
visitAll(roots)
|
||||
seen[k] = true
|
||||
|
||||
if exitcode == 0 && len(seen) > 0 {
|
||||
exitcode = 3 // successfuly produced diagnostics
|
||||
}
|
||||
}
|
||||
|
||||
// Print timing info.
|
||||
if dbg('t') {
|
||||
if !dbg('p') {
|
||||
log.Println("Warning: times are mostly GC/scheduler noise; use -debug=tp to disable parallelism")
|
||||
}
|
||||
var all []*action
|
||||
var total time.Duration
|
||||
for act := range printed {
|
||||
all = append(all, act)
|
||||
total += act.duration
|
||||
}
|
||||
sort.Slice(all, func(i, j int) bool {
|
||||
return all[i].duration > all[j].duration
|
||||
})
|
||||
|
||||
// Print actions accounting for 90% of the total.
|
||||
var sum time.Duration
|
||||
for _, act := range all {
|
||||
fmt.Fprintf(os.Stderr, "%s\t%s\n", act.duration, act)
|
||||
sum += act.duration
|
||||
if sum >= total*9/10 {
|
||||
break
|
||||
retDiags = append(retDiags, Diagnostic{Diagnostic: diag, AnalyzerName: act.a.Name, Position: posn})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return exitcode
|
||||
visitAll(roots)
|
||||
return
|
||||
}
|
||||
|
||||
// needFacts reports whether any analysis required by the specified set
|
||||
// NeedFacts reports whether any analysis required by the specified set
|
||||
// needs facts. If so, we must load the entire program from source.
|
||||
func needFacts(analyzers []*analysis.Analyzer) bool {
|
||||
func NeedFacts(analyzers []*analysis.Analyzer) bool {
|
||||
seen := make(map[*analysis.Analyzer]bool)
|
||||
var q []*analysis.Analyzer // for BFS
|
||||
q = append(q, analyzers...)
|
||||
|
Loading…
x
Reference in New Issue
Block a user