Fix staticcheck panic on packages that do not compile
The bug was introduced in golangci-lint when migrating staticcheck to go/packages. Also, thanks to pkg.IllTyped we can analyze as max as we can by staticcheck. Relates: #418, #369, #429, #489
This commit is contained in:
parent
09677d574e
commit
39f46be460
@ -24,4 +24,8 @@ var (
|
||||
Message: "no go files to analyze",
|
||||
Code: NoGoFiles,
|
||||
}
|
||||
ErrFailure = &ExitError{
|
||||
Message: "failed to analyze",
|
||||
Code: Failure,
|
||||
}
|
||||
)
|
||||
|
@ -18,9 +18,6 @@ import (
|
||||
"github.com/golangci/go-tools/unused"
|
||||
"golang.org/x/tools/go/packages"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/fsutils"
|
||||
libpackages "github.com/golangci/golangci-lint/pkg/packages"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||
"github.com/golangci/golangci-lint/pkg/result"
|
||||
)
|
||||
@ -179,67 +176,7 @@ func (m MegacheckMetalinter) isValidChild(name string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func prettifyCompilationError(err packages.Error) error {
|
||||
i, _ := TypeCheck{}.parseError(err)
|
||||
if i == nil {
|
||||
return err
|
||||
}
|
||||
|
||||
shortFilename, pathErr := fsutils.ShortestRelPath(i.Pos.Filename, "")
|
||||
if pathErr != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
errText := shortFilename
|
||||
if i.Line() != 0 {
|
||||
errText += fmt.Sprintf(":%d", i.Line())
|
||||
}
|
||||
errText += fmt.Sprintf(": %s", i.Text)
|
||||
return errors.New(errText)
|
||||
}
|
||||
|
||||
func (m megacheck) canAnalyze(lintCtx *linter.Context) bool {
|
||||
if len(lintCtx.NotCompilingPackages) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
var errPkgs []string
|
||||
var errs []packages.Error
|
||||
for _, p := range lintCtx.NotCompilingPackages {
|
||||
if p.Name == "main" {
|
||||
// megacheck crashes on not compiling packages but main packages
|
||||
// aren't reachable by megacheck: other packages can't depend on them.
|
||||
continue
|
||||
}
|
||||
|
||||
errPkgs = append(errPkgs, p.String())
|
||||
errs = append(errs, libpackages.ExtractErrors(p, lintCtx.ASTCache)...)
|
||||
}
|
||||
|
||||
if len(errPkgs) == 0 { // only main packages do not compile
|
||||
return true
|
||||
}
|
||||
|
||||
// TODO: print real linter names in this message
|
||||
warnText := fmt.Sprintf("Can't run megacheck because of compilation errors in packages %s", errPkgs)
|
||||
if len(errs) != 0 {
|
||||
warnText += fmt.Sprintf(": %s", prettifyCompilationError(errs[0]))
|
||||
if len(errs) > 1 {
|
||||
const runCmd = "golangci-lint run --no-config --disable-all -E typecheck"
|
||||
warnText += fmt.Sprintf(" and %d more errors: run `%s` to see all errors", len(errs)-1, runCmd)
|
||||
}
|
||||
}
|
||||
lintCtx.Log.Warnf("%s", warnText)
|
||||
|
||||
// megacheck crashes if there are not compiling packages
|
||||
return false
|
||||
}
|
||||
|
||||
func (m megacheck) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
|
||||
if !m.canAnalyze(lintCtx) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
issues, err := m.runMegacheck(lintCtx.Packages, lintCtx.Settings().Unused.CheckExported)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to run megacheck")
|
||||
|
@ -23,7 +23,6 @@ import (
|
||||
"github.com/golangci/golangci-lint/pkg/lint/astcache"
|
||||
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||
"github.com/golangci/golangci-lint/pkg/logutils"
|
||||
libpackages "github.com/golangci/golangci-lint/pkg/packages"
|
||||
)
|
||||
|
||||
type ContextLoader struct {
|
||||
@ -265,6 +264,10 @@ func (cl ContextLoader) loadPackages(ctx context.Context, loadMode packages.Load
|
||||
if strings.Contains(err.Msg, "no Go files") {
|
||||
return nil, errors.Wrapf(exitcodes.ErrNoGoFiles, "package %s", pkg.PkgPath)
|
||||
}
|
||||
if strings.Contains(err.Msg, "cannot find package") {
|
||||
// when analyzing not existing directory
|
||||
return nil, errors.Wrap(exitcodes.ErrFailure, err.Msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,29 +361,23 @@ func (cl ContextLoader) Load(ctx context.Context, linters []*linter.Config) (*li
|
||||
Log: cl.log,
|
||||
}
|
||||
|
||||
if prog != nil {
|
||||
saveNotCompilingPackages(ret)
|
||||
} else {
|
||||
for _, pkg := range pkgs {
|
||||
if pkg.IllTyped {
|
||||
cl.log.Infof("Pkg %s errors: %v", pkg.ID, libpackages.ExtractErrors(pkg, astCache))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
separateNotCompilingPackages(ret)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// saveNotCompilingPackages saves not compiling packages into separate slice:
|
||||
// a lot of linters crash on such packages. Leave them only for those linters
|
||||
// which can work with them.
|
||||
func saveNotCompilingPackages(lintCtx *linter.Context) {
|
||||
// separateNotCompilingPackages moves not compiling packages into separate slice:
|
||||
// a lot of linters crash on such packages
|
||||
func separateNotCompilingPackages(lintCtx *linter.Context) {
|
||||
goodPkgs := make([]*packages.Package, 0, len(lintCtx.Packages))
|
||||
for _, pkg := range lintCtx.Packages {
|
||||
if pkg.IllTyped {
|
||||
lintCtx.NotCompilingPackages = append(lintCtx.NotCompilingPackages, pkg)
|
||||
} else {
|
||||
goodPkgs = append(goodPkgs, pkg)
|
||||
}
|
||||
}
|
||||
|
||||
lintCtx.Packages = goodPkgs
|
||||
if len(lintCtx.NotCompilingPackages) != 0 {
|
||||
lintCtx.Log.Infof("Packages that do not compile: %+v", lintCtx.NotCompilingPackages)
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ func TestEmptyDirRun(t *testing.T) {
|
||||
|
||||
func TestNotExistingDirRun(t *testing.T) {
|
||||
testshared.NewLintRunner(t).Run(getTestDataDir("no_such_dir")).
|
||||
ExpectExitCode(exitcodes.WarningInTest).
|
||||
ExpectExitCode(exitcodes.Failure).
|
||||
ExpectOutputContains(`cannot find package \"./testdata/no_such_dir\"`)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user