diff --git a/go.mod b/go.mod index 3b863fab7daf..d7dfac693082 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,6 @@ require ( github.com/gofrs/flock v0.8.0 github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a - github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a github.com/golangci/ineffassign v0.0.0-20190609212857-42439a7714cc @@ -35,6 +34,7 @@ require ( github.com/jgautheron/goconst v0.0.0-20201117150253-ccae5bf973f3 github.com/jingyugao/rowserrcheck v0.0.0-20210130005344-c6a0c12dd98d github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3 + github.com/kisielk/errcheck v1.6.0 github.com/kulti/thelper v0.4.0 github.com/kunwardeep/paralleltest v1.0.2 github.com/kyoh86/exportloopref v0.1.8 diff --git a/go.sum b/go.sum index a282bd68e052..4ef6a4476232 100644 --- a/go.sum +++ b/go.sum @@ -134,8 +134,6 @@ github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5 github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= -github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6 h1:YYWNAGTKWhKpcLLt7aSj/odlKrSrelQwlovBpDuf19w= -github.com/golangci/errcheck v0.0.0-20181223084120-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw= github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= github.com/golangci/gofmt v0.0.0-20190930125516-244bba706f1a h1:iR3fYXUjHCR97qWS8ch1y9zPNsgXThGwjKPrYfqMPks= @@ -222,6 +220,8 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.6.0 h1:YTDO4pNy7AUN/021p+JGHycQyYNIyMoenM1YDVK6RlY= +github.com/kisielk/errcheck v1.6.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= @@ -578,6 +578,7 @@ golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWc golang.org/x/tools v0.0.0-20200414032229-332987a829c3/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200422022333-3d57cf2e726e/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200622203043-20e05c1c8ffa/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200624225443-88f3c62a19ff/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200625211823-6506e20df31f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= diff --git a/pkg/commands/executor.go b/pkg/commands/executor.go index 0becc9900db6..7f4702ed6adc 100644 --- a/pkg/commands/executor.go +++ b/pkg/commands/executor.go @@ -205,7 +205,9 @@ func computeConfigSalt(cfg *config.Config) ([]byte, error) { configData.WriteString("\nbuild-tags=%s" + strings.Join(cfg.Run.BuildTags, ",")) h := sha256.New() - h.Write(configData.Bytes()) //nolint:errcheck + if _, err := h.Write(configData.Bytes()); err != nil { + return nil, err + } return h.Sum(nil), nil } diff --git a/pkg/golinters/errcheck.go b/pkg/golinters/errcheck.go index 7df11fc8739e..9aac7326a0ca 100644 --- a/pkg/golinters/errcheck.go +++ b/pkg/golinters/errcheck.go @@ -10,9 +10,10 @@ import ( "strings" "sync" - errcheck "github.com/golangci/errcheck/golangci" + "github.com/kisielk/errcheck/errcheck" "github.com/pkg/errors" "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/packages" "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/fsutils" @@ -23,12 +24,15 @@ import ( func NewErrcheck() *goanalysis.Linter { const linterName = "errcheck" + var mu sync.Mutex var res []goanalysis.Issue + analyzer := &analysis.Analyzer{ Name: linterName, Doc: goanalysis.TheOnlyanalyzerDoc, } + return goanalysis.NewLinter( linterName, "Errcheck is a program for checking for unchecked errors "+ @@ -36,38 +40,54 @@ func NewErrcheck() *goanalysis.Linter { []*analysis.Analyzer{analyzer}, nil, ).WithContextSetter(func(lintCtx *linter.Context) { + // copied from errcheck + checker, err := getChecker(&lintCtx.Settings().Errcheck) + if err != nil { + lintCtx.Log.Errorf("failed to get checker: %v", err) + return + } + + checker.Tags = lintCtx.Cfg.Run.BuildTags + analyzer.Run = func(pass *analysis.Pass) (interface{}, error) { - prog := goanalysis.MakeFakeLoaderProgram(pass) - errCfg, err := genConfig(&lintCtx.Settings().Errcheck) - if err != nil { - return nil, err - } - errcheckIssues, err := errcheck.RunWithConfig(prog, errCfg) - if err != nil { - return nil, err + pkg := &packages.Package{ + Fset: pass.Fset, + Syntax: pass.Files, + Types: pass.Pkg, + TypesInfo: pass.TypesInfo, } - if len(errcheckIssues) == 0 { + errcheckIssues := checker.CheckPackage(pkg).Unique() + if len(errcheckIssues.UncheckedErrors) == 0 { return nil, nil } - issues := make([]goanalysis.Issue, 0, len(errcheckIssues)) - for _, i := range errcheckIssues { + issues := make([]goanalysis.Issue, len(errcheckIssues.UncheckedErrors)) + for i, err := range errcheckIssues.UncheckedErrors { var text string - if i.FuncName != "" { - text = fmt.Sprintf("Error return value of %s is not checked", formatCode(i.FuncName, lintCtx.Cfg)) + if err.FuncName != "" { + text = fmt.Sprintf( + "Error return value of %s is not checked", + formatCode(err.SelectorName, lintCtx.Cfg), + ) } else { text = "Error return value is not checked" } - issues = append(issues, goanalysis.NewIssue(&result.Issue{ - FromLinter: linterName, - Text: text, - Pos: i.Pos, - }, pass)) + + issues[i] = goanalysis.NewIssue( + &result.Issue{ + FromLinter: linterName, + Text: text, + Pos: err.Pos, + }, + pass, + ) } + mu.Lock() res = append(res, issues...) mu.Unlock() + return nil, nil } }).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue { @@ -104,16 +124,23 @@ func parseIgnoreConfig(s string) (map[string]*regexp.Regexp, error) { return cfg, nil } -func genConfig(errCfg *config.ErrcheckSettings) (*errcheck.Config, error) { +func getChecker(errCfg *config.ErrcheckSettings) (*errcheck.Checker, error) { ignoreConfig, err := parseIgnoreConfig(errCfg.Ignore) if err != nil { return nil, errors.Wrap(err, "failed to parse 'ignore' directive") } - c := &errcheck.Config{ - Ignore: ignoreConfig, - Blank: errCfg.CheckAssignToBlank, - Asserts: errCfg.CheckTypeAssertions, + checker := errcheck.Checker{ + Exclusions: errcheck.Exclusions{ + BlankAssignments: !errCfg.CheckAssignToBlank, + TypeAssertions: !errCfg.CheckTypeAssertions, + SymbolRegexpsByPackage: map[string]*regexp.Regexp{}, + Symbols: append([]string{}, errcheck.DefaultExcludedSymbols...), + }, + } + + for pkg, re := range ignoreConfig { + checker.Exclusions.SymbolRegexpsByPackage[pkg] = re } if errCfg.Exclude != "" { @@ -121,10 +148,11 @@ func genConfig(errCfg *config.ErrcheckSettings) (*errcheck.Config, error) { if err != nil { return nil, err } - c.Exclude = exclude + + checker.Exclusions.Symbols = append(checker.Exclusions.Symbols, exclude...) } - return c, nil + return &checker, nil } func getFirstPathArg() string { @@ -192,7 +220,7 @@ func setupConfigFileSearch(name string) []string { return configSearchPaths } -func readExcludeFile(name string) (map[string]bool, error) { +func readExcludeFile(name string) ([]string, error) { var err error var fh *os.File @@ -205,13 +233,17 @@ func readExcludeFile(name string) (map[string]bool, error) { if fh == nil { return nil, errors.Wrapf(err, "failed reading exclude file: %s", name) } + scanner := bufio.NewScanner(fh) - exclude := make(map[string]bool) + + var excludes []string for scanner.Scan() { - exclude[scanner.Text()] = true + excludes = append(excludes, scanner.Text()) } + if err := scanner.Err(); err != nil { return nil, errors.Wrapf(err, "failed scanning file: %s", name) } - return exclude, nil + + return excludes, nil } diff --git a/scripts/expand_website_templates/main.go b/scripts/expand_website_templates/main.go index 7705f96f2c90..edc1dec3c2dd 100644 --- a/scripts/expand_website_templates/main.go +++ b/scripts/expand_website_templates/main.go @@ -55,7 +55,9 @@ func updateStateFile(replacements map[string]string) error { } h := sha256.New() - h.Write(replBytes) //nolint:errcheck + if _, err := h.Write(replBytes); err != nil { + return err + } var contentBuf bytes.Buffer contentBuf.WriteString("This file stores hash of website templates to trigger " + diff --git a/test/run_test.go b/test/run_test.go index c9bd7a801b0e..7c889cfcde8b 100644 --- a/test/run_test.go +++ b/test/run_test.go @@ -135,7 +135,7 @@ func TestSortedResults(t *testing.T) { "--sort-results=false", strings.Join([]string{ "testdata/sort_results/main.go:12:5: `db` is unused (deadcode)", - "testdata/sort_results/main.go:15:13: Error return value of `returnError` is not checked (errcheck)", + "testdata/sort_results/main.go:15:13: Error return value is not checked (errcheck)", "testdata/sort_results/main.go:8:6: func `returnError` is unused (unused)", }, "\n"), }, @@ -144,7 +144,7 @@ func TestSortedResults(t *testing.T) { strings.Join([]string{ "testdata/sort_results/main.go:8:6: func `returnError` is unused (unused)", "testdata/sort_results/main.go:12:5: `db` is unused (deadcode)", - "testdata/sort_results/main.go:15:13: Error return value of `returnError` is not checked (errcheck)", + "testdata/sort_results/main.go:15:13: Error return value is not checked (errcheck)", }, "\n"), }, } diff --git a/test/testdata/errcheck.go b/test/testdata/errcheck.go index 0d6c118bd36a..dc4343507a09 100644 --- a/test/testdata/errcheck.go +++ b/test/testdata/errcheck.go @@ -12,7 +12,7 @@ func RetErr() error { } func MissedErrorCheck() { - RetErr() // ERROR "Error return value of `RetErr` is not checked" + RetErr() // ERROR "Error return value is not checked" } func IgnoreCloseMissingErrHandling() error { diff --git a/test/testdata/errcheck_ignore.go b/test/testdata/errcheck_ignore.go index 197f46ed7e06..8528d78bae32 100644 --- a/test/testdata/errcheck_ignore.go +++ b/test/testdata/errcheck_ignore.go @@ -12,8 +12,8 @@ func TestErrcheckIgnoreOs() { _, _ = os.Open("f.txt") } -func TestErrcheckNoIgnoreFmt(s string) int { - n, _ := fmt.Println(s) // ERROR "Error return value of `fmt.Println` is not checked" +func TestErrcheckIgnoreFmt(s string) int { + n, _ := fmt.Println(s) return n } diff --git a/test/testdata/errcheck_ignore_default.go b/test/testdata/errcheck_ignore_default.go index da59c6d0f9e2..18b090c20eb8 100644 --- a/test/testdata/errcheck_ignore_default.go +++ b/test/testdata/errcheck_ignore_default.go @@ -3,10 +3,17 @@ package testdata import ( + "crypto/sha256" "fmt" "os" ) +func TestErrcheckIgnoreHashWriteByDefault() []byte { + h := sha256.New() + h.Write([]byte("food")) + return h.Sum(nil) +} + func TestErrcheckIgnoreFmtByDefault(s string) int { n, _ := fmt.Println(s) return n diff --git a/test/testdata/errcheck_type_assertions.go b/test/testdata/errcheck_type_assertions.go new file mode 100644 index 000000000000..a85d69ddfbfd --- /dev/null +++ b/test/testdata/errcheck_type_assertions.go @@ -0,0 +1,7 @@ +//args: -Eerrcheck +//config: linters-settings.errcheck.check-type-assertions=true +package testdata + +func ErrorTypeAssertion(filter map[string]interface{}) bool { + return filter["messages_sent.messageid"].(map[string]interface{})["$ne"] != nil +}