github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/pkg/golinters/goanalysis/runner_action.go (about)

     1  package goanalysis
     2  
     3  import (
     4  	"fmt"
     5  	"go/types"
     6  	"reflect"
     7  	"runtime/debug"
     8  	"time"
     9  
    10  	"github.com/hashicorp/go-multierror"
    11  	"github.com/pkg/errors"
    12  	"golang.org/x/tools/go/analysis"
    13  	"golang.org/x/tools/go/packages"
    14  	"golang.org/x/tools/go/types/objectpath"
    15  
    16  	"github.com/elek/golangci-lint/internal/errorutil"
    17  	"github.com/elek/golangci-lint/internal/pkgcache"
    18  )
    19  
    20  type actionAllocator struct {
    21  	allocatedActions []action
    22  	nextFreeIndex    int
    23  }
    24  
    25  func newActionAllocator(maxCount int) *actionAllocator {
    26  	return &actionAllocator{
    27  		allocatedActions: make([]action, maxCount),
    28  		nextFreeIndex:    0,
    29  	}
    30  }
    31  
    32  func (actAlloc *actionAllocator) alloc() *action {
    33  	if actAlloc.nextFreeIndex == len(actAlloc.allocatedActions) {
    34  		panic(fmt.Sprintf("Made too many allocations of actions: %d allowed", len(actAlloc.allocatedActions)))
    35  	}
    36  	act := &actAlloc.allocatedActions[actAlloc.nextFreeIndex]
    37  	actAlloc.nextFreeIndex++
    38  	return act
    39  }
    40  
    41  // An action represents one unit of analysis work: the application of
    42  // one analysis to one package. Actions form a DAG, both within a
    43  // package (as different analyzers are applied, either in sequence or
    44  // parallel), and across packages (as dependencies are analyzed).
    45  type action struct {
    46  	a                   *analysis.Analyzer
    47  	pkg                 *packages.Package
    48  	pass                *analysis.Pass
    49  	deps                []*action
    50  	objectFacts         map[objectFactKey]analysis.Fact
    51  	packageFacts        map[packageFactKey]analysis.Fact
    52  	result              interface{}
    53  	diagnostics         []analysis.Diagnostic
    54  	err                 error
    55  	r                   *runner
    56  	analysisDoneCh      chan struct{}
    57  	loadCachedFactsDone bool
    58  	loadCachedFactsOk   bool
    59  	isroot              bool
    60  	isInitialPkg        bool
    61  	needAnalyzeSource   bool
    62  }
    63  
    64  func (act *action) String() string {
    65  	return fmt.Sprintf("%s@%s", act.a, act.pkg)
    66  }
    67  
    68  func (act *action) loadCachedFacts() bool {
    69  	if act.loadCachedFactsDone { // can't be set in parallel
    70  		return act.loadCachedFactsOk
    71  	}
    72  
    73  	res := func() bool {
    74  		if act.isInitialPkg {
    75  			return true // load cached facts only for non-initial packages
    76  		}
    77  
    78  		if len(act.a.FactTypes) == 0 {
    79  			return true // no need to load facts
    80  		}
    81  
    82  		return act.loadPersistedFacts()
    83  	}()
    84  	act.loadCachedFactsDone = true
    85  	act.loadCachedFactsOk = res
    86  	return res
    87  }
    88  
    89  func (act *action) waitUntilDependingAnalyzersWorked() {
    90  	for _, dep := range act.deps {
    91  		if dep.pkg == act.pkg {
    92  			<-dep.analysisDoneCh
    93  		}
    94  	}
    95  }
    96  
    97  func (act *action) analyzeSafe() {
    98  	defer func() {
    99  		if p := recover(); p != nil {
   100  			act.err = errorutil.NewPanicError(fmt.Sprintf("%s: package %q (isInitialPkg: %t, needAnalyzeSource: %t): %s",
   101  				act.a.Name, act.pkg.Name, act.isInitialPkg, act.needAnalyzeSource, p), debug.Stack())
   102  		}
   103  	}()
   104  	act.r.sw.TrackStage(act.a.Name, func() {
   105  		act.analyze()
   106  	})
   107  }
   108  
   109  func (act *action) analyze() {
   110  	defer close(act.analysisDoneCh) // unblock actions depending on this action
   111  
   112  	if !act.needAnalyzeSource {
   113  		return
   114  	}
   115  
   116  	defer func(now time.Time) {
   117  		analyzeDebugf("go/analysis: %s: %s: analyzed package %q in %s", act.r.prefix, act.a.Name, act.pkg.Name, time.Since(now))
   118  	}(time.Now())
   119  
   120  	// Report an error if any dependency failures.
   121  	var depErrors *multierror.Error
   122  	for _, dep := range act.deps {
   123  		if dep.err == nil {
   124  			continue
   125  		}
   126  
   127  		depErrors = multierror.Append(depErrors, errors.Cause(dep.err))
   128  	}
   129  	if depErrors != nil {
   130  		depErrors.ErrorFormat = func(e []error) string {
   131  			return fmt.Sprintf("failed prerequisites: %v", e)
   132  		}
   133  
   134  		act.err = depErrors
   135  		return
   136  	}
   137  
   138  	// Plumb the output values of the dependencies
   139  	// into the inputs of this action.  Also facts.
   140  	inputs := make(map[*analysis.Analyzer]interface{})
   141  	startedAt := time.Now()
   142  	for _, dep := range act.deps {
   143  		if dep.pkg == act.pkg {
   144  			// Same package, different analysis (horizontal edge):
   145  			// in-memory outputs of prerequisite analyzers
   146  			// become inputs to this analysis pass.
   147  			inputs[dep.a] = dep.result
   148  		} else if dep.a == act.a { // (always true)
   149  			// Same analysis, different package (vertical edge):
   150  			// serialized facts produced by prerequisite analysis
   151  			// become available to this analysis pass.
   152  			inheritFacts(act, dep)
   153  		}
   154  	}
   155  	factsDebugf("%s: Inherited facts in %s", act, time.Since(startedAt))
   156  
   157  	// Run the analysis.
   158  	pass := &analysis.Pass{
   159  		Analyzer:          act.a,
   160  		Fset:              act.pkg.Fset,
   161  		Files:             act.pkg.Syntax,
   162  		OtherFiles:        act.pkg.OtherFiles,
   163  		Pkg:               act.pkg.Types,
   164  		TypesInfo:         act.pkg.TypesInfo,
   165  		TypesSizes:        act.pkg.TypesSizes,
   166  		ResultOf:          inputs,
   167  		Report:            func(d analysis.Diagnostic) { act.diagnostics = append(act.diagnostics, d) },
   168  		ImportObjectFact:  act.importObjectFact,
   169  		ExportObjectFact:  act.exportObjectFact,
   170  		ImportPackageFact: act.importPackageFact,
   171  		ExportPackageFact: act.exportPackageFact,
   172  		AllObjectFacts:    act.allObjectFacts,
   173  		AllPackageFacts:   act.allPackageFacts,
   174  	}
   175  	act.pass = pass
   176  	act.r.passToPkgGuard.Lock()
   177  	act.r.passToPkg[pass] = act.pkg
   178  	act.r.passToPkgGuard.Unlock()
   179  
   180  	if act.pkg.IllTyped {
   181  		// It looks like there should be !pass.Analyzer.RunDespiteErrors
   182  		// but govet's cgocall crashes on it. Govet itself contains !pass.Analyzer.RunDespiteErrors condition here
   183  		// but it exit before it if packages.Load have failed.
   184  		act.err = errors.Wrap(&IllTypedError{Pkg: act.pkg}, "analysis skipped")
   185  	} else {
   186  		startedAt = time.Now()
   187  		act.result, act.err = pass.Analyzer.Run(pass)
   188  		analyzedIn := time.Since(startedAt)
   189  		if analyzedIn > time.Millisecond*10 {
   190  			debugf("%s: run analyzer in %s", act, analyzedIn)
   191  		}
   192  	}
   193  
   194  	// disallow calls after Run
   195  	pass.ExportObjectFact = nil
   196  	pass.ExportPackageFact = nil
   197  
   198  	if err := act.persistFactsToCache(); err != nil {
   199  		act.r.log.Warnf("Failed to persist facts to cache: %s", err)
   200  	}
   201  }
   202  
   203  // importObjectFact implements Pass.ImportObjectFact.
   204  // Given a non-nil pointer ptr of type *T, where *T satisfies Fact,
   205  // importObjectFact copies the fact value to *ptr.
   206  func (act *action) importObjectFact(obj types.Object, ptr analysis.Fact) bool {
   207  	if obj == nil {
   208  		panic("nil object")
   209  	}
   210  	key := objectFactKey{obj, act.factType(ptr)}
   211  	if v, ok := act.objectFacts[key]; ok {
   212  		reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
   213  		return true
   214  	}
   215  	return false
   216  }
   217  
   218  // exportObjectFact implements Pass.ExportObjectFact.
   219  func (act *action) exportObjectFact(obj types.Object, fact analysis.Fact) {
   220  	if obj.Pkg() != act.pkg.Types {
   221  		act.r.log.Panicf("internal error: in analysis %s of package %s: Fact.Set(%s, %T): can't set facts on objects belonging another package",
   222  			act.a, act.pkg, obj, fact)
   223  	}
   224  
   225  	key := objectFactKey{obj, act.factType(fact)}
   226  	act.objectFacts[key] = fact // clobber any existing entry
   227  	if isFactsExportDebug {
   228  		objstr := types.ObjectString(obj, (*types.Package).Name)
   229  		factsExportDebugf("%s: object %s has fact %s\n",
   230  			act.pkg.Fset.Position(obj.Pos()), objstr, fact)
   231  	}
   232  }
   233  
   234  func (act *action) allObjectFacts() []analysis.ObjectFact {
   235  	out := make([]analysis.ObjectFact, 0, len(act.objectFacts))
   236  	for key, fact := range act.objectFacts {
   237  		out = append(out, analysis.ObjectFact{
   238  			Object: key.obj,
   239  			Fact:   fact,
   240  		})
   241  	}
   242  	return out
   243  }
   244  
   245  // importPackageFact implements Pass.ImportPackageFact.
   246  // Given a non-nil pointer ptr of type *T, where *T satisfies Fact,
   247  // fact copies the fact value to *ptr.
   248  func (act *action) importPackageFact(pkg *types.Package, ptr analysis.Fact) bool {
   249  	if pkg == nil {
   250  		panic("nil package")
   251  	}
   252  	key := packageFactKey{pkg, act.factType(ptr)}
   253  	if v, ok := act.packageFacts[key]; ok {
   254  		reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
   255  		return true
   256  	}
   257  	return false
   258  }
   259  
   260  // exportPackageFact implements Pass.ExportPackageFact.
   261  func (act *action) exportPackageFact(fact analysis.Fact) {
   262  	key := packageFactKey{act.pass.Pkg, act.factType(fact)}
   263  	act.packageFacts[key] = fact // clobber any existing entry
   264  	factsDebugf("%s: package %s has fact %s\n",
   265  		act.pkg.Fset.Position(act.pass.Files[0].Pos()), act.pass.Pkg.Path(), fact)
   266  }
   267  
   268  func (act *action) allPackageFacts() []analysis.PackageFact {
   269  	out := make([]analysis.PackageFact, 0, len(act.packageFacts))
   270  	for key, fact := range act.packageFacts {
   271  		out = append(out, analysis.PackageFact{
   272  			Package: key.pkg,
   273  			Fact:    fact,
   274  		})
   275  	}
   276  	return out
   277  }
   278  
   279  func (act *action) factType(fact analysis.Fact) reflect.Type {
   280  	t := reflect.TypeOf(fact)
   281  	if t.Kind() != reflect.Ptr {
   282  		act.r.log.Fatalf("invalid Fact type: got %T, want pointer", t)
   283  	}
   284  	return t
   285  }
   286  
   287  func (act *action) persistFactsToCache() error {
   288  	analyzer := act.a
   289  	if len(analyzer.FactTypes) == 0 {
   290  		return nil
   291  	}
   292  
   293  	// Merge new facts into the package and persist them.
   294  	var facts []Fact
   295  	for key, fact := range act.packageFacts {
   296  		if key.pkg != act.pkg.Types {
   297  			// The fact is from inherited facts from another package
   298  			continue
   299  		}
   300  		facts = append(facts, Fact{
   301  			Path: "",
   302  			Fact: fact,
   303  		})
   304  	}
   305  	for key, fact := range act.objectFacts {
   306  		obj := key.obj
   307  		if obj.Pkg() != act.pkg.Types {
   308  			// The fact is from inherited facts from another package
   309  			continue
   310  		}
   311  
   312  		path, err := objectpath.For(obj)
   313  		if err != nil {
   314  			// The object is not globally addressable
   315  			continue
   316  		}
   317  
   318  		facts = append(facts, Fact{
   319  			Path: string(path),
   320  			Fact: fact,
   321  		})
   322  	}
   323  
   324  	factsCacheDebugf("Caching %d facts for package %q and analyzer %s", len(facts), act.pkg.Name, act.a.Name)
   325  
   326  	key := fmt.Sprintf("%s/facts", analyzer.Name)
   327  	return act.r.pkgCache.Put(act.pkg, pkgcache.HashModeNeedAllDeps, key, facts)
   328  }
   329  
   330  func (act *action) loadPersistedFacts() bool {
   331  	var facts []Fact
   332  	key := fmt.Sprintf("%s/facts", act.a.Name)
   333  	if err := act.r.pkgCache.Get(act.pkg, pkgcache.HashModeNeedAllDeps, key, &facts); err != nil {
   334  		if err != pkgcache.ErrMissing {
   335  			act.r.log.Warnf("Failed to get persisted facts: %s", err)
   336  		}
   337  
   338  		factsCacheDebugf("No cached facts for package %q and analyzer %s", act.pkg.Name, act.a.Name)
   339  		return false
   340  	}
   341  
   342  	factsCacheDebugf("Loaded %d cached facts for package %q and analyzer %s", len(facts), act.pkg.Name, act.a.Name)
   343  
   344  	for _, f := range facts {
   345  		if f.Path == "" { // this is a package fact
   346  			key := packageFactKey{act.pkg.Types, act.factType(f.Fact)}
   347  			act.packageFacts[key] = f.Fact
   348  			continue
   349  		}
   350  		obj, err := objectpath.Object(act.pkg.Types, objectpath.Path(f.Path))
   351  		if err != nil {
   352  			// Be lenient about these errors. For example, when
   353  			// analyzing io/ioutil from source, we may get a fact
   354  			// for methods on the devNull type, and objectpath
   355  			// will happily create a path for them. However, when
   356  			// we later load io/ioutil from export data, the path
   357  			// no longer resolves.
   358  			//
   359  			// If an exported type embeds the unexported type,
   360  			// then (part of) the unexported type will become part
   361  			// of the type information and our path will resolve
   362  			// again.
   363  			continue
   364  		}
   365  		factKey := objectFactKey{obj, act.factType(f.Fact)}
   366  		act.objectFacts[factKey] = f.Fact
   367  	}
   368  
   369  	return true
   370  }
   371  
   372  func (act *action) markDepsForAnalyzingSource() {
   373  	// Horizontal deps (analyzer.Requires) must be loaded from source and analyzed before analyzing
   374  	// this action.
   375  	for _, dep := range act.deps {
   376  		if dep.pkg == act.pkg {
   377  			// Analyze source only for horizontal dependencies, e.g. from "buildssa".
   378  			dep.needAnalyzeSource = true // can't be set in parallel
   379  		}
   380  	}
   381  }