github.com/nozzle/golangci-lint@v1.49.0-nz3/pkg/golinters/goanalysis/runner_action.go (about)

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