github.com/nozzle/golangci-lint@v1.49.0-nz3/pkg/golinters/goanalysis/runner_loadingpackage.go (about) 1 package goanalysis 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/parser" 7 "go/scanner" 8 "go/types" 9 "os" 10 "reflect" 11 "sync" 12 "sync/atomic" 13 14 "github.com/pkg/errors" 15 "golang.org/x/tools/go/gcexportdata" 16 "golang.org/x/tools/go/packages" 17 18 "github.com/golangci/golangci-lint/pkg/golinters/goanalysis/load" 19 "github.com/golangci/golangci-lint/pkg/logutils" 20 ) 21 22 const unsafePkgName = "unsafe" 23 24 type loadingPackage struct { 25 pkg *packages.Package 26 imports map[string]*loadingPackage 27 isInitial bool 28 log logutils.Log 29 actions []*action // all actions with this package 30 loadGuard *load.Guard 31 dependents int32 // number of depending on it packages 32 analyzeOnce sync.Once 33 decUseMutex sync.Mutex 34 } 35 36 func (lp *loadingPackage) analyzeRecursive(loadMode LoadMode, loadSem chan struct{}) { 37 lp.analyzeOnce.Do(func() { 38 // Load the direct dependencies, in parallel. 39 var wg sync.WaitGroup 40 wg.Add(len(lp.imports)) 41 for _, imp := range lp.imports { 42 go func(imp *loadingPackage) { 43 imp.analyzeRecursive(loadMode, loadSem) 44 wg.Done() 45 }(imp) 46 } 47 wg.Wait() 48 lp.analyze(loadMode, loadSem) 49 }) 50 } 51 52 func (lp *loadingPackage) analyze(loadMode LoadMode, loadSem chan struct{}) { 53 loadSem <- struct{}{} 54 defer func() { 55 <-loadSem 56 }() 57 58 // Save memory on unused more fields. 59 defer lp.decUse(loadMode < LoadModeWholeProgram) 60 61 if err := lp.loadWithFacts(loadMode); err != nil { 62 werr := errors.Wrapf(err, "failed to load package %s", lp.pkg.Name) 63 // Don't need to write error to errCh, it will be extracted and reported on another layer. 64 // Unblock depending on actions and propagate error. 65 for _, act := range lp.actions { 66 close(act.analysisDoneCh) 67 act.err = werr 68 } 69 return 70 } 71 72 var actsWg sync.WaitGroup 73 actsWg.Add(len(lp.actions)) 74 for _, act := range lp.actions { 75 go func(act *action) { 76 defer actsWg.Done() 77 78 act.waitUntilDependingAnalyzersWorked() 79 80 act.analyzeSafe() 81 }(act) 82 } 83 actsWg.Wait() 84 } 85 86 func (lp *loadingPackage) loadFromSource(loadMode LoadMode) error { 87 pkg := lp.pkg 88 89 // Many packages have few files, much fewer than there 90 // are CPU cores. Additionally, parsing each individual file is 91 // very fast. A naive parallel implementation of this loop won't 92 // be faster, and tends to be slower due to extra scheduling, 93 // bookkeeping and potentially false sharing of cache lines. 94 pkg.Syntax = make([]*ast.File, 0, len(pkg.CompiledGoFiles)) 95 for _, file := range pkg.CompiledGoFiles { 96 f, err := parser.ParseFile(pkg.Fset, file, nil, parser.ParseComments) 97 if err != nil { 98 pkg.Errors = append(pkg.Errors, lp.convertError(err)...) 99 continue 100 } 101 pkg.Syntax = append(pkg.Syntax, f) 102 } 103 if len(pkg.Errors) != 0 { 104 pkg.IllTyped = true 105 return nil 106 } 107 108 if loadMode == LoadModeSyntax { 109 return nil 110 } 111 112 // Call NewPackage directly with explicit name. 113 // This avoids skew between golist and go/types when the files' 114 // package declarations are inconsistent. 115 // Subtle: we populate all Types fields with an empty Package 116 // before loading export data so that export data processing 117 // never has to create a types.Package for an indirect dependency, 118 // which would then require that such created packages be explicitly 119 // inserted back into the Import graph as a final step after export data loading. 120 pkg.Types = types.NewPackage(pkg.PkgPath, pkg.Name) 121 122 pkg.IllTyped = true 123 124 pkg.TypesInfo = &types.Info{ 125 Types: make(map[ast.Expr]types.TypeAndValue), 126 Instances: make(map[*ast.Ident]types.Instance), 127 Defs: make(map[*ast.Ident]types.Object), 128 Uses: make(map[*ast.Ident]types.Object), 129 Implicits: make(map[ast.Node]types.Object), 130 Scopes: make(map[ast.Node]*types.Scope), 131 Selections: make(map[*ast.SelectorExpr]*types.Selection), 132 } 133 134 importer := func(path string) (*types.Package, error) { 135 if path == unsafePkgName { 136 return types.Unsafe, nil 137 } 138 if path == "C" { 139 // go/packages doesn't tell us that cgo preprocessing 140 // failed. When we subsequently try to parse the package, 141 // we'll encounter the raw C import. 142 return nil, errors.New("cgo preprocessing failed") 143 } 144 imp := pkg.Imports[path] 145 if imp == nil { 146 return nil, nil 147 } 148 if len(imp.Errors) > 0 { 149 return nil, imp.Errors[0] 150 } 151 return imp.Types, nil 152 } 153 tc := &types.Config{ 154 Importer: importerFunc(importer), 155 Error: func(err error) { 156 pkg.Errors = append(pkg.Errors, lp.convertError(err)...) 157 }, 158 } 159 _ = types.NewChecker(tc, pkg.Fset, pkg.Types, pkg.TypesInfo).Files(pkg.Syntax) 160 // Don't handle error here: errors are adding by tc.Error function. 161 162 illTyped := len(pkg.Errors) != 0 163 if !illTyped { 164 for _, imp := range lp.imports { 165 if imp.pkg.IllTyped { 166 illTyped = true 167 break 168 } 169 } 170 } 171 pkg.IllTyped = illTyped 172 return nil 173 } 174 175 func (lp *loadingPackage) loadFromExportData() error { 176 pkg := lp.pkg 177 178 // Call NewPackage directly with explicit name. 179 // This avoids skew between golist and go/types when the files' 180 // package declarations are inconsistent. 181 // Subtle: we populate all Types fields with an empty Package 182 // before loading export data so that export data processing 183 // never has to create a types.Package for an indirect dependency, 184 // which would then require that such created packages be explicitly 185 // inserted back into the Import graph as a final step after export data loading. 186 pkg.Types = types.NewPackage(pkg.PkgPath, pkg.Name) 187 188 pkg.IllTyped = true 189 for path, pkg := range pkg.Imports { 190 if pkg.Types == nil { 191 return fmt.Errorf("dependency %q hasn't been loaded yet", path) 192 } 193 } 194 if pkg.ExportFile == "" { 195 return fmt.Errorf("no export data for %q", pkg.ID) 196 } 197 f, err := os.Open(pkg.ExportFile) 198 if err != nil { 199 return err 200 } 201 defer f.Close() 202 203 r, err := gcexportdata.NewReader(f) 204 if err != nil { 205 return err 206 } 207 208 view := make(map[string]*types.Package) // view seen by gcexportdata 209 seen := make(map[*packages.Package]bool) // all visited packages 210 var visit func(pkgs map[string]*packages.Package) 211 visit = func(pkgs map[string]*packages.Package) { 212 for _, pkg := range pkgs { 213 if !seen[pkg] { 214 seen[pkg] = true 215 view[pkg.PkgPath] = pkg.Types 216 visit(pkg.Imports) 217 } 218 } 219 } 220 visit(pkg.Imports) 221 tpkg, err := gcexportdata.Read(r, pkg.Fset, view, pkg.PkgPath) 222 if err != nil { 223 return err 224 } 225 pkg.Types = tpkg 226 pkg.IllTyped = false 227 return nil 228 } 229 230 func (lp *loadingPackage) loadWithFacts(loadMode LoadMode) error { 231 pkg := lp.pkg 232 233 if pkg.PkgPath == unsafePkgName { 234 // Fill in the blanks to avoid surprises. 235 pkg.Syntax = []*ast.File{} 236 if loadMode >= LoadModeTypesInfo { 237 pkg.Types = types.Unsafe 238 pkg.TypesInfo = new(types.Info) 239 } 240 return nil 241 } 242 243 if pkg.TypesInfo != nil { 244 // Already loaded package, e.g. because another not go/analysis linter required types for deps. 245 // Try load cached facts for it. 246 247 for _, act := range lp.actions { 248 if !act.loadCachedFacts() { 249 // Cached facts loading failed: analyze later the action from source. 250 act.needAnalyzeSource = true 251 factsCacheDebugf("Loading of facts for already loaded %s failed, analyze it from source later", act) 252 act.markDepsForAnalyzingSource() 253 } 254 } 255 return nil 256 } 257 258 if lp.isInitial { 259 // No need to load cached facts: the package will be analyzed from source 260 // because it's the initial. 261 return lp.loadFromSource(loadMode) 262 } 263 264 return lp.loadImportedPackageWithFacts(loadMode) 265 } 266 267 func (lp *loadingPackage) loadImportedPackageWithFacts(loadMode LoadMode) error { 268 pkg := lp.pkg 269 270 // Load package from export data 271 if loadMode >= LoadModeTypesInfo { 272 if err := lp.loadFromExportData(); err != nil { 273 // We asked Go to give us up-to-date export data, yet 274 // we can't load it. There must be something wrong. 275 // 276 // Attempt loading from source. This should fail (because 277 // otherwise there would be export data); we just want to 278 // get the compile errors. If loading from source succeeds 279 // we discard the result, anyway. Otherwise, we'll fail 280 // when trying to reload from export data later. 281 282 // Otherwise, it panics because uses already existing (from exported data) types. 283 pkg.Types = types.NewPackage(pkg.PkgPath, pkg.Name) 284 if srcErr := lp.loadFromSource(loadMode); srcErr != nil { 285 return srcErr 286 } 287 // Make sure this package can't be imported successfully 288 pkg.Errors = append(pkg.Errors, packages.Error{ 289 Pos: "-", 290 Msg: fmt.Sprintf("could not load export data: %s", err), 291 Kind: packages.ParseError, 292 }) 293 return errors.Wrap(err, "could not load export data") 294 } 295 } 296 297 needLoadFromSource := false 298 for _, act := range lp.actions { 299 if act.loadCachedFacts() { 300 continue 301 } 302 303 // Cached facts loading failed: analyze later the action from source. 304 factsCacheDebugf("Loading of facts for %s failed, analyze it from source later", act) 305 act.needAnalyzeSource = true // can't be set in parallel 306 needLoadFromSource = true 307 308 act.markDepsForAnalyzingSource() 309 } 310 311 if needLoadFromSource { 312 // Cached facts loading failed: analyze later the action from source. To perform 313 // the analysis we need to load the package from source code. 314 315 // Otherwise, it panics because uses already existing (from exported data) types. 316 if loadMode >= LoadModeTypesInfo { 317 pkg.Types = types.NewPackage(pkg.PkgPath, pkg.Name) 318 } 319 return lp.loadFromSource(loadMode) 320 } 321 322 return nil 323 } 324 325 func (lp *loadingPackage) decUse(canClearTypes bool) { 326 lp.decUseMutex.Lock() 327 defer lp.decUseMutex.Unlock() 328 329 for _, act := range lp.actions { 330 pass := act.pass 331 if pass == nil { 332 continue 333 } 334 335 pass.Files = nil 336 pass.TypesInfo = nil 337 pass.TypesSizes = nil 338 pass.ResultOf = nil 339 pass.Pkg = nil 340 pass.OtherFiles = nil 341 pass.AllObjectFacts = nil 342 pass.AllPackageFacts = nil 343 pass.ImportObjectFact = nil 344 pass.ExportObjectFact = nil 345 pass.ImportPackageFact = nil 346 pass.ExportPackageFact = nil 347 act.pass = nil 348 act.deps = nil 349 if act.result != nil { 350 if isMemoryDebug { 351 debugf("%s: decUse: nilling act result of size %d bytes", act, sizeOfValueTreeBytes(act.result)) 352 } 353 act.result = nil 354 } 355 } 356 357 lp.pkg.Syntax = nil 358 lp.pkg.TypesInfo = nil 359 lp.pkg.TypesSizes = nil 360 361 // Can't set lp.pkg.Imports to nil because of loadFromExportData.visit. 362 363 dependents := atomic.AddInt32(&lp.dependents, -1) 364 if dependents != 0 { 365 return 366 } 367 368 if canClearTypes { 369 // canClearTypes is set to true if we can discard type 370 // information after the package and its dependents have been 371 // processed. This is the case when no whole program checkers (unused) are 372 // being run. 373 lp.pkg.Types = nil 374 } 375 lp.pkg = nil 376 377 for _, imp := range lp.imports { 378 imp.decUse(canClearTypes) 379 } 380 lp.imports = nil 381 382 for _, act := range lp.actions { 383 if !lp.isInitial { 384 act.pkg = nil 385 } 386 act.packageFacts = nil 387 act.objectFacts = nil 388 } 389 lp.actions = nil 390 } 391 392 func (lp *loadingPackage) convertError(err error) []packages.Error { 393 var errs []packages.Error 394 // taken from go/packages 395 switch err := err.(type) { 396 case packages.Error: 397 // from driver 398 errs = append(errs, err) 399 400 case *os.PathError: 401 // from parser 402 errs = append(errs, packages.Error{ 403 Pos: err.Path + ":1", 404 Msg: err.Err.Error(), 405 Kind: packages.ParseError, 406 }) 407 408 case scanner.ErrorList: 409 // from parser 410 for _, err := range err { 411 errs = append(errs, packages.Error{ 412 Pos: err.Pos.String(), 413 Msg: err.Msg, 414 Kind: packages.ParseError, 415 }) 416 } 417 418 case types.Error: 419 // from type checker 420 errs = append(errs, packages.Error{ 421 Pos: err.Fset.Position(err.Pos).String(), 422 Msg: err.Msg, 423 Kind: packages.TypeError, 424 }) 425 426 default: 427 // unexpected impoverished error from parser? 428 errs = append(errs, packages.Error{ 429 Pos: "-", 430 Msg: err.Error(), 431 Kind: packages.UnknownError, 432 }) 433 434 // If you see this error message, please file a bug. 435 lp.log.Warnf("Internal error: error %q (%T) without position", err, err) 436 } 437 return errs 438 } 439 440 func (lp *loadingPackage) String() string { 441 return fmt.Sprintf("%s@%s", lp.pkg.PkgPath, lp.pkg.Name) 442 } 443 444 type importerFunc func(path string) (*types.Package, error) 445 446 func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) } 447 448 func sizeOfValueTreeBytes(v interface{}) int { 449 return sizeOfReflectValueTreeBytes(reflect.ValueOf(v), map[uintptr]struct{}{}) 450 } 451 452 func sizeOfReflectValueTreeBytes(rv reflect.Value, visitedPtrs map[uintptr]struct{}) int { 453 switch rv.Kind() { 454 case reflect.Ptr: 455 ptrSize := int(rv.Type().Size()) 456 if rv.IsNil() { 457 return ptrSize 458 } 459 ptr := rv.Pointer() 460 if _, ok := visitedPtrs[ptr]; ok { 461 return 0 462 } 463 visitedPtrs[ptr] = struct{}{} 464 return ptrSize + sizeOfReflectValueTreeBytes(rv.Elem(), visitedPtrs) 465 case reflect.Interface: 466 if rv.IsNil() { 467 return 0 468 } 469 return sizeOfReflectValueTreeBytes(rv.Elem(), visitedPtrs) 470 case reflect.Struct: 471 ret := 0 472 for i := 0; i < rv.NumField(); i++ { 473 ret += sizeOfReflectValueTreeBytes(rv.Field(i), visitedPtrs) 474 } 475 return ret 476 case reflect.Slice, reflect.Array, reflect.Chan: 477 return int(rv.Type().Size()) + rv.Cap()*int(rv.Type().Elem().Size()) 478 case reflect.Map: 479 ret := 0 480 for _, key := range rv.MapKeys() { 481 mv := rv.MapIndex(key) 482 ret += sizeOfReflectValueTreeBytes(key, visitedPtrs) 483 ret += sizeOfReflectValueTreeBytes(mv, visitedPtrs) 484 } 485 return ret 486 case reflect.String: 487 return rv.Len() 488 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 489 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 490 reflect.Uintptr, reflect.Bool, reflect.Float32, reflect.Float64, 491 reflect.Complex64, reflect.Complex128, reflect.Func, reflect.UnsafePointer: 492 return int(rv.Type().Size()) 493 case reflect.Invalid: 494 return 0 495 default: 496 panic("unknown rv of type " + rv.String()) 497 } 498 }