full diff: https://github.com/dominikh/go-tools/compare/2019.2.3...2020.1.3 Also updates tests to accomodate updated rules: --- FAIL: TestSourcesFromTestdataWithIssuesDir/staticcheck.go (0.43s) linters_test.go:137: [run --disable-all --print-issued-lines=false --print-linter-name=false --out-format=line-number --max-same-issues=10 -Estaticcheck --no-config testdata/staticcheck.go] linters_test.go:33: Error Trace: linters_test.go:33 linters_test.go:138 linters_test.go:53 Error: Received unexpected error: staticcheck.go:11: no match for `self-assignment of x to x` vs ["SA4006: this value of `x` is never used"] in: staticcheck.go:11:2: SA4006: this value of `x` is never used unmatched errors staticcheck.go:11:2: SA4006: this value of `x` is never used Test: TestSourcesFromTestdataWithIssuesDir/staticcheck.go Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
		
			
				
	
	
		
			1115 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1115 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package lint
 | 
						||
 | 
						||
/*
 | 
						||
Package loading
 | 
						||
 | 
						||
Conceptually, package loading in the runner can be imagined as a
 | 
						||
graph-shaped work list. We iteratively pop off leaf nodes (packages
 | 
						||
that have no unloaded dependencies) and load data from export data,
 | 
						||
our cache, or source.
 | 
						||
 | 
						||
Specifically, non-initial packages are loaded from export data and the
 | 
						||
fact cache if possible, otherwise from source. Initial packages are
 | 
						||
loaded from export data, the fact cache and the (problems, ignores,
 | 
						||
config) cache if possible, otherwise from source.
 | 
						||
 | 
						||
The appeal of this approach is that it is both simple to implement and
 | 
						||
easily parallelizable. Each leaf node can be processed independently,
 | 
						||
and new leaf nodes appear as their dependencies are being processed.
 | 
						||
 | 
						||
The downside of this approach, however, is that we're doing more work
 | 
						||
than necessary. Imagine an initial package A, which has the following
 | 
						||
dependency chain: A->B->C->D – in the current implementation, we will
 | 
						||
load all 4 packages. However, if package A can be loaded fully from
 | 
						||
cached information, then none of its dependencies are necessary, and
 | 
						||
we could avoid loading them.
 | 
						||
 | 
						||
 | 
						||
Parallelism
 | 
						||
 | 
						||
Runner implements parallel processing of packages by spawning one
 | 
						||
goroutine per package in the dependency graph, without any semaphores.
 | 
						||
Each goroutine initially waits on the completion of all of its
 | 
						||
dependencies, thus establishing correct order of processing. Once all
 | 
						||
dependencies finish processing, the goroutine will load the package
 | 
						||
from export data or source – this loading is guarded by a semaphore,
 | 
						||
sized according to the number of CPU cores. This way, we only have as
 | 
						||
many packages occupying memory and CPU resources as there are actual
 | 
						||
cores to process them.
 | 
						||
 | 
						||
This combination of unbounded goroutines but bounded package loading
 | 
						||
means that if we have many parallel, independent subgraphs, they will
 | 
						||
all execute in parallel, while not wasting resources for long linear
 | 
						||
chains or trying to process more subgraphs in parallel than the system
 | 
						||
can handle.
 | 
						||
 | 
						||
 | 
						||
Caching
 | 
						||
 | 
						||
We make use of several caches. These caches are Go's export data, our
 | 
						||
facts cache, and our (problems, ignores, config) cache.
 | 
						||
 | 
						||
Initial packages will either be loaded from a combination of all three
 | 
						||
caches, or from source. Non-initial packages will either be loaded
 | 
						||
from a combination of export data and facts cache, or from source.
 | 
						||
 | 
						||
The facts cache is separate from the (problems, ignores, config) cache
 | 
						||
because when we process non-initial packages, we generate facts, but
 | 
						||
we discard problems and ignores.
 | 
						||
 | 
						||
The facts cache is keyed by (package, analyzer), whereas the
 | 
						||
(problems, ignores, config) cache is keyed by (package, list of
 | 
						||
analyzes). The difference between the two exists because there are
 | 
						||
only a handful of analyses that produce facts, but hundreds of
 | 
						||
analyses that don't. Creating one cache entry per fact-generating
 | 
						||
analysis is feasible, creating one cache entry per normal analysis has
 | 
						||
significant performance and storage overheads.
 | 
						||
 | 
						||
The downside of keying by the list of analyzes is, naturally, that a
 | 
						||
change in list of analyzes changes the cache key. `staticcheck -checks
 | 
						||
A` and `staticcheck -checks A,B` will therefore need their own cache
 | 
						||
entries and not reuse each other's work. This problem does not affect
 | 
						||
the facts cache.
 | 
						||
 | 
						||
*/
 | 
						||
 | 
						||
import (
 | 
						||
	"bytes"
 | 
						||
	"encoding/gob"
 | 
						||
	"encoding/hex"
 | 
						||
	"fmt"
 | 
						||
	"go/ast"
 | 
						||
	"go/token"
 | 
						||
	"go/types"
 | 
						||
	"reflect"
 | 
						||
	"regexp"
 | 
						||
	"runtime"
 | 
						||
	"sort"
 | 
						||
	"strconv"
 | 
						||
	"strings"
 | 
						||
	"sync"
 | 
						||
	"sync/atomic"
 | 
						||
	"time"
 | 
						||
 | 
						||
	"golang.org/x/tools/go/analysis"
 | 
						||
	"golang.org/x/tools/go/packages"
 | 
						||
	"golang.org/x/tools/go/types/objectpath"
 | 
						||
	"honnef.co/go/tools/config"
 | 
						||
	"honnef.co/go/tools/facts"
 | 
						||
	"honnef.co/go/tools/internal/cache"
 | 
						||
	"honnef.co/go/tools/loader"
 | 
						||
)
 | 
						||
 | 
						||
func init() {
 | 
						||
	gob.Register(&FileIgnore{})
 | 
						||
	gob.Register(&LineIgnore{})
 | 
						||
}
 | 
						||
 | 
						||
// If enabled, abuse of the go/analysis API will lead to panics
 | 
						||
const sanityCheck = true
 | 
						||
 | 
						||
// OPT(dh): for a dependency tree A->B->C->D, if we have cached data
 | 
						||
// for B, there should be no need to load C and D individually. Go's
 | 
						||
// export data for B contains all the data we need on types, and our
 | 
						||
// fact cache could store the union of B, C and D in B.
 | 
						||
//
 | 
						||
// This may change unused's behavior, however, as it may observe fewer
 | 
						||
// interfaces from transitive dependencies.
 | 
						||
 | 
						||
// OPT(dh): every single package will have the same value for
 | 
						||
// canClearTypes. We could move the Package.decUse method to runner to
 | 
						||
// eliminate this field. This is probably not worth it, though. There
 | 
						||
// are only thousands of packages, so the field only takes up
 | 
						||
// kilobytes of memory.
 | 
						||
 | 
						||
// OPT(dh): do we really need the Package.gen field? it's based
 | 
						||
// trivially on pkg.results and merely caches the result of a type
 | 
						||
// assertion. How often do we actually use the field?
 | 
						||
 | 
						||
type Package struct {
 | 
						||
	// dependents is initially set to 1 plus the number of packages
 | 
						||
	// that directly import this package. It is atomically decreased
 | 
						||
	// by 1 every time a dependent has been processed or when the
 | 
						||
	// package itself has been processed. Once the value reaches zero,
 | 
						||
	// the package is no longer needed.
 | 
						||
	dependents uint64
 | 
						||
 | 
						||
	*packages.Package
 | 
						||
	Imports []*Package
 | 
						||
	initial bool
 | 
						||
	// fromSource is set to true for packages that have been loaded
 | 
						||
	// from source. This is the case for initial packages, packages
 | 
						||
	// with missing export data, and packages with no cached facts.
 | 
						||
	fromSource bool
 | 
						||
	// hash stores the package hash, as computed by packageHash
 | 
						||
	hash     string
 | 
						||
	actionID cache.ActionID
 | 
						||
	done     chan struct{}
 | 
						||
 | 
						||
	resultsMu sync.Mutex
 | 
						||
	// results maps analyzer IDs to analyzer results. it is
 | 
						||
	// implemented as a deduplicating concurrent cache.
 | 
						||
	results []*result
 | 
						||
 | 
						||
	cfg *config.Config
 | 
						||
	// gen maps file names to the code generator that created them
 | 
						||
	gen      map[string]facts.Generator
 | 
						||
	problems []Problem
 | 
						||
	ignores  []Ignore
 | 
						||
	errs     []error
 | 
						||
 | 
						||
	// these slices are indexed by analysis
 | 
						||
	facts    []map[types.Object][]analysis.Fact
 | 
						||
	pkgFacts [][]analysis.Fact
 | 
						||
 | 
						||
	// canClearTypes is set to true if we can discard type
 | 
						||
	// information after the package and its dependents have been
 | 
						||
	// processed. This is the case when no cumulative checkers are
 | 
						||
	// being run.
 | 
						||
	canClearTypes bool
 | 
						||
}
 | 
						||
 | 
						||
type cachedPackage struct {
 | 
						||
	Problems []Problem
 | 
						||
	Ignores  []Ignore
 | 
						||
	Config   *config.Config
 | 
						||
}
 | 
						||
 | 
						||
func (pkg *Package) decUse() {
 | 
						||
	ret := atomic.AddUint64(&pkg.dependents, ^uint64(0))
 | 
						||
	if ret == 0 {
 | 
						||
		// nobody depends on this package anymore
 | 
						||
		if pkg.canClearTypes {
 | 
						||
			pkg.Types = nil
 | 
						||
		}
 | 
						||
		pkg.facts = nil
 | 
						||
		pkg.pkgFacts = nil
 | 
						||
 | 
						||
		for _, imp := range pkg.Imports {
 | 
						||
			imp.decUse()
 | 
						||
		}
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
type result struct {
 | 
						||
	v     interface{}
 | 
						||
	err   error
 | 
						||
	ready chan struct{}
 | 
						||
}
 | 
						||
 | 
						||
type Runner struct {
 | 
						||
	cache           *cache.Cache
 | 
						||
	goVersion       int
 | 
						||
	stats           *Stats
 | 
						||
	repeatAnalyzers uint
 | 
						||
 | 
						||
	analyzerIDs      analyzerIDs
 | 
						||
	problemsCacheKey string
 | 
						||
 | 
						||
	// limits parallelism of loading packages
 | 
						||
	loadSem chan struct{}
 | 
						||
}
 | 
						||
 | 
						||
type analyzerIDs struct {
 | 
						||
	m map[*analysis.Analyzer]int
 | 
						||
}
 | 
						||
 | 
						||
func (ids analyzerIDs) get(a *analysis.Analyzer) int {
 | 
						||
	id, ok := ids.m[a]
 | 
						||
	if !ok {
 | 
						||
		panic(fmt.Sprintf("no analyzer ID for %s", a.Name))
 | 
						||
	}
 | 
						||
	return id
 | 
						||
}
 | 
						||
 | 
						||
type Fact struct {
 | 
						||
	Path string
 | 
						||
	Fact analysis.Fact
 | 
						||
}
 | 
						||
 | 
						||
type analysisAction struct {
 | 
						||
	analyzer        *analysis.Analyzer
 | 
						||
	analyzerID      int
 | 
						||
	pkg             *Package
 | 
						||
	newPackageFacts []analysis.Fact
 | 
						||
	problems        []Problem
 | 
						||
 | 
						||
	pkgFacts map[*types.Package][]analysis.Fact
 | 
						||
}
 | 
						||
 | 
						||
func (ac *analysisAction) String() string {
 | 
						||
	return fmt.Sprintf("%s @ %s", ac.analyzer, ac.pkg)
 | 
						||
}
 | 
						||
 | 
						||
func (ac *analysisAction) allObjectFacts() []analysis.ObjectFact {
 | 
						||
	out := make([]analysis.ObjectFact, 0, len(ac.pkg.facts[ac.analyzerID]))
 | 
						||
	for obj, facts := range ac.pkg.facts[ac.analyzerID] {
 | 
						||
		for _, fact := range facts {
 | 
						||
			out = append(out, analysis.ObjectFact{
 | 
						||
				Object: obj,
 | 
						||
				Fact:   fact,
 | 
						||
			})
 | 
						||
		}
 | 
						||
	}
 | 
						||
	return out
 | 
						||
}
 | 
						||
 | 
						||
func (ac *analysisAction) allPackageFacts() []analysis.PackageFact {
 | 
						||
	out := make([]analysis.PackageFact, 0, len(ac.pkgFacts))
 | 
						||
	for pkg, facts := range ac.pkgFacts {
 | 
						||
		for _, fact := range facts {
 | 
						||
			out = append(out, analysis.PackageFact{
 | 
						||
				Package: pkg,
 | 
						||
				Fact:    fact,
 | 
						||
			})
 | 
						||
		}
 | 
						||
	}
 | 
						||
	return out
 | 
						||
}
 | 
						||
 | 
						||
func (ac *analysisAction) importObjectFact(obj types.Object, fact analysis.Fact) bool {
 | 
						||
	if sanityCheck && len(ac.analyzer.FactTypes) == 0 {
 | 
						||
		panic("analysis doesn't export any facts")
 | 
						||
	}
 | 
						||
	for _, f := range ac.pkg.facts[ac.analyzerID][obj] {
 | 
						||
		if reflect.TypeOf(f) == reflect.TypeOf(fact) {
 | 
						||
			reflect.ValueOf(fact).Elem().Set(reflect.ValueOf(f).Elem())
 | 
						||
			return true
 | 
						||
		}
 | 
						||
	}
 | 
						||
	return false
 | 
						||
}
 | 
						||
 | 
						||
func (ac *analysisAction) importPackageFact(pkg *types.Package, fact analysis.Fact) bool {
 | 
						||
	if sanityCheck && len(ac.analyzer.FactTypes) == 0 {
 | 
						||
		panic("analysis doesn't export any facts")
 | 
						||
	}
 | 
						||
	for _, f := range ac.pkgFacts[pkg] {
 | 
						||
		if reflect.TypeOf(f) == reflect.TypeOf(fact) {
 | 
						||
			reflect.ValueOf(fact).Elem().Set(reflect.ValueOf(f).Elem())
 | 
						||
			return true
 | 
						||
		}
 | 
						||
	}
 | 
						||
	return false
 | 
						||
}
 | 
						||
 | 
						||
func (ac *analysisAction) exportObjectFact(obj types.Object, fact analysis.Fact) {
 | 
						||
	if sanityCheck && len(ac.analyzer.FactTypes) == 0 {
 | 
						||
		panic("analysis doesn't export any facts")
 | 
						||
	}
 | 
						||
	ac.pkg.facts[ac.analyzerID][obj] = append(ac.pkg.facts[ac.analyzerID][obj], fact)
 | 
						||
}
 | 
						||
 | 
						||
func (ac *analysisAction) exportPackageFact(fact analysis.Fact) {
 | 
						||
	if sanityCheck && len(ac.analyzer.FactTypes) == 0 {
 | 
						||
		panic("analysis doesn't export any facts")
 | 
						||
	}
 | 
						||
	ac.pkgFacts[ac.pkg.Types] = append(ac.pkgFacts[ac.pkg.Types], fact)
 | 
						||
	ac.newPackageFacts = append(ac.newPackageFacts, fact)
 | 
						||
}
 | 
						||
 | 
						||
func (ac *analysisAction) report(pass *analysis.Pass, d analysis.Diagnostic) {
 | 
						||
	p := Problem{
 | 
						||
		Pos:     DisplayPosition(pass.Fset, d.Pos),
 | 
						||
		End:     DisplayPosition(pass.Fset, d.End),
 | 
						||
		Message: d.Message,
 | 
						||
		Check:   pass.Analyzer.Name,
 | 
						||
	}
 | 
						||
	for _, r := range d.Related {
 | 
						||
		p.Related = append(p.Related, Related{
 | 
						||
			Pos:     DisplayPosition(pass.Fset, r.Pos),
 | 
						||
			End:     DisplayPosition(pass.Fset, r.End),
 | 
						||
			Message: r.Message,
 | 
						||
		})
 | 
						||
	}
 | 
						||
	ac.problems = append(ac.problems, p)
 | 
						||
}
 | 
						||
 | 
						||
func (r *Runner) runAnalysis(ac *analysisAction) (ret interface{}, err error) {
 | 
						||
	ac.pkg.resultsMu.Lock()
 | 
						||
	res := ac.pkg.results[r.analyzerIDs.get(ac.analyzer)]
 | 
						||
	if res != nil {
 | 
						||
		ac.pkg.resultsMu.Unlock()
 | 
						||
		<-res.ready
 | 
						||
		return res.v, res.err
 | 
						||
	} else {
 | 
						||
		res = &result{
 | 
						||
			ready: make(chan struct{}),
 | 
						||
		}
 | 
						||
		ac.pkg.results[r.analyzerIDs.get(ac.analyzer)] = res
 | 
						||
		ac.pkg.resultsMu.Unlock()
 | 
						||
 | 
						||
		defer func() {
 | 
						||
			res.v = ret
 | 
						||
			res.err = err
 | 
						||
			close(res.ready)
 | 
						||
		}()
 | 
						||
 | 
						||
		pass := new(analysis.Pass)
 | 
						||
		*pass = analysis.Pass{
 | 
						||
			Analyzer: ac.analyzer,
 | 
						||
			Fset:     ac.pkg.Fset,
 | 
						||
			Files:    ac.pkg.Syntax,
 | 
						||
			// type information may be nil or may be populated. if it is
 | 
						||
			// nil, it will get populated later.
 | 
						||
			Pkg:               ac.pkg.Types,
 | 
						||
			TypesInfo:         ac.pkg.TypesInfo,
 | 
						||
			TypesSizes:        ac.pkg.TypesSizes,
 | 
						||
			ResultOf:          map[*analysis.Analyzer]interface{}{},
 | 
						||
			ImportObjectFact:  ac.importObjectFact,
 | 
						||
			ImportPackageFact: ac.importPackageFact,
 | 
						||
			ExportObjectFact:  ac.exportObjectFact,
 | 
						||
			ExportPackageFact: ac.exportPackageFact,
 | 
						||
			Report: func(d analysis.Diagnostic) {
 | 
						||
				ac.report(pass, d)
 | 
						||
			},
 | 
						||
			AllObjectFacts:  ac.allObjectFacts,
 | 
						||
			AllPackageFacts: ac.allPackageFacts,
 | 
						||
		}
 | 
						||
 | 
						||
		if !ac.pkg.initial {
 | 
						||
			// Don't report problems in dependencies
 | 
						||
			pass.Report = func(analysis.Diagnostic) {}
 | 
						||
		}
 | 
						||
		return r.runAnalysisUser(pass, ac)
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
func (r *Runner) loadCachedPackage(pkg *Package, analyzers []*analysis.Analyzer) (cachedPackage, bool) {
 | 
						||
	// OPT(dh): we can cache this computation, it'll be the same for all packages
 | 
						||
	id := cache.Subkey(pkg.actionID, "data "+r.problemsCacheKey)
 | 
						||
 | 
						||
	b, _, err := r.cache.GetBytes(id)
 | 
						||
	if err != nil {
 | 
						||
		return cachedPackage{}, false
 | 
						||
	}
 | 
						||
	var cpkg cachedPackage
 | 
						||
	if err := gob.NewDecoder(bytes.NewReader(b)).Decode(&cpkg); err != nil {
 | 
						||
		return cachedPackage{}, false
 | 
						||
	}
 | 
						||
	return cpkg, true
 | 
						||
}
 | 
						||
 | 
						||
func (r *Runner) loadCachedFacts(a *analysis.Analyzer, pkg *Package) ([]Fact, bool) {
 | 
						||
	if len(a.FactTypes) == 0 {
 | 
						||
		return nil, true
 | 
						||
	}
 | 
						||
 | 
						||
	var facts []Fact
 | 
						||
	// Look in the cache for facts
 | 
						||
	aID := passActionID(pkg, a)
 | 
						||
	aID = cache.Subkey(aID, "facts")
 | 
						||
	b, _, err := r.cache.GetBytes(aID)
 | 
						||
	if err != nil {
 | 
						||
		// No cached facts, analyse this package like a user-provided one, but ignore diagnostics
 | 
						||
		return nil, false
 | 
						||
	}
 | 
						||
 | 
						||
	if err := gob.NewDecoder(bytes.NewReader(b)).Decode(&facts); err != nil {
 | 
						||
		// Cached facts are broken, analyse this package like a user-provided one, but ignore diagnostics
 | 
						||
		return nil, false
 | 
						||
	}
 | 
						||
	return facts, true
 | 
						||
}
 | 
						||
 | 
						||
type dependencyError struct {
 | 
						||
	dep string
 | 
						||
	err error
 | 
						||
}
 | 
						||
 | 
						||
func (err dependencyError) nested() dependencyError {
 | 
						||
	if o, ok := err.err.(dependencyError); ok {
 | 
						||
		return o.nested()
 | 
						||
	}
 | 
						||
	return err
 | 
						||
}
 | 
						||
 | 
						||
func (err dependencyError) Error() string {
 | 
						||
	if o, ok := err.err.(dependencyError); ok {
 | 
						||
		return o.Error()
 | 
						||
	}
 | 
						||
	return fmt.Sprintf("error running dependency %s: %s", err.dep, err.err)
 | 
						||
}
 | 
						||
 | 
						||
func (r *Runner) makeAnalysisAction(a *analysis.Analyzer, pkg *Package) *analysisAction {
 | 
						||
	aid := r.analyzerIDs.get(a)
 | 
						||
	ac := &analysisAction{
 | 
						||
		analyzer:   a,
 | 
						||
		analyzerID: aid,
 | 
						||
		pkg:        pkg,
 | 
						||
	}
 | 
						||
 | 
						||
	if len(a.FactTypes) == 0 {
 | 
						||
		return ac
 | 
						||
	}
 | 
						||
 | 
						||
	// Merge all package facts of dependencies
 | 
						||
	ac.pkgFacts = map[*types.Package][]analysis.Fact{}
 | 
						||
	seen := map[*Package]struct{}{}
 | 
						||
	var dfs func(*Package)
 | 
						||
	dfs = func(pkg *Package) {
 | 
						||
		if _, ok := seen[pkg]; ok {
 | 
						||
			return
 | 
						||
		}
 | 
						||
		seen[pkg] = struct{}{}
 | 
						||
		s := pkg.pkgFacts[aid]
 | 
						||
		ac.pkgFacts[pkg.Types] = s[0:len(s):len(s)]
 | 
						||
		for _, imp := range pkg.Imports {
 | 
						||
			dfs(imp)
 | 
						||
		}
 | 
						||
	}
 | 
						||
	dfs(pkg)
 | 
						||
 | 
						||
	return ac
 | 
						||
}
 | 
						||
 | 
						||
// analyzes that we always want to run, even if they're not being run
 | 
						||
// explicitly or as dependencies. these are necessary for the inner
 | 
						||
// workings of the runner.
 | 
						||
var injectedAnalyses = []*analysis.Analyzer{facts.Generated, config.Analyzer}
 | 
						||
 | 
						||
func (r *Runner) runAnalysisUser(pass *analysis.Pass, ac *analysisAction) (interface{}, error) {
 | 
						||
	if !ac.pkg.fromSource {
 | 
						||
		panic(fmt.Sprintf("internal error: %s was not loaded from source", ac.pkg))
 | 
						||
	}
 | 
						||
 | 
						||
	// User-provided package, analyse it
 | 
						||
	// First analyze it with dependencies
 | 
						||
	for _, req := range ac.analyzer.Requires {
 | 
						||
		acReq := r.makeAnalysisAction(req, ac.pkg)
 | 
						||
		ret, err := r.runAnalysis(acReq)
 | 
						||
		if err != nil {
 | 
						||
			// We couldn't run a dependency, no point in going on
 | 
						||
			return nil, dependencyError{req.Name, err}
 | 
						||
		}
 | 
						||
 | 
						||
		pass.ResultOf[req] = ret
 | 
						||
	}
 | 
						||
 | 
						||
	// Then with this analyzer
 | 
						||
	var ret interface{}
 | 
						||
	for i := uint(0); i < r.repeatAnalyzers+1; i++ {
 | 
						||
		var err error
 | 
						||
		t := time.Now()
 | 
						||
		ret, err = ac.analyzer.Run(pass)
 | 
						||
		r.stats.MeasureAnalyzer(ac.analyzer, ac.pkg, time.Since(t))
 | 
						||
		if err != nil {
 | 
						||
			return nil, err
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	if len(ac.analyzer.FactTypes) > 0 {
 | 
						||
		// Merge new facts into the package and persist them.
 | 
						||
		var facts []Fact
 | 
						||
		for _, fact := range ac.newPackageFacts {
 | 
						||
			id := r.analyzerIDs.get(ac.analyzer)
 | 
						||
			ac.pkg.pkgFacts[id] = append(ac.pkg.pkgFacts[id], fact)
 | 
						||
			facts = append(facts, Fact{"", fact})
 | 
						||
		}
 | 
						||
		for obj, afacts := range ac.pkg.facts[ac.analyzerID] {
 | 
						||
			if obj.Pkg() != ac.pkg.Package.Types {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
			path, err := objectpath.For(obj)
 | 
						||
			if err != nil {
 | 
						||
				continue
 | 
						||
			}
 | 
						||
			for _, fact := range afacts {
 | 
						||
				facts = append(facts, Fact{string(path), fact})
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		if err := r.cacheData(facts, ac.pkg, ac.analyzer, "facts"); err != nil {
 | 
						||
			return nil, err
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	return ret, nil
 | 
						||
}
 | 
						||
 | 
						||
func (r *Runner) cacheData(v interface{}, pkg *Package, a *analysis.Analyzer, subkey string) error {
 | 
						||
	buf := &bytes.Buffer{}
 | 
						||
	if err := gob.NewEncoder(buf).Encode(v); err != nil {
 | 
						||
		return err
 | 
						||
	}
 | 
						||
	aID := passActionID(pkg, a)
 | 
						||
	aID = cache.Subkey(aID, subkey)
 | 
						||
	if err := r.cache.PutBytes(aID, buf.Bytes()); err != nil {
 | 
						||
		return err
 | 
						||
	}
 | 
						||
	return nil
 | 
						||
}
 | 
						||
 | 
						||
func NewRunner(stats *Stats) (*Runner, error) {
 | 
						||
	cache, err := cache.Default()
 | 
						||
	if err != nil {
 | 
						||
		return nil, err
 | 
						||
	}
 | 
						||
 | 
						||
	return &Runner{
 | 
						||
		cache: cache,
 | 
						||
		stats: stats,
 | 
						||
	}, nil
 | 
						||
}
 | 
						||
 | 
						||
// Run loads packages corresponding to patterns and analyses them with
 | 
						||
// analyzers. It returns the loaded packages, which contain reported
 | 
						||
// diagnostics as well as extracted ignore directives.
 | 
						||
//
 | 
						||
// Note that diagnostics have not been filtered at this point yet, to
 | 
						||
// accommodate cumulative analyzes that require additional steps to
 | 
						||
// produce diagnostics.
 | 
						||
func (r *Runner) Run(cfg *packages.Config, patterns []string, analyzers []*analysis.Analyzer, hasCumulative bool) ([]*Package, error) {
 | 
						||
	checkerNames := make([]string, len(analyzers))
 | 
						||
	for i, a := range analyzers {
 | 
						||
		checkerNames[i] = a.Name
 | 
						||
	}
 | 
						||
	sort.Strings(checkerNames)
 | 
						||
	r.problemsCacheKey = strings.Join(checkerNames, " ")
 | 
						||
 | 
						||
	var allAnalyzers []*analysis.Analyzer
 | 
						||
	r.analyzerIDs = analyzerIDs{m: map[*analysis.Analyzer]int{}}
 | 
						||
	id := 0
 | 
						||
	seen := map[*analysis.Analyzer]struct{}{}
 | 
						||
	var dfs func(a *analysis.Analyzer)
 | 
						||
	dfs = func(a *analysis.Analyzer) {
 | 
						||
		if _, ok := seen[a]; ok {
 | 
						||
			return
 | 
						||
		}
 | 
						||
		seen[a] = struct{}{}
 | 
						||
		allAnalyzers = append(allAnalyzers, a)
 | 
						||
		r.analyzerIDs.m[a] = id
 | 
						||
		id++
 | 
						||
		for _, f := range a.FactTypes {
 | 
						||
			gob.Register(f)
 | 
						||
		}
 | 
						||
		for _, req := range a.Requires {
 | 
						||
			dfs(req)
 | 
						||
		}
 | 
						||
	}
 | 
						||
	for _, a := range analyzers {
 | 
						||
		if v := a.Flags.Lookup("go"); v != nil {
 | 
						||
			v.Value.Set(fmt.Sprintf("1.%d", r.goVersion))
 | 
						||
		}
 | 
						||
		dfs(a)
 | 
						||
	}
 | 
						||
	for _, a := range injectedAnalyses {
 | 
						||
		dfs(a)
 | 
						||
	}
 | 
						||
	// Run all analyzers on all packages (subject to further
 | 
						||
	// restrictions enforced later). This guarantees that if analyzer
 | 
						||
	// A1 depends on A2, and A2 has facts, that A2 will run on the
 | 
						||
	// dependencies of user-provided packages, even though A1 won't.
 | 
						||
	analyzers = allAnalyzers
 | 
						||
 | 
						||
	var dcfg packages.Config
 | 
						||
	if cfg != nil {
 | 
						||
		dcfg = *cfg
 | 
						||
	}
 | 
						||
 | 
						||
	atomic.StoreUint32(&r.stats.State, StateGraph)
 | 
						||
	initialPkgs, err := loader.Graph(dcfg, patterns...)
 | 
						||
	if err != nil {
 | 
						||
		return nil, err
 | 
						||
	}
 | 
						||
	defer r.cache.Trim()
 | 
						||
 | 
						||
	var allPkgs []*Package
 | 
						||
	m := map[*packages.Package]*Package{}
 | 
						||
	packages.Visit(initialPkgs, nil, func(l *packages.Package) {
 | 
						||
		m[l] = &Package{
 | 
						||
			Package:  l,
 | 
						||
			results:  make([]*result, len(r.analyzerIDs.m)),
 | 
						||
			facts:    make([]map[types.Object][]analysis.Fact, len(r.analyzerIDs.m)),
 | 
						||
			pkgFacts: make([][]analysis.Fact, len(r.analyzerIDs.m)),
 | 
						||
			done:     make(chan struct{}),
 | 
						||
			// every package needs itself
 | 
						||
			dependents:    1,
 | 
						||
			canClearTypes: !hasCumulative,
 | 
						||
		}
 | 
						||
		allPkgs = append(allPkgs, m[l])
 | 
						||
		for i := range m[l].facts {
 | 
						||
			m[l].facts[i] = map[types.Object][]analysis.Fact{}
 | 
						||
		}
 | 
						||
		for _, err := range l.Errors {
 | 
						||
			m[l].errs = append(m[l].errs, err)
 | 
						||
		}
 | 
						||
		for _, v := range l.Imports {
 | 
						||
			m[v].dependents++
 | 
						||
			m[l].Imports = append(m[l].Imports, m[v])
 | 
						||
		}
 | 
						||
 | 
						||
		m[l].hash, err = r.packageHash(m[l])
 | 
						||
		m[l].actionID = packageActionID(m[l])
 | 
						||
		if err != nil {
 | 
						||
			m[l].errs = append(m[l].errs, err)
 | 
						||
		}
 | 
						||
	})
 | 
						||
 | 
						||
	pkgs := make([]*Package, len(initialPkgs))
 | 
						||
	for i, l := range initialPkgs {
 | 
						||
		pkgs[i] = m[l]
 | 
						||
		pkgs[i].initial = true
 | 
						||
	}
 | 
						||
 | 
						||
	atomic.StoreUint32(&r.stats.InitialPackages, uint32(len(initialPkgs)))
 | 
						||
	atomic.StoreUint32(&r.stats.TotalPackages, uint32(len(allPkgs)))
 | 
						||
	atomic.StoreUint32(&r.stats.State, StateProcessing)
 | 
						||
 | 
						||
	var wg sync.WaitGroup
 | 
						||
	wg.Add(len(allPkgs))
 | 
						||
	r.loadSem = make(chan struct{}, runtime.GOMAXPROCS(-1))
 | 
						||
	atomic.StoreUint32(&r.stats.TotalWorkers, uint32(cap(r.loadSem)))
 | 
						||
	for _, pkg := range allPkgs {
 | 
						||
		pkg := pkg
 | 
						||
		go func() {
 | 
						||
			r.processPkg(pkg, analyzers)
 | 
						||
 | 
						||
			if pkg.initial {
 | 
						||
				atomic.AddUint32(&r.stats.ProcessedInitialPackages, 1)
 | 
						||
			}
 | 
						||
			atomic.AddUint32(&r.stats.Problems, uint32(len(pkg.problems)))
 | 
						||
			wg.Done()
 | 
						||
		}()
 | 
						||
	}
 | 
						||
	wg.Wait()
 | 
						||
 | 
						||
	return pkgs, nil
 | 
						||
}
 | 
						||
 | 
						||
var posRe = regexp.MustCompile(`^(.+?):(\d+)(?::(\d+)?)?`)
 | 
						||
 | 
						||
func parsePos(pos string) (token.Position, int, error) {
 | 
						||
	if pos == "-" || pos == "" {
 | 
						||
		return token.Position{}, 0, nil
 | 
						||
	}
 | 
						||
	parts := posRe.FindStringSubmatch(pos)
 | 
						||
	if parts == nil {
 | 
						||
		return token.Position{}, 0, fmt.Errorf("malformed position %q", pos)
 | 
						||
	}
 | 
						||
	file := parts[1]
 | 
						||
	line, _ := strconv.Atoi(parts[2])
 | 
						||
	col, _ := strconv.Atoi(parts[3])
 | 
						||
	return token.Position{
 | 
						||
		Filename: file,
 | 
						||
		Line:     line,
 | 
						||
		Column:   col,
 | 
						||
	}, len(parts[0]), nil
 | 
						||
}
 | 
						||
 | 
						||
// loadPkg loads a Go package. It may be loaded from a combination of
 | 
						||
// caches, or from source.
 | 
						||
func (r *Runner) loadPkg(pkg *Package, analyzers []*analysis.Analyzer) error {
 | 
						||
	if pkg.Types != nil {
 | 
						||
		panic(fmt.Sprintf("internal error: %s has already been loaded", pkg.Package))
 | 
						||
	}
 | 
						||
 | 
						||
	if pkg.initial {
 | 
						||
		// Try to load cached package
 | 
						||
		cpkg, ok := r.loadCachedPackage(pkg, analyzers)
 | 
						||
		if ok {
 | 
						||
			pkg.problems = cpkg.Problems
 | 
						||
			pkg.ignores = cpkg.Ignores
 | 
						||
			pkg.cfg = cpkg.Config
 | 
						||
		} else {
 | 
						||
			pkg.fromSource = true
 | 
						||
			return loader.LoadFromSource(pkg.Package)
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// At this point we're either working with a non-initial package,
 | 
						||
	// or we managed to load cached problems for the package. We still
 | 
						||
	// need export data and facts.
 | 
						||
 | 
						||
	// OPT(dh): we don't need type information for this package if no
 | 
						||
	// other package depends on it. this may be the case for initial
 | 
						||
	// packages.
 | 
						||
 | 
						||
	// Load package from export data
 | 
						||
	if err := loader.LoadFromExport(pkg.Package); err != nil {
 | 
						||
		// We asked Go to give us up to date export data, yet
 | 
						||
		// we can't load it. There must be something wrong.
 | 
						||
		//
 | 
						||
		// Attempt loading from source. This should fail (because
 | 
						||
		// otherwise there would be export data); we just want to
 | 
						||
		// get the compile errors. If loading from source succeeds
 | 
						||
		// we discard the result, anyway. Otherwise we'll fail
 | 
						||
		// when trying to reload from export data later.
 | 
						||
		//
 | 
						||
		// FIXME(dh): we no longer reload from export data, so
 | 
						||
		// theoretically we should be able to continue
 | 
						||
		pkg.fromSource = true
 | 
						||
		if err := loader.LoadFromSource(pkg.Package); err != nil {
 | 
						||
			return err
 | 
						||
		}
 | 
						||
		// Make sure this package can't be imported successfully
 | 
						||
		pkg.Package.Errors = append(pkg.Package.Errors, packages.Error{
 | 
						||
			Pos:  "-",
 | 
						||
			Msg:  fmt.Sprintf("could not load export data: %s", err),
 | 
						||
			Kind: packages.ParseError,
 | 
						||
		})
 | 
						||
		return fmt.Errorf("could not load export data: %s", err)
 | 
						||
	}
 | 
						||
 | 
						||
	failed := false
 | 
						||
	seen := make([]bool, len(r.analyzerIDs.m))
 | 
						||
	var dfs func(*analysis.Analyzer)
 | 
						||
	dfs = func(a *analysis.Analyzer) {
 | 
						||
		if seen[r.analyzerIDs.get(a)] {
 | 
						||
			return
 | 
						||
		}
 | 
						||
		seen[r.analyzerIDs.get(a)] = true
 | 
						||
 | 
						||
		if len(a.FactTypes) > 0 {
 | 
						||
			facts, ok := r.loadCachedFacts(a, pkg)
 | 
						||
			if !ok {
 | 
						||
				failed = true
 | 
						||
				return
 | 
						||
			}
 | 
						||
 | 
						||
			for _, f := range facts {
 | 
						||
				if f.Path == "" {
 | 
						||
					// This is a package fact
 | 
						||
					pkg.pkgFacts[r.analyzerIDs.get(a)] = append(pkg.pkgFacts[r.analyzerIDs.get(a)], f.Fact)
 | 
						||
					continue
 | 
						||
				}
 | 
						||
				obj, err := objectpath.Object(pkg.Types, objectpath.Path(f.Path))
 | 
						||
				if err != nil {
 | 
						||
					// Be lenient about these errors. For example, when
 | 
						||
					// analysing io/ioutil from source, we may get a fact
 | 
						||
					// for methods on the devNull type, and objectpath
 | 
						||
					// will happily create a path for them. However, when
 | 
						||
					// we later load io/ioutil from export data, the path
 | 
						||
					// no longer resolves.
 | 
						||
					//
 | 
						||
					// If an exported type embeds the unexported type,
 | 
						||
					// then (part of) the unexported type will become part
 | 
						||
					// of the type information and our path will resolve
 | 
						||
					// again.
 | 
						||
					continue
 | 
						||
				}
 | 
						||
				pkg.facts[r.analyzerIDs.get(a)][obj] = append(pkg.facts[r.analyzerIDs.get(a)][obj], f.Fact)
 | 
						||
			}
 | 
						||
		}
 | 
						||
 | 
						||
		for _, req := range a.Requires {
 | 
						||
			dfs(req)
 | 
						||
		}
 | 
						||
	}
 | 
						||
	for _, a := range analyzers {
 | 
						||
		dfs(a)
 | 
						||
	}
 | 
						||
 | 
						||
	if !failed {
 | 
						||
		return nil
 | 
						||
	}
 | 
						||
 | 
						||
	// We failed to load some cached facts
 | 
						||
	pkg.fromSource = true
 | 
						||
	// XXX we added facts to the maps, we need to get rid of those
 | 
						||
	return loader.LoadFromSource(pkg.Package)
 | 
						||
}
 | 
						||
 | 
						||
type analysisError struct {
 | 
						||
	analyzer *analysis.Analyzer
 | 
						||
	pkg      *Package
 | 
						||
	err      error
 | 
						||
}
 | 
						||
 | 
						||
func (err analysisError) Error() string {
 | 
						||
	return fmt.Sprintf("error running analyzer %s on %s: %s", err.analyzer, err.pkg, err.err)
 | 
						||
}
 | 
						||
 | 
						||
// processPkg processes a package. This involves loading the package,
 | 
						||
// either from export data or from source. For packages loaded from
 | 
						||
// source, the provides analyzers will be run on the package.
 | 
						||
func (r *Runner) processPkg(pkg *Package, analyzers []*analysis.Analyzer) {
 | 
						||
	defer func() {
 | 
						||
		// Clear information we no longer need. Make sure to do this
 | 
						||
		// when returning from processPkg so that we clear
 | 
						||
		// dependencies, not just initial packages.
 | 
						||
		pkg.TypesInfo = nil
 | 
						||
		pkg.Syntax = nil
 | 
						||
		pkg.results = nil
 | 
						||
 | 
						||
		atomic.AddUint32(&r.stats.ProcessedPackages, 1)
 | 
						||
		pkg.decUse()
 | 
						||
		close(pkg.done)
 | 
						||
	}()
 | 
						||
 | 
						||
	// Ensure all packages have the generated map and config. This is
 | 
						||
	// required by internals of the runner. Analyses that themselves
 | 
						||
	// make use of either have an explicit dependency so that other
 | 
						||
	// runners work correctly, too.
 | 
						||
	analyzers = append(analyzers[0:len(analyzers):len(analyzers)], injectedAnalyses...)
 | 
						||
 | 
						||
	if len(pkg.errs) != 0 {
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	for _, imp := range pkg.Imports {
 | 
						||
		<-imp.done
 | 
						||
		if len(imp.errs) > 0 {
 | 
						||
			if imp.initial {
 | 
						||
				// Don't print the error of the dependency since it's
 | 
						||
				// an initial package and we're already printing the
 | 
						||
				// error.
 | 
						||
				pkg.errs = append(pkg.errs, fmt.Errorf("could not analyze dependency %s of %s", imp, pkg))
 | 
						||
			} else {
 | 
						||
				var s string
 | 
						||
				for _, err := range imp.errs {
 | 
						||
					s += "\n\t" + err.Error()
 | 
						||
				}
 | 
						||
				pkg.errs = append(pkg.errs, fmt.Errorf("could not analyze dependency %s of %s: %s", imp, pkg, s))
 | 
						||
			}
 | 
						||
			return
 | 
						||
		}
 | 
						||
	}
 | 
						||
	if pkg.PkgPath == "unsafe" {
 | 
						||
		pkg.Types = types.Unsafe
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	r.loadSem <- struct{}{}
 | 
						||
	atomic.AddUint32(&r.stats.ActiveWorkers, 1)
 | 
						||
	defer func() {
 | 
						||
		<-r.loadSem
 | 
						||
		atomic.AddUint32(&r.stats.ActiveWorkers, ^uint32(0))
 | 
						||
	}()
 | 
						||
	if err := r.loadPkg(pkg, analyzers); err != nil {
 | 
						||
		pkg.errs = append(pkg.errs, err)
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	// A package's object facts is the union of all of its dependencies.
 | 
						||
	for _, imp := range pkg.Imports {
 | 
						||
		for ai, m := range imp.facts {
 | 
						||
			for obj, facts := range m {
 | 
						||
				pkg.facts[ai][obj] = facts[0:len(facts):len(facts)]
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	if !pkg.fromSource {
 | 
						||
		// Nothing left to do for the package.
 | 
						||
		return
 | 
						||
	}
 | 
						||
 | 
						||
	// Run analyses on initial packages and those missing facts
 | 
						||
	var wg sync.WaitGroup
 | 
						||
	wg.Add(len(analyzers))
 | 
						||
	errs := make([]error, len(analyzers))
 | 
						||
	var acs []*analysisAction
 | 
						||
	for i, a := range analyzers {
 | 
						||
		i := i
 | 
						||
		a := a
 | 
						||
		ac := r.makeAnalysisAction(a, pkg)
 | 
						||
		acs = append(acs, ac)
 | 
						||
		go func() {
 | 
						||
			defer wg.Done()
 | 
						||
			// Only initial packages and packages with missing
 | 
						||
			// facts will have been loaded from source.
 | 
						||
			if pkg.initial || len(a.FactTypes) > 0 {
 | 
						||
				if _, err := r.runAnalysis(ac); err != nil {
 | 
						||
					errs[i] = analysisError{a, pkg, err}
 | 
						||
					return
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}()
 | 
						||
	}
 | 
						||
	wg.Wait()
 | 
						||
 | 
						||
	depErrors := map[dependencyError]int{}
 | 
						||
	for _, err := range errs {
 | 
						||
		if err == nil {
 | 
						||
			continue
 | 
						||
		}
 | 
						||
		switch err := err.(type) {
 | 
						||
		case analysisError:
 | 
						||
			switch err := err.err.(type) {
 | 
						||
			case dependencyError:
 | 
						||
				depErrors[err.nested()]++
 | 
						||
			default:
 | 
						||
				pkg.errs = append(pkg.errs, err)
 | 
						||
			}
 | 
						||
		default:
 | 
						||
			pkg.errs = append(pkg.errs, err)
 | 
						||
		}
 | 
						||
	}
 | 
						||
	for err, count := range depErrors {
 | 
						||
		pkg.errs = append(pkg.errs,
 | 
						||
			fmt.Errorf("could not run %s@%s, preventing %d analyzers from running: %s", err.dep, pkg, count, err.err))
 | 
						||
	}
 | 
						||
 | 
						||
	// We can't process ignores at this point because `unused` needs
 | 
						||
	// to see more than one package to make its decision.
 | 
						||
	//
 | 
						||
	// OPT(dh): can't we guard this block of code by pkg.initial?
 | 
						||
	ignores, problems := parseDirectives(pkg.Package)
 | 
						||
	pkg.ignores = append(pkg.ignores, ignores...)
 | 
						||
	pkg.problems = append(pkg.problems, problems...)
 | 
						||
	for _, ac := range acs {
 | 
						||
		pkg.problems = append(pkg.problems, ac.problems...)
 | 
						||
	}
 | 
						||
 | 
						||
	if pkg.initial {
 | 
						||
		// Only initial packages have these analyzers run, and only
 | 
						||
		// initial packages need these.
 | 
						||
		if pkg.results[r.analyzerIDs.get(config.Analyzer)].v != nil {
 | 
						||
			pkg.cfg = pkg.results[r.analyzerIDs.get(config.Analyzer)].v.(*config.Config)
 | 
						||
		}
 | 
						||
		pkg.gen = pkg.results[r.analyzerIDs.get(facts.Generated)].v.(map[string]facts.Generator)
 | 
						||
	}
 | 
						||
 | 
						||
	// In a previous version of the code, we would throw away all type
 | 
						||
	// information and reload it from export data. That was
 | 
						||
	// nonsensical. The *types.Package doesn't keep any information
 | 
						||
	// live that export data wouldn't also. We only need to discard
 | 
						||
	// the AST and the TypesInfo maps; that happens after we return
 | 
						||
	// from processPkg.
 | 
						||
}
 | 
						||
 | 
						||
func parseDirective(s string) (cmd string, args []string) {
 | 
						||
	if !strings.HasPrefix(s, "//lint:") {
 | 
						||
		return "", nil
 | 
						||
	}
 | 
						||
	s = strings.TrimPrefix(s, "//lint:")
 | 
						||
	fields := strings.Split(s, " ")
 | 
						||
	return fields[0], fields[1:]
 | 
						||
}
 | 
						||
 | 
						||
// parseDirectives extracts all linter directives from the source
 | 
						||
// files of the package. Malformed directives are returned as problems.
 | 
						||
func parseDirectives(pkg *packages.Package) ([]Ignore, []Problem) {
 | 
						||
	var ignores []Ignore
 | 
						||
	var problems []Problem
 | 
						||
 | 
						||
	for _, f := range pkg.Syntax {
 | 
						||
		found := false
 | 
						||
	commentLoop:
 | 
						||
		for _, cg := range f.Comments {
 | 
						||
			for _, c := range cg.List {
 | 
						||
				if strings.Contains(c.Text, "//lint:") {
 | 
						||
					found = true
 | 
						||
					break commentLoop
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
		if !found {
 | 
						||
			continue
 | 
						||
		}
 | 
						||
		cm := ast.NewCommentMap(pkg.Fset, f, f.Comments)
 | 
						||
		for node, cgs := range cm {
 | 
						||
			for _, cg := range cgs {
 | 
						||
				for _, c := range cg.List {
 | 
						||
					if !strings.HasPrefix(c.Text, "//lint:") {
 | 
						||
						continue
 | 
						||
					}
 | 
						||
					cmd, args := parseDirective(c.Text)
 | 
						||
					switch cmd {
 | 
						||
					case "ignore", "file-ignore":
 | 
						||
						if len(args) < 2 {
 | 
						||
							p := Problem{
 | 
						||
								Pos:      DisplayPosition(pkg.Fset, c.Pos()),
 | 
						||
								Message:  "malformed linter directive; missing the required reason field?",
 | 
						||
								Severity: Error,
 | 
						||
								Check:    "compile",
 | 
						||
							}
 | 
						||
							problems = append(problems, p)
 | 
						||
							continue
 | 
						||
						}
 | 
						||
					default:
 | 
						||
						// unknown directive, ignore
 | 
						||
						continue
 | 
						||
					}
 | 
						||
					checks := strings.Split(args[0], ",")
 | 
						||
					pos := DisplayPosition(pkg.Fset, node.Pos())
 | 
						||
					var ig Ignore
 | 
						||
					switch cmd {
 | 
						||
					case "ignore":
 | 
						||
						ig = &LineIgnore{
 | 
						||
							File:   pos.Filename,
 | 
						||
							Line:   pos.Line,
 | 
						||
							Checks: checks,
 | 
						||
							Pos:    DisplayPosition(pkg.Fset, c.Pos()),
 | 
						||
						}
 | 
						||
					case "file-ignore":
 | 
						||
						ig = &FileIgnore{
 | 
						||
							File:   pos.Filename,
 | 
						||
							Checks: checks,
 | 
						||
						}
 | 
						||
					}
 | 
						||
					ignores = append(ignores, ig)
 | 
						||
				}
 | 
						||
			}
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	return ignores, problems
 | 
						||
}
 | 
						||
 | 
						||
// packageHash computes a package's hash. The hash is based on all Go
 | 
						||
// files that make up the package, as well as the hashes of imported
 | 
						||
// packages.
 | 
						||
func (r *Runner) packageHash(pkg *Package) (string, error) {
 | 
						||
	key := cache.NewHash("package hash")
 | 
						||
	fmt.Fprintf(key, "pkgpath %s\n", pkg.PkgPath)
 | 
						||
	fmt.Fprintf(key, "go %d\n", r.goVersion)
 | 
						||
	for _, f := range pkg.CompiledGoFiles {
 | 
						||
		h, err := cache.FileHash(f)
 | 
						||
		if err != nil {
 | 
						||
			return "", err
 | 
						||
		}
 | 
						||
		fmt.Fprintf(key, "file %s %x\n", f, h)
 | 
						||
	}
 | 
						||
 | 
						||
	// Actually load the configuration to calculate its hash. This
 | 
						||
	// will take into consideration inheritance of configuration
 | 
						||
	// files, as well as the default configuration.
 | 
						||
	//
 | 
						||
	// OPT(dh): doing this means we'll load the config twice: once for
 | 
						||
	// computing the hash, and once when analyzing the package from
 | 
						||
	// source.
 | 
						||
	cdir := config.Dir(pkg.GoFiles)
 | 
						||
	if cdir == "" {
 | 
						||
		fmt.Fprintf(key, "file %s %x\n", config.ConfigName, [cache.HashSize]byte{})
 | 
						||
	} else {
 | 
						||
		cfg, err := config.Load(cdir)
 | 
						||
		if err != nil {
 | 
						||
			return "", err
 | 
						||
		}
 | 
						||
		h := cache.NewHash(config.ConfigName)
 | 
						||
		if _, err := h.Write([]byte(cfg.String())); err != nil {
 | 
						||
			return "", err
 | 
						||
		}
 | 
						||
		fmt.Fprintf(key, "file %s %x\n", config.ConfigName, h.Sum())
 | 
						||
	}
 | 
						||
 | 
						||
	imps := make([]*Package, len(pkg.Imports))
 | 
						||
	copy(imps, pkg.Imports)
 | 
						||
	sort.Slice(imps, func(i, j int) bool {
 | 
						||
		return imps[i].PkgPath < imps[j].PkgPath
 | 
						||
	})
 | 
						||
	for _, dep := range imps {
 | 
						||
		if dep.PkgPath == "unsafe" {
 | 
						||
			continue
 | 
						||
		}
 | 
						||
 | 
						||
		fmt.Fprintf(key, "import %s %s\n", dep.PkgPath, dep.hash)
 | 
						||
	}
 | 
						||
	h := key.Sum()
 | 
						||
	return hex.EncodeToString(h[:]), nil
 | 
						||
}
 | 
						||
 | 
						||
func packageActionID(pkg *Package) cache.ActionID {
 | 
						||
	key := cache.NewHash("package ID")
 | 
						||
	fmt.Fprintf(key, "pkgpath %s\n", pkg.PkgPath)
 | 
						||
	fmt.Fprintf(key, "pkghash %s\n", pkg.hash)
 | 
						||
	return key.Sum()
 | 
						||
}
 | 
						||
 | 
						||
// passActionID computes an ActionID for an analysis pass.
 | 
						||
func passActionID(pkg *Package, analyzer *analysis.Analyzer) cache.ActionID {
 | 
						||
	return cache.Subkey(pkg.actionID, fmt.Sprintf("analyzer %s", analyzer.Name))
 | 
						||
}
 |