github.com/chenfeining/golangci-lint@v1.0.2-0.20230730162517-14c6c67868df/pkg/golinters/goanalysis/runner_action.go (about)

     1  package goanalysis
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"go/types"
     7  	"io"
     8  	"reflect"
     9  	"runtime/debug"
    10  	"time"
    11  
    12  	"github.com/hashicorp/go-multierror"
    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/chenfeining/golangci-lint/internal/errorutil"
    18  	"github.com/chenfeining/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              any
    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  			if !act.isroot {
   102  				// This line allows to display "hidden" panic with analyzers like buildssa.
   103  				// Some linters are dependent of sub-analyzers but when a sub-analyzer fails the linter is not aware of that,
   104  				// this results to another panic (ex: "interface conversion: interface {} is nil, not *buildssa.SSA").
   105  				act.r.log.Errorf("%s: panic during analysis: %v, %s", act.a.Name, p, string(debug.Stack()))
   106  			}
   107  
   108  			act.err = errorutil.NewPanicError(fmt.Sprintf("%s: package %q (isInitialPkg: %t, needAnalyzeSource: %t): %s",
   109  				act.a.Name, act.pkg.Name, act.isInitialPkg, act.needAnalyzeSource, p), debug.Stack())
   110  		}
   111  	}()
   112  	act.r.sw.TrackStage(act.a.Name, func() {
   113  		act.analyze()
   114  	})
   115  }
   116  
   117  func (act *action) analyze() {
   118  	defer close(act.analysisDoneCh) // unblock actions depending on this action
   119  
   120  	if !act.needAnalyzeSource {
   121  		return
   122  	}
   123  
   124  	defer func(now time.Time) {
   125  		analyzeDebugf("go/analysis: %s: %s: analyzed package %q in %s", act.r.prefix, act.a.Name, act.pkg.Name, time.Since(now))
   126  	}(time.Now())
   127  
   128  	// Report an error if any dependency failures.
   129  	var depErrors *multierror.Error
   130  	for _, dep := range act.deps {
   131  		if dep.err == nil {
   132  			continue
   133  		}
   134  
   135  		depErrors = multierror.Append(depErrors, errors.Unwrap(dep.err))
   136  	}
   137  	if depErrors != nil {
   138  		depErrors.ErrorFormat = func(e []error) string {
   139  			return fmt.Sprintf("failed prerequisites: %v", e)
   140  		}
   141  
   142  		act.err = depErrors
   143  		return
   144  	}
   145  
   146  	// Plumb the output values of the dependencies
   147  	// into the inputs of this action.  Also facts.
   148  	inputs := make(map[*analysis.Analyzer]any)
   149  	startedAt := time.Now()
   150  	for _, dep := range act.deps {
   151  		if dep.pkg == act.pkg {
   152  			// Same package, different analysis (horizontal edge):
   153  			// in-memory outputs of prerequisite analyzers
   154  			// become inputs to this analysis pass.
   155  			inputs[dep.a] = dep.result
   156  		} else if dep.a == act.a { // (always true)
   157  			// Same analysis, different package (vertical edge):
   158  			// serialized facts produced by prerequisite analysis
   159  			// become available to this analysis pass.
   160  			inheritFacts(act, dep)
   161  		}
   162  	}
   163  	factsDebugf("%s: Inherited facts in %s", act, time.Since(startedAt))
   164  
   165  	// Run the analysis.
   166  	pass := &analysis.Pass{
   167  		Analyzer:          act.a,
   168  		Fset:              act.pkg.Fset,
   169  		Files:             act.pkg.Syntax,
   170  		OtherFiles:        act.pkg.OtherFiles,
   171  		Pkg:               act.pkg.Types,
   172  		TypesInfo:         act.pkg.TypesInfo,
   173  		TypesSizes:        act.pkg.TypesSizes,
   174  		ResultOf:          inputs,
   175  		Report:            func(d analysis.Diagnostic) { act.diagnostics = append(act.diagnostics, d) },
   176  		ImportObjectFact:  act.importObjectFact,
   177  		ExportObjectFact:  act.exportObjectFact,
   178  		ImportPackageFact: act.importPackageFact,
   179  		ExportPackageFact: act.exportPackageFact,
   180  		AllObjectFacts:    act.allObjectFacts,
   181  		AllPackageFacts:   act.allPackageFacts,
   182  	}
   183  	act.pass = pass
   184  	act.r.passToPkgGuard.Lock()
   185  	act.r.passToPkg[pass] = act.pkg
   186  	act.r.passToPkgGuard.Unlock()
   187  
   188  	if act.pkg.IllTyped {
   189  		// It looks like there should be !pass.Analyzer.RunDespiteErrors
   190  		// but govet's cgocall crashes on it. Govet itself contains !pass.Analyzer.RunDespiteErrors condition here,
   191  		// but it exits before it if packages.Load have failed.
   192  		act.err = fmt.Errorf("analysis skipped: %w", &IllTypedError{Pkg: act.pkg})
   193  	} else {
   194  		startedAt = time.Now()
   195  		act.result, act.err = pass.Analyzer.Run(pass)
   196  		analyzedIn := time.Since(startedAt)
   197  		if analyzedIn > time.Millisecond*10 {
   198  			debugf("%s: run analyzer in %s", act, analyzedIn)
   199  		}
   200  	}
   201  
   202  	// disallow calls after Run
   203  	pass.ExportObjectFact = nil
   204  	pass.ExportPackageFact = nil
   205  
   206  	if err := act.persistFactsToCache(); err != nil {
   207  		act.r.log.Warnf("Failed to persist facts to cache: %s", err)
   208  	}
   209  }
   210  
   211  // importObjectFact implements Pass.ImportObjectFact.
   212  // Given a non-nil pointer ptr of type *T, where *T satisfies Fact,
   213  // importObjectFact copies the fact value to *ptr.
   214  func (act *action) importObjectFact(obj types.Object, ptr analysis.Fact) bool {
   215  	if obj == nil {
   216  		panic("nil object")
   217  	}
   218  	key := objectFactKey{obj, act.factType(ptr)}
   219  	if v, ok := act.objectFacts[key]; ok {
   220  		reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
   221  		return true
   222  	}
   223  	return false
   224  }
   225  
   226  // exportObjectFact implements Pass.ExportObjectFact.
   227  func (act *action) exportObjectFact(obj types.Object, fact analysis.Fact) {
   228  	if obj.Pkg() != act.pkg.Types {
   229  		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",
   230  			act.a, act.pkg, obj, fact)
   231  	}
   232  
   233  	key := objectFactKey{obj, act.factType(fact)}
   234  	act.objectFacts[key] = fact // clobber any existing entry
   235  	if isFactsExportDebug {
   236  		objstr := types.ObjectString(obj, (*types.Package).Name)
   237  		factsExportDebugf("%s: object %s has fact %s\n",
   238  			act.pkg.Fset.Position(obj.Pos()), objstr, fact)
   239  	}
   240  }
   241  
   242  func (act *action) allObjectFacts() []analysis.ObjectFact {
   243  	out := make([]analysis.ObjectFact, 0, len(act.objectFacts))
   244  	for key, fact := range act.objectFacts {
   245  		out = append(out, analysis.ObjectFact{
   246  			Object: key.obj,
   247  			Fact:   fact,
   248  		})
   249  	}
   250  	return out
   251  }
   252  
   253  // importPackageFact implements Pass.ImportPackageFact.
   254  // Given a non-nil pointer ptr of type *T, where *T satisfies Fact,
   255  // fact copies the fact value to *ptr.
   256  func (act *action) importPackageFact(pkg *types.Package, ptr analysis.Fact) bool {
   257  	if pkg == nil {
   258  		panic("nil package")
   259  	}
   260  	key := packageFactKey{pkg, act.factType(ptr)}
   261  	if v, ok := act.packageFacts[key]; ok {
   262  		reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
   263  		return true
   264  	}
   265  	return false
   266  }
   267  
   268  // exportPackageFact implements Pass.ExportPackageFact.
   269  func (act *action) exportPackageFact(fact analysis.Fact) {
   270  	key := packageFactKey{act.pass.Pkg, act.factType(fact)}
   271  	act.packageFacts[key] = fact // clobber any existing entry
   272  	factsDebugf("%s: package %s has fact %s\n",
   273  		act.pkg.Fset.Position(act.pass.Files[0].Pos()), act.pass.Pkg.Path(), fact)
   274  }
   275  
   276  func (act *action) allPackageFacts() []analysis.PackageFact {
   277  	out := make([]analysis.PackageFact, 0, len(act.packageFacts))
   278  	for key, fact := range act.packageFacts {
   279  		out = append(out, analysis.PackageFact{
   280  			Package: key.pkg,
   281  			Fact:    fact,
   282  		})
   283  	}
   284  	return out
   285  }
   286  
   287  func (act *action) factType(fact analysis.Fact) reflect.Type {
   288  	t := reflect.TypeOf(fact)
   289  	if t.Kind() != reflect.Ptr {
   290  		act.r.log.Fatalf("invalid Fact type: got %T, want pointer", t)
   291  	}
   292  	return t
   293  }
   294  
   295  func (act *action) persistFactsToCache() error {
   296  	analyzer := act.a
   297  	if len(analyzer.FactTypes) == 0 {
   298  		return nil
   299  	}
   300  
   301  	// Merge new facts into the package and persist them.
   302  	var facts []Fact
   303  	for key, fact := range act.packageFacts {
   304  		if key.pkg != act.pkg.Types {
   305  			// The fact is from inherited facts from another package
   306  			continue
   307  		}
   308  		facts = append(facts, Fact{
   309  			Path: "",
   310  			Fact: fact,
   311  		})
   312  	}
   313  	for key, fact := range act.objectFacts {
   314  		obj := key.obj
   315  		if obj.Pkg() != act.pkg.Types {
   316  			// The fact is from inherited facts from another package
   317  			continue
   318  		}
   319  
   320  		path, err := objectpath.For(obj)
   321  		if err != nil {
   322  			// The object is not globally addressable
   323  			continue
   324  		}
   325  
   326  		facts = append(facts, Fact{
   327  			Path: string(path),
   328  			Fact: fact,
   329  		})
   330  	}
   331  
   332  	factsCacheDebugf("Caching %d facts for package %q and analyzer %s", len(facts), act.pkg.Name, act.a.Name)
   333  
   334  	key := fmt.Sprintf("%s/facts", analyzer.Name)
   335  	return act.r.pkgCache.Put(act.pkg, pkgcache.HashModeNeedAllDeps, key, facts)
   336  }
   337  
   338  func (act *action) loadPersistedFacts() bool {
   339  	var facts []Fact
   340  	key := fmt.Sprintf("%s/facts", act.a.Name)
   341  	if err := act.r.pkgCache.Get(act.pkg, pkgcache.HashModeNeedAllDeps, key, &facts); err != nil {
   342  		if !errors.Is(err, pkgcache.ErrMissing) && !errors.Is(err, io.EOF) {
   343  			act.r.log.Warnf("Failed to get persisted facts: %s", err)
   344  		}
   345  
   346  		factsCacheDebugf("No cached facts for package %q and analyzer %s", act.pkg.Name, act.a.Name)
   347  		return false
   348  	}
   349  
   350  	factsCacheDebugf("Loaded %d cached facts for package %q and analyzer %s", len(facts), act.pkg.Name, act.a.Name)
   351  
   352  	for _, f := range facts {
   353  		if f.Path == "" { // this is a package fact
   354  			key := packageFactKey{act.pkg.Types, act.factType(f.Fact)}
   355  			act.packageFacts[key] = f.Fact
   356  			continue
   357  		}
   358  		obj, err := objectpath.Object(act.pkg.Types, objectpath.Path(f.Path))
   359  		if err != nil {
   360  			// Be lenient about these errors. For example, when
   361  			// analyzing io/ioutil from source, we may get a fact
   362  			// for methods on the devNull type, and objectpath
   363  			// will happily create a path for them. However, when
   364  			// we later load io/ioutil from export data, the path
   365  			// no longer resolves.
   366  			//
   367  			// If an exported type embeds the unexported type,
   368  			// then (part of) the unexported type will become part
   369  			// of the type information and our path will resolve
   370  			// again.
   371  			continue
   372  		}
   373  		factKey := objectFactKey{obj, act.factType(f.Fact)}
   374  		act.objectFacts[factKey] = f.Fact
   375  	}
   376  
   377  	return true
   378  }
   379  
   380  func (act *action) markDepsForAnalyzingSource() {
   381  	// Horizontal deps (analyzer.Requires) must be loaded from source and analyzed before analyzing
   382  	// this action.
   383  	for _, dep := range act.deps {
   384  		if dep.pkg == act.pkg {
   385  			// Analyze source only for horizontal dependencies, e.g. from "buildssa".
   386  			dep.needAnalyzeSource = true // can't be set in parallel
   387  		}
   388  	}
   389  }