full diff: https://github.com/dominikh/go-tools/compare/2019.2.3...2020.1.3 Also updates tests to accomodate updated rules: --- FAIL: TestSourcesFromTestdataWithIssuesDir/staticcheck.go (0.43s) linters_test.go:137: [run --disable-all --print-issued-lines=false --print-linter-name=false --out-format=line-number --max-same-issues=10 -Estaticcheck --no-config testdata/staticcheck.go] linters_test.go:33: Error Trace: linters_test.go:33 linters_test.go:138 linters_test.go:53 Error: Received unexpected error: staticcheck.go:11: no match for `self-assignment of x to x` vs ["SA4006: this value of `x` is never used"] in: staticcheck.go:11:2: SA4006: this value of `x` is never used unmatched errors staticcheck.go:11:2: SA4006: this value of `x` is never used Test: TestSourcesFromTestdataWithIssuesDir/staticcheck.go Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
		
			
				
	
	
		
			211 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package loader
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"go/ast"
 | 
						|
	"go/parser"
 | 
						|
	"go/scanner"
 | 
						|
	"go/token"
 | 
						|
	"go/types"
 | 
						|
	"log"
 | 
						|
	"os"
 | 
						|
 | 
						|
	"golang.org/x/tools/go/gcexportdata"
 | 
						|
	"golang.org/x/tools/go/packages"
 | 
						|
)
 | 
						|
 | 
						|
// Graph resolves patterns and returns packages with all the
 | 
						|
// information required to later load type information, and optionally
 | 
						|
// syntax trees.
 | 
						|
//
 | 
						|
// The provided config can set any setting with the exception of Mode.
 | 
						|
func Graph(cfg packages.Config, patterns ...string) ([]*packages.Package, error) {
 | 
						|
	cfg.Mode = packages.NeedName | packages.NeedImports | packages.NeedDeps | packages.NeedExportsFile | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedTypesSizes
 | 
						|
	pkgs, err := packages.Load(&cfg, patterns...)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	fset := token.NewFileSet()
 | 
						|
	packages.Visit(pkgs, nil, func(pkg *packages.Package) {
 | 
						|
		pkg.Fset = fset
 | 
						|
	})
 | 
						|
 | 
						|
	n := 0
 | 
						|
	for _, pkg := range pkgs {
 | 
						|
		if len(pkg.CompiledGoFiles) == 0 && len(pkg.Errors) == 0 && pkg.PkgPath != "unsafe" {
 | 
						|
			// If a package consists only of test files, then
 | 
						|
			// go/packages incorrectly(?) returns an empty package for
 | 
						|
			// the non-test variant. Get rid of those packages. See
 | 
						|
			// #646.
 | 
						|
			//
 | 
						|
			// Do not, however, skip packages that have errors. Those,
 | 
						|
			// too, may have no files, but we want to print the
 | 
						|
			// errors.
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		pkgs[n] = pkg
 | 
						|
		n++
 | 
						|
	}
 | 
						|
	return pkgs[:n], nil
 | 
						|
}
 | 
						|
 | 
						|
// LoadFromExport loads a package from export data. All of its
 | 
						|
// dependencies must have been loaded already.
 | 
						|
func LoadFromExport(pkg *packages.Package) error {
 | 
						|
	pkg.IllTyped = true
 | 
						|
	for path, pkg := range pkg.Imports {
 | 
						|
		if pkg.Types == nil {
 | 
						|
			return fmt.Errorf("dependency %q hasn't been loaded yet", path)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if pkg.ExportFile == "" {
 | 
						|
		return fmt.Errorf("no export data for %q", pkg.ID)
 | 
						|
	}
 | 
						|
	f, err := os.Open(pkg.ExportFile)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	defer f.Close()
 | 
						|
 | 
						|
	r, err := gcexportdata.NewReader(f)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	view := make(map[string]*types.Package)  // view seen by gcexportdata
 | 
						|
	seen := make(map[*packages.Package]bool) // all visited packages
 | 
						|
	var visit func(pkgs map[string]*packages.Package)
 | 
						|
	visit = func(pkgs map[string]*packages.Package) {
 | 
						|
		for _, pkg := range pkgs {
 | 
						|
			if !seen[pkg] {
 | 
						|
				seen[pkg] = true
 | 
						|
				view[pkg.PkgPath] = pkg.Types
 | 
						|
				visit(pkg.Imports)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	visit(pkg.Imports)
 | 
						|
	tpkg, err := gcexportdata.Read(r, pkg.Fset, view, pkg.PkgPath)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	pkg.Types = tpkg
 | 
						|
	pkg.IllTyped = false
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// LoadFromSource loads a package from source. All of its dependencies
 | 
						|
// must have been loaded already.
 | 
						|
func LoadFromSource(pkg *packages.Package) error {
 | 
						|
	pkg.IllTyped = true
 | 
						|
	pkg.Types = types.NewPackage(pkg.PkgPath, pkg.Name)
 | 
						|
 | 
						|
	// OPT(dh): many packages have few files, much fewer than there
 | 
						|
	// are CPU cores. Additionally, parsing each individual file is
 | 
						|
	// very fast. A naive parallel implementation of this loop won't
 | 
						|
	// be faster, and tends to be slower due to extra scheduling,
 | 
						|
	// bookkeeping and potentially false sharing of cache lines.
 | 
						|
	pkg.Syntax = make([]*ast.File, len(pkg.CompiledGoFiles))
 | 
						|
	for i, file := range pkg.CompiledGoFiles {
 | 
						|
		f, err := parser.ParseFile(pkg.Fset, file, nil, parser.ParseComments)
 | 
						|
		if err != nil {
 | 
						|
			pkg.Errors = append(pkg.Errors, convertError(err)...)
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		pkg.Syntax[i] = f
 | 
						|
	}
 | 
						|
	pkg.TypesInfo = &types.Info{
 | 
						|
		Types:      make(map[ast.Expr]types.TypeAndValue),
 | 
						|
		Defs:       make(map[*ast.Ident]types.Object),
 | 
						|
		Uses:       make(map[*ast.Ident]types.Object),
 | 
						|
		Implicits:  make(map[ast.Node]types.Object),
 | 
						|
		Scopes:     make(map[ast.Node]*types.Scope),
 | 
						|
		Selections: make(map[*ast.SelectorExpr]*types.Selection),
 | 
						|
	}
 | 
						|
 | 
						|
	importer := func(path string) (*types.Package, error) {
 | 
						|
		if path == "unsafe" {
 | 
						|
			return types.Unsafe, nil
 | 
						|
		}
 | 
						|
		if path == "C" {
 | 
						|
			// go/packages doesn't tell us that cgo preprocessing
 | 
						|
			// failed. When we subsequently try to parse the package,
 | 
						|
			// we'll encounter the raw C import.
 | 
						|
			return nil, errors.New("cgo preprocessing failed")
 | 
						|
		}
 | 
						|
		imp := pkg.Imports[path]
 | 
						|
		if imp == nil {
 | 
						|
			return nil, nil
 | 
						|
		}
 | 
						|
		if len(imp.Errors) > 0 {
 | 
						|
			return nil, imp.Errors[0]
 | 
						|
		}
 | 
						|
		return imp.Types, nil
 | 
						|
	}
 | 
						|
	tc := &types.Config{
 | 
						|
		Importer: importerFunc(importer),
 | 
						|
		Error: func(err error) {
 | 
						|
			pkg.Errors = append(pkg.Errors, convertError(err)...)
 | 
						|
		},
 | 
						|
	}
 | 
						|
	err := types.NewChecker(tc, pkg.Fset, pkg.Types, pkg.TypesInfo).Files(pkg.Syntax)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	pkg.IllTyped = false
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func convertError(err error) []packages.Error {
 | 
						|
	var errs []packages.Error
 | 
						|
	// taken from go/packages
 | 
						|
	switch err := err.(type) {
 | 
						|
	case packages.Error:
 | 
						|
		// from driver
 | 
						|
		errs = append(errs, err)
 | 
						|
 | 
						|
	case *os.PathError:
 | 
						|
		// from parser
 | 
						|
		errs = append(errs, packages.Error{
 | 
						|
			Pos:  err.Path + ":1",
 | 
						|
			Msg:  err.Err.Error(),
 | 
						|
			Kind: packages.ParseError,
 | 
						|
		})
 | 
						|
 | 
						|
	case scanner.ErrorList:
 | 
						|
		// from parser
 | 
						|
		for _, err := range err {
 | 
						|
			errs = append(errs, packages.Error{
 | 
						|
				Pos:  err.Pos.String(),
 | 
						|
				Msg:  err.Msg,
 | 
						|
				Kind: packages.ParseError,
 | 
						|
			})
 | 
						|
		}
 | 
						|
 | 
						|
	case types.Error:
 | 
						|
		// from type checker
 | 
						|
		errs = append(errs, packages.Error{
 | 
						|
			Pos:  err.Fset.Position(err.Pos).String(),
 | 
						|
			Msg:  err.Msg,
 | 
						|
			Kind: packages.TypeError,
 | 
						|
		})
 | 
						|
 | 
						|
	default:
 | 
						|
		// unexpected impoverished error from parser?
 | 
						|
		errs = append(errs, packages.Error{
 | 
						|
			Pos:  "-",
 | 
						|
			Msg:  err.Error(),
 | 
						|
			Kind: packages.UnknownError,
 | 
						|
		})
 | 
						|
 | 
						|
		// If you see this error message, please file a bug.
 | 
						|
		log.Printf("internal error: error %q (%T) without position", err, err)
 | 
						|
	}
 | 
						|
	return errs
 | 
						|
}
 | 
						|
 | 
						|
type importerFunc func(path string) (*types.Package, error)
 | 
						|
 | 
						|
func (f importerFunc) Import(path string) (*types.Package, error) { return f(path) }
 |