Add go-errorlint (#1420)

* Add errorlint

* Add errorlint config example
This commit is contained in:
Vladimir Evgrafov 2020-10-09 15:42:48 +03:00 committed by GitHub
parent 501f00c6b0
commit 796a958805
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 167 additions and 2 deletions

View File

@ -331,6 +331,9 @@ linters-settings:
# Choose whether or not to use the extra rules that are disabled
# by default
extra-rules: false
errorlint:
# Report non-wrapping error creation using fmt.Errorf
errorf: true
# The custom section can be used to define linter plugins to be loaded at runtime. See README doc
# for more info.

1
go.mod
View File

@ -38,6 +38,7 @@ require (
github.com/nakabonne/nestif v0.3.0
github.com/nishanths/exhaustive v0.0.0-20200811152831-6cf413ae40e0
github.com/pkg/errors v0.9.1
github.com/polyfloyd/go-errorlint v0.0.0-20201006195004-351e25ade6e3
github.com/ryancurrah/gomodguard v1.1.0
github.com/ryanrolds/sqlclosecheck v0.3.0
github.com/securego/gosec/v2 v2.4.0

8
go.sum generated
View File

@ -223,6 +223,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kyoh86/exportloopref v0.1.7 h1:u+iHuTbkbTS2D/JP7fCuZDo/t3rBVGo3Hf58Rc+lQVY=
github.com/kyoh86/exportloopref v0.1.7/go.mod h1:h1rDl2Kdj97+Kwh4gdz3ujE7XHmH51Q0lUiZ1z4NLj8=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
@ -233,8 +234,6 @@ github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb h1:RHba4YImhrUVQDHUC
github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@ -242,6 +241,7 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@ -294,6 +294,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/polyfloyd/go-errorlint v0.0.0-20201006195004-351e25ade6e3 h1:Amgs0nbayPhBNGh1qPqqr2e7B2qNAcBgRjnBH/lmn8k=
github.com/polyfloyd/go-errorlint v0.0.0-20201006195004-351e25ade6e3/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
@ -457,6 +459,7 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -520,6 +523,7 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=

View File

@ -249,6 +249,7 @@ type LintersSettings struct {
NoLintLint NoLintLintSettings
Exhaustive ExhaustiveSettings
Gofumpt GofumptSettings
ErrorLint ErrorLintSettings
Custom map[string]CustomLinterSettings
}
@ -360,6 +361,10 @@ type GofumptSettings struct {
ExtraRules bool `mapstructure:"extra-rules"`
}
type ErrorLintSettings struct {
Errorf bool `mapstructure:"errorf"`
}
var defaultLintersSettings = LintersSettings{
Lll: LllSettings{
LineLength: 120,
@ -416,6 +421,9 @@ var defaultLintersSettings = LintersSettings{
Gofumpt: GofumptSettings{
ExtraRules: false,
},
ErrorLint: ErrorLintSettings{
Errorf: true,
},
}
type CustomLinterSettings struct {

View File

@ -0,0 +1,27 @@
package golinters
import (
"github.com/polyfloyd/go-errorlint/errorlint"
"golang.org/x/tools/go/analysis"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
)
func NewErrorLint(cfg *config.ErrorLintSettings) *goanalysis.Linter {
a := errorlint.NewAnalyzer()
cfgMap := map[string]map[string]interface{}{}
if cfg != nil {
cfgMap[a.Name] = map[string]interface{}{
"errorf": cfg.Errorf,
}
}
return goanalysis.NewLinter(
"errorlint",
"go-errorlint is a source code linter for Go software "+
"that can be used to find code that will cause problems"+
"with the error wrapping scheme introduced in Go 1.13.",
[]*analysis.Analyzer{a},
cfgMap,
).WithLoadMode(goanalysis.LoadModeTypesInfo)
}

View File

@ -90,10 +90,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
var govetCfg *config.GovetSettings
var testpackageCfg *config.TestpackageSettings
var exhaustiveCfg *config.ExhaustiveSettings
var errorlintCfg *config.ErrorLintSettings
if m.cfg != nil {
govetCfg = &m.cfg.LintersSettings.Govet
testpackageCfg = &m.cfg.LintersSettings.Testpackage
exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive
errorlintCfg = &m.cfg.LintersSettings.ErrorLint
}
const megacheckName = "megacheck"
lcs := []*linter.Config{
@ -315,6 +317,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
WithPresets(linter.PresetStyle).
WithLoadForGoAnalysis().
WithURL("https://github.com/moricho/tparallel"),
linter.NewConfig(golinters.NewErrorLint(errorlintCfg)).
WithPresets(linter.PresetBugs).
WithLoadForGoAnalysis().
WithURL("https://github.com/polyfloyd/go-errorlint"),
// nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives
linter.NewConfig(golinters.NewNoLintLint()).

90
test/testdata/errorlint.go vendored Normal file
View File

@ -0,0 +1,90 @@
//args: -Eerrorlint
package testdata
import (
"errors"
"log"
)
var errFoo = errors.New("foo")
func doThing() error {
return errFoo
}
func compare() {
err := doThing()
if errors.Is(err, errFoo) {
log.Println("ErrFoo")
}
if err == nil {
log.Println("nil")
}
if err != nil {
log.Println("nil")
}
if nil == err {
log.Println("nil")
}
if nil != err {
log.Println("nil")
}
if err == errFoo { // ERROR "comparing with == will fail on wrapped errors. Use errors.Is to check for a specific error"
log.Println("errFoo")
}
if err != errFoo { // ERROR "comparing with != will fail on wrapped errors. Use errors.Is to check for a specific error"
log.Println("not errFoo")
}
if errFoo == err { // ERROR "comparing with == will fail on wrapped errors. Use errors.Is to check for a specific error"
log.Println("errFoo")
}
if errFoo != err { // ERROR "comparing with != will fail on wrapped errors. Use errors.Is to check for a specific error"
log.Println("not errFoo")
}
switch err { // ERROR "switch on an error will fail on wrapped errors. Use errors.Is to check for specific errors"
case errFoo:
log.Println("errFoo")
}
switch doThing() { // ERROR "switch on an error will fail on wrapped errors. Use errors.Is to check for specific errors"
case errFoo:
log.Println("errFoo")
}
}
type myError struct{}
func (*myError) Error() string {
return "foo"
}
func doAnotherThing() error {
return &myError{}
}
func typeCheck() {
err := doAnotherThing()
var me *myError
if errors.As(err, &me) {
log.Println("myError")
}
_, ok := err.(*myError) // ERROR "type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors"
if ok {
log.Println("myError")
}
switch err.(type) { // ERROR "type switch on error will fail on wrapped errors. Use errors.As to check for specific errors"
case *myError:
log.Println("myError")
}
switch doAnotherThing().(type) { // ERROR "type switch on error will fail on wrapped errors. Use errors.As to check for specific errors"
case *myError:
log.Println("myError")
}
switch t := err.(type) { // ERROR "type switch on error will fail on wrapped errors. Use errors.As to check for specific errors"
case *myError:
log.Println("myError", t)
}
switch t := doAnotherThing().(type) { // ERROR "type switch on error will fail on wrapped errors. Use errors.As to check for specific errors"
case *myError:
log.Println("myError", t)
}
}

26
test/testdata/errorlint_errorf.go vendored Normal file
View File

@ -0,0 +1,26 @@
//args: -Eerrorlint
//config: linters-settings.errorlint.errorf=true
package testdata
import (
"errors"
"fmt"
)
type customError struct{}
func (customError) Error() string {
return "oops"
}
func wraps() {
err := errors.New("oops")
fmt.Errorf("error: %w", err)
fmt.Errorf("error: %v", err) // ERROR "non-wrapping format verb for fmt.Errorf. Use `%w` to format errors"
fmt.Errorf("%v %v", err, err) // ERROR "non-wrapping format verb for fmt.Errorf. Use `%w` to format errors"
fmt.Errorf("error: %s", err.Error()) // ERROR "non-wrapping format verb for fmt.Errorf. Use `%w` to format errors"
customError := customError{}
fmt.Errorf("error: %s", customError.Error()) // ERROR "non-wrapping format verb for fmt.Errorf. Use `%w` to format errors"
strErr := "oops"
fmt.Errorf("%v", strErr)
}