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 }