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 }