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  }