github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/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/elek/golangci-lint/pkg/golinters/goanalysis/load"
    19  	"github.com/elek/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 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  		Defs:       make(map[*ast.Ident]types.Object),
   127  		Uses:       make(map[*ast.Ident]types.Object),
   128  		Implicits:  make(map[ast.Node]types.Object),
   129  		Scopes:     make(map[ast.Node]*types.Scope),
   130  		Selections: make(map[*ast.SelectorExpr]*types.Selection),
   131  	}
   132  
   133  	importer := func(path string) (*types.Package, error) {
   134  		if path == unsafePkgName {
   135  			return types.Unsafe, nil
   136  		}
   137  		if path == "C" {
   138  			// go/packages doesn't tell us that cgo preprocessing
   139  			// failed. When we subsequently try to parse the package,
   140  			// we'll encounter the raw C import.
   141  			return nil, errors.New("cgo preprocessing failed")
   142  		}
   143  		imp := pkg.Imports[path]
   144  		if imp == nil {
   145  			return nil, nil
   146  		}
   147  		if len(imp.Errors) > 0 {
   148  			return nil, imp.Errors[0]
   149  		}
   150  		return imp.Types, nil
   151  	}
   152  	tc := &types.Config{
   153  		Importer: importerFunc(importer),
   154  		Error: func(err error) {
   155  			pkg.Errors = append(pkg.Errors, lp.convertError(err)...)
   156  		},
   157  	}
   158  	_ = types.NewChecker(tc, pkg.Fset, pkg.Types, pkg.TypesInfo).Files(pkg.Syntax)
   159  	// Don't handle error here: errors are adding by tc.Error function.
   160  
   161  	illTyped := len(pkg.Errors) != 0
   162  	if !illTyped {
   163  		for _, imp := range lp.imports {
   164  			if imp.pkg.IllTyped {
   165  				illTyped = true
   166  				break
   167  			}
   168  		}
   169  	}
   170  	pkg.IllTyped = illTyped
   171  	return nil
   172  }
   173  
   174  func (lp *loadingPackage) loadFromExportData() error {
   175  	pkg := lp.pkg
   176  
   177  	// Call NewPackage directly with explicit name.
   178  	// This avoids skew between golist and go/types when the files'
   179  	// package declarations are inconsistent.
   180  	// Subtle: we populate all Types fields with an empty Package
   181  	// before loading export data so that export data processing
   182  	// never has to create a types.Package for an indirect dependency,
   183  	// which would then require that such created packages be explicitly
   184  	// inserted back into the Import graph as a final step after export data loading.
   185  	pkg.Types = types.NewPackage(pkg.PkgPath, pkg.Name)
   186  
   187  	pkg.IllTyped = true
   188  	for path, pkg := range pkg.Imports {
   189  		if pkg.Types == nil {
   190  			return fmt.Errorf("dependency %q hasn't been loaded yet", path)
   191  		}
   192  	}
   193  	if pkg.ExportFile == "" {
   194  		return fmt.Errorf("no export data for %q", pkg.ID)
   195  	}
   196  	f, err := os.Open(pkg.ExportFile)
   197  	if err != nil {
   198  		return err
   199  	}
   200  	defer f.Close()
   201  
   202  	r, err := gcexportdata.NewReader(f)
   203  	if err != nil {
   204  		return err
   205  	}
   206  
   207  	view := make(map[string]*types.Package)  // view seen by gcexportdata
   208  	seen := make(map[*packages.Package]bool) // all visited packages
   209  	var visit func(pkgs map[string]*packages.Package)
   210  	visit = func(pkgs map[string]*packages.Package) {
   211  		for _, pkg := range pkgs {
   212  			if !seen[pkg] {
   213  				seen[pkg] = true
   214  				view[pkg.PkgPath] = pkg.Types
   215  				visit(pkg.Imports)
   216  			}
   217  		}
   218  	}
   219  	visit(pkg.Imports)
   220  	tpkg, err := gcexportdata.Read(r, pkg.Fset, view, pkg.PkgPath)
   221  	if err != nil {
   222  		return err
   223  	}
   224  	pkg.Types = tpkg
   225  	pkg.IllTyped = false
   226  	return nil
   227  }
   228  
   229  func (lp *loadingPackage) loadWithFacts(loadMode LoadMode) error {
   230  	pkg := lp.pkg
   231  
   232  	if pkg.PkgPath == unsafePkgName {
   233  		// Fill in the blanks to avoid surprises.
   234  		pkg.Syntax = []*ast.File{}
   235  		if loadMode >= LoadModeTypesInfo {
   236  			pkg.Types = types.Unsafe
   237  			pkg.TypesInfo = new(types.Info)
   238  		}
   239  		return nil
   240  	}
   241  
   242  	if pkg.TypesInfo != nil {
   243  		// Already loaded package, e.g. because another not go/analysis linter required types for deps.
   244  		// Try load cached facts for it.
   245  
   246  		for _, act := range lp.actions {
   247  			if !act.loadCachedFacts() {
   248  				// Cached facts loading failed: analyze later the action from source.
   249  				act.needAnalyzeSource = true
   250  				factsCacheDebugf("Loading of facts for already loaded %s failed, analyze it from source later", act)
   251  				act.markDepsForAnalyzingSource()
   252  			}
   253  		}
   254  		return nil
   255  	}
   256  
   257  	if lp.isInitial {
   258  		// No need to load cached facts: the package will be analyzed from source
   259  		// because it's the initial.
   260  		return lp.loadFromSource(loadMode)
   261  	}
   262  
   263  	return lp.loadImportedPackageWithFacts(loadMode)
   264  }
   265  
   266  func (lp *loadingPackage) loadImportedPackageWithFacts(loadMode LoadMode) error {
   267  	pkg := lp.pkg
   268  
   269  	// Load package from export data
   270  	if loadMode >= LoadModeTypesInfo {
   271  		if err := lp.loadFromExportData(); err != nil {
   272  			// We asked Go to give us up to date export data, yet
   273  			// we can't load it. There must be something wrong.
   274  			//
   275  			// Attempt loading from source. This should fail (because
   276  			// otherwise there would be export data); we just want to
   277  			// get the compile errors. If loading from source succeeds
   278  			// we discard the result, anyway. Otherwise we'll fail
   279  			// when trying to reload from export data later.
   280  
   281  			// Otherwise it panics because uses already existing (from exported data) types.
   282  			pkg.Types = types.NewPackage(pkg.PkgPath, pkg.Name)
   283  			if srcErr := lp.loadFromSource(loadMode); srcErr != nil {
   284  				return srcErr
   285  			}
   286  			// Make sure this package can't be imported successfully
   287  			pkg.Errors = append(pkg.Errors, packages.Error{
   288  				Pos:  "-",
   289  				Msg:  fmt.Sprintf("could not load export data: %s", err),
   290  				Kind: packages.ParseError,
   291  			})
   292  			return errors.Wrap(err, "could not load export data")
   293  		}
   294  	}
   295  
   296  	needLoadFromSource := false
   297  	for _, act := range lp.actions {
   298  		if act.loadCachedFacts() {
   299  			continue
   300  		}
   301  
   302  		// Cached facts loading failed: analyze later the action from source.
   303  		factsCacheDebugf("Loading of facts for %s failed, analyze it from source later", act)
   304  		act.needAnalyzeSource = true // can't be set in parallel
   305  		needLoadFromSource = true
   306  
   307  		act.markDepsForAnalyzingSource()
   308  	}
   309  
   310  	if needLoadFromSource {
   311  		// Cached facts loading failed: analyze later the action from source. To perform
   312  		// the analysis we need to load the package from source code.
   313  
   314  		// Otherwise it panics because uses already existing (from exported data) types.
   315  		if loadMode >= LoadModeTypesInfo {
   316  			pkg.Types = types.NewPackage(pkg.PkgPath, pkg.Name)
   317  		}
   318  		return lp.loadFromSource(loadMode)
   319  	}
   320  
   321  	return nil
   322  }
   323  
   324  func (lp *loadingPackage) decUse(canClearTypes bool) {
   325  	lp.decUseMutex.Lock()
   326  	defer lp.decUseMutex.Unlock()
   327  
   328  	for _, act := range lp.actions {
   329  		pass := act.pass
   330  		if pass == nil {
   331  			continue
   332  		}
   333  
   334  		pass.Files = nil
   335  		pass.TypesInfo = nil
   336  		pass.TypesSizes = nil
   337  		pass.ResultOf = nil
   338  		pass.Pkg = nil
   339  		pass.OtherFiles = nil
   340  		pass.AllObjectFacts = nil
   341  		pass.AllPackageFacts = nil
   342  		pass.ImportObjectFact = nil
   343  		pass.ExportObjectFact = nil
   344  		pass.ImportPackageFact = nil
   345  		pass.ExportPackageFact = nil
   346  		act.pass = nil
   347  		act.deps = nil
   348  		if act.result != nil {
   349  			if isMemoryDebug {
   350  				debugf("%s: decUse: nilling act result of size %d bytes", act, sizeOfValueTreeBytes(act.result))
   351  			}
   352  			act.result = nil
   353  		}
   354  	}
   355  
   356  	lp.pkg.Syntax = nil
   357  	lp.pkg.TypesInfo = nil
   358  	lp.pkg.TypesSizes = nil
   359  
   360  	// Can't set lp.pkg.Imports to nil because of loadFromExportData.visit.
   361  
   362  	dependents := atomic.AddInt32(&lp.dependents, -1)
   363  	if dependents != 0 {
   364  		return
   365  	}
   366  
   367  	if canClearTypes {
   368  		// canClearTypes is set to true if we can discard type
   369  		// information after the package and its dependents have been
   370  		// processed. This is the case when no whole program checkers (unused) are
   371  		// being run.
   372  		lp.pkg.Types = nil
   373  	}
   374  	lp.pkg = nil
   375  
   376  	for _, imp := range lp.imports {
   377  		imp.decUse(canClearTypes)
   378  	}
   379  	lp.imports = nil
   380  
   381  	for _, act := range lp.actions {
   382  		if !lp.isInitial {
   383  			act.pkg = nil
   384  		}
   385  		act.packageFacts = nil
   386  		act.objectFacts = nil
   387  	}
   388  	lp.actions = nil
   389  }
   390  
   391  func (lp *loadingPackage) convertError(err error) []packages.Error {
   392  	var errs []packages.Error
   393  	// taken from go/packages
   394  	switch err := err.(type) {
   395  	case packages.Error:
   396  		// from driver
   397  		errs = append(errs, err)
   398  
   399  	case *os.PathError:
   400  		// from parser
   401  		errs = append(errs, packages.Error{
   402  			Pos:  err.Path + ":1",
   403  			Msg:  err.Err.Error(),
   404  			Kind: packages.ParseError,
   405  		})
   406  
   407  	case scanner.ErrorList:
   408  		// from parser
   409  		for _, err := range err {
   410  			errs = append(errs, packages.Error{
   411  				Pos:  err.Pos.String(),
   412  				Msg:  err.Msg,
   413  				Kind: packages.ParseError,
   414  			})
   415  		}
   416  
   417  	case types.Error:
   418  		// from type checker
   419  		errs = append(errs, packages.Error{
   420  			Pos:  err.Fset.Position(err.Pos).String(),
   421  			Msg:  err.Msg,
   422  			Kind: packages.TypeError,
   423  		})
   424  
   425  	default:
   426  		// unexpected impoverished error from parser?
   427  		errs = append(errs, packages.Error{
   428  			Pos:  "-",
   429  			Msg:  err.Error(),
   430  			Kind: packages.UnknownError,
   431  		})
   432  
   433  		// If you see this error message, please file a bug.
   434  		lp.log.Warnf("Internal error: error %q (%T) without position", err, err)
   435  	}
   436  	return errs
   437  }
   438  
   439  func (lp *loadingPackage) String() string {
   440  	return fmt.Sprintf("%s@%s", lp.pkg.PkgPath, lp.pkg.Name)
   441  }
   442  
   443  type importerFunc func(path string) (*types.Package, error)
   444  
   445  func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
   446  
   447  func sizeOfValueTreeBytes(v interface{}) int {
   448  	return sizeOfReflectValueTreeBytes(reflect.ValueOf(v), map[uintptr]struct{}{})
   449  }
   450  
   451  func sizeOfReflectValueTreeBytes(rv reflect.Value, visitedPtrs map[uintptr]struct{}) int {
   452  	switch rv.Kind() {
   453  	case reflect.Ptr:
   454  		ptrSize := int(rv.Type().Size())
   455  		if rv.IsNil() {
   456  			return ptrSize
   457  		}
   458  		ptr := rv.Pointer()
   459  		if _, ok := visitedPtrs[ptr]; ok {
   460  			return 0
   461  		}
   462  		visitedPtrs[ptr] = struct{}{}
   463  		return ptrSize + sizeOfReflectValueTreeBytes(rv.Elem(), visitedPtrs)
   464  	case reflect.Interface:
   465  		if rv.IsNil() {
   466  			return 0
   467  		}
   468  		return sizeOfReflectValueTreeBytes(rv.Elem(), visitedPtrs)
   469  	case reflect.Struct:
   470  		ret := 0
   471  		for i := 0; i < rv.NumField(); i++ {
   472  			ret += sizeOfReflectValueTreeBytes(rv.Field(i), visitedPtrs)
   473  		}
   474  		return ret
   475  	case reflect.Slice, reflect.Array, reflect.Chan:
   476  		return int(rv.Type().Size()) + rv.Cap()*int(rv.Type().Elem().Size())
   477  	case reflect.Map:
   478  		ret := 0
   479  		for _, key := range rv.MapKeys() {
   480  			mv := rv.MapIndex(key)
   481  			ret += sizeOfReflectValueTreeBytes(key, visitedPtrs)
   482  			ret += sizeOfReflectValueTreeBytes(mv, visitedPtrs)
   483  		}
   484  		return ret
   485  	case reflect.String:
   486  		return rv.Len()
   487  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
   488  		reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
   489  		reflect.Uintptr, reflect.Bool, reflect.Float32, reflect.Float64,
   490  		reflect.Complex64, reflect.Complex128, reflect.Func, reflect.UnsafePointer:
   491  		return int(rv.Type().Size())
   492  	case reflect.Invalid:
   493  		return 0
   494  	default:
   495  		panic("unknown rv of type " + fmt.Sprint(rv))
   496  	}
   497  }