improve typecheck errors parsing
This commit is contained in:
parent
55a18ae18a
commit
dba3907ff3
@ -88,7 +88,7 @@ func (m Megacheck) Run(ctx context.Context, lintCtx *linter.Context) ([]result.I
|
||||
var errors []packages.Error
|
||||
for _, p := range lintCtx.NotCompilingPackages {
|
||||
errPkgs = append(errPkgs, p.String())
|
||||
errors = append(errors, libpackages.ExtractErrors(p)...)
|
||||
errors = append(errors, libpackages.ExtractErrors(p, lintCtx.ASTCache)...)
|
||||
}
|
||||
|
||||
warnText := fmt.Sprintf("Can't run megacheck because of compilation errors in packages %s",
|
||||
|
@ -2,12 +2,7 @@ package golinters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/tools/go/packages"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/lint/linter"
|
||||
@ -26,44 +21,31 @@ func (TypeCheck) Desc() string {
|
||||
}
|
||||
|
||||
func (lint TypeCheck) parseError(srcErr packages.Error) (*result.Issue, error) {
|
||||
// file:line(<optional>:colon)
|
||||
parts := strings.Split(srcErr.Pos, ":")
|
||||
if len(parts) == 1 {
|
||||
return nil, errors.New("no colons")
|
||||
}
|
||||
|
||||
file := parts[0]
|
||||
line, err := strconv.Atoi(parts[1])
|
||||
pos, err := libpackages.ParseErrorPosition(srcErr.Pos)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't parse line number %q: %s", parts[1], err)
|
||||
}
|
||||
|
||||
var column int
|
||||
if len(parts) == 3 { // no column
|
||||
column, err = strconv.Atoi(parts[2])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse column from %q", parts[2])
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result.Issue{
|
||||
Pos: token.Position{
|
||||
Filename: file,
|
||||
Line: line,
|
||||
Column: column,
|
||||
},
|
||||
Pos: *pos,
|
||||
Text: srcErr.Msg,
|
||||
FromLinter: lint.Name(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (lint TypeCheck) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
|
||||
uniqReportedIssues := map[string]bool{}
|
||||
|
||||
var res []result.Issue
|
||||
for _, pkg := range lintCtx.NotCompilingPackages {
|
||||
errors := libpackages.ExtractErrors(pkg)
|
||||
errors := libpackages.ExtractErrors(pkg, lintCtx.ASTCache)
|
||||
for _, err := range errors {
|
||||
i, perr := lint.parseError(err)
|
||||
if perr != nil { // failed to parse
|
||||
if uniqReportedIssues[err.Msg] {
|
||||
continue
|
||||
}
|
||||
uniqReportedIssues[err.Msg] = true
|
||||
lintCtx.Log.Errorf("typechecking error: %s", err.Msg)
|
||||
} else {
|
||||
res = append(res, *i)
|
||||
|
@ -334,7 +334,7 @@ func (cl ContextLoader) Load(ctx context.Context, linters []linter.Config) (*lin
|
||||
} else {
|
||||
for _, pkg := range pkgs {
|
||||
if pkg.IllTyped {
|
||||
cl.log.Infof("Pkg %s errors: %v", pkg.ID, libpackages.ExtractErrors(pkg))
|
||||
cl.log.Infof("Pkg %s errors: %v", pkg.ID, libpackages.ExtractErrors(pkg, astCache))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
38
pkg/packages/errors.go
Normal file
38
pkg/packages/errors.go
Normal file
@ -0,0 +1,38 @@
|
||||
package packages
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func ParseErrorPosition(pos string) (*token.Position, error) {
|
||||
// file:line(<optional>:colon)
|
||||
parts := strings.Split(pos, ":")
|
||||
if len(parts) == 1 {
|
||||
return nil, errors.New("no colons")
|
||||
}
|
||||
|
||||
file := parts[0]
|
||||
line, err := strconv.Atoi(parts[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't parse line number %q: %s", parts[1], err)
|
||||
}
|
||||
|
||||
var column int
|
||||
if len(parts) == 3 { // no column
|
||||
column, err = strconv.Atoi(parts[2])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse column from %q", parts[2])
|
||||
}
|
||||
}
|
||||
|
||||
return &token.Position{
|
||||
Filename: file,
|
||||
Line: line,
|
||||
Column: column,
|
||||
}, nil
|
||||
}
|
@ -3,10 +3,13 @@ package packages
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golangci/golangci-lint/pkg/lint/astcache"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
func ExtractErrors(pkg *packages.Package) []packages.Error {
|
||||
//nolint:gocyclo
|
||||
func ExtractErrors(pkg *packages.Package, astCache *astcache.Cache) []packages.Error {
|
||||
errors := extractErrorsImpl(pkg)
|
||||
if len(errors) == 0 {
|
||||
return errors
|
||||
@ -22,17 +25,18 @@ func ExtractErrors(pkg *packages.Package) []packages.Error {
|
||||
uniqErrors = append(uniqErrors, err)
|
||||
}
|
||||
|
||||
if len(pkg.Errors) == 0 && len(pkg.GoFiles) != 0 {
|
||||
// erorrs were extracted from deps and have at leat one file in package
|
||||
for i := range uniqErrors {
|
||||
// change pos to local file to properly process it by processors (properly read line etc)
|
||||
uniqErrors[i].Msg = fmt.Sprintf("%s: %s", uniqErrors[i].Pos, uniqErrors[i].Msg)
|
||||
uniqErrors[i].Pos = fmt.Sprintf("%s:1", pkg.GoFiles[0])
|
||||
}
|
||||
}
|
||||
|
||||
// some errors like "code in directory expects import" don't have Pos, set it here
|
||||
if len(pkg.GoFiles) != 0 {
|
||||
// errors were extracted from deps and have at leat one file in package
|
||||
for i := range uniqErrors {
|
||||
errPos, parseErr := ParseErrorPosition(uniqErrors[i].Pos)
|
||||
if parseErr != nil || astCache.Get(errPos.Filename) == nil {
|
||||
// change pos to local file to properly process it by processors (properly read line etc)
|
||||
uniqErrors[i].Msg = fmt.Sprintf("%s: %s", uniqErrors[i].Pos, uniqErrors[i].Msg)
|
||||
uniqErrors[i].Pos = fmt.Sprintf("%s:1", pkg.GoFiles[0])
|
||||
}
|
||||
}
|
||||
|
||||
// some errors like "code in directory expects import" don't have Pos, set it here
|
||||
for i := range uniqErrors {
|
||||
err := &uniqErrors[i]
|
||||
if err.Pos == "" {
|
||||
|
@ -42,6 +42,11 @@ func (p *AutogeneratedExclude) Process(issues []result.Issue) ([]result.Issue, e
|
||||
}
|
||||
|
||||
func (p *AutogeneratedExclude) shouldPassIssue(i *result.Issue) (bool, error) {
|
||||
if i.FromLinter == "typecheck" {
|
||||
// don't hide typechecking errors in generated files: users expect to see why the project isn't compiling
|
||||
return true, nil
|
||||
}
|
||||
|
||||
fs, err := p.getOrCreateFileSummary(i)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -24,7 +24,8 @@ func TestEmptyDirRun(t *testing.T) {
|
||||
|
||||
func TestNotExistingDirRun(t *testing.T) {
|
||||
testshared.NewLintRunner(t).Run(getTestDataDir("no_such_dir")).
|
||||
ExpectHasIssue(`cannot find package \"./testdata/no_such_dir\"`)
|
||||
ExpectExitCode(exitcodes.WarningInTest).
|
||||
ExpectOutputContains(`cannot find package \"./testdata/no_such_dir\"`)
|
||||
}
|
||||
|
||||
func TestSymlinkLoop(t *testing.T) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user