Skip to content

Commit 796a958

Browse files
authored
Add go-errorlint (#1420)
* Add errorlint * Add errorlint config example
1 parent 501f00c commit 796a958

File tree

8 files changed

+167
-2
lines changed

8 files changed

+167
-2
lines changed

.golangci.example.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,9 @@ linters-settings:
331331
# Choose whether or not to use the extra rules that are disabled
332332
# by default
333333
extra-rules: false
334+
errorlint:
335+
# Report non-wrapping error creation using fmt.Errorf
336+
errorf: true
334337

335338
# The custom section can be used to define linter plugins to be loaded at runtime. See README doc
336339
# for more info.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ require (
3838
github.com/nakabonne/nestif v0.3.0
3939
github.com/nishanths/exhaustive v0.0.0-20200811152831-6cf413ae40e0
4040
github.com/pkg/errors v0.9.1
41+
github.com/polyfloyd/go-errorlint v0.0.0-20201006195004-351e25ade6e3
4142
github.com/ryancurrah/gomodguard v1.1.0
4243
github.com/ryanrolds/sqlclosecheck v0.3.0
4344
github.com/securego/gosec/v2 v2.4.0

go.sum

Lines changed: 6 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/config/config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ type LintersSettings struct {
249249
NoLintLint NoLintLintSettings
250250
Exhaustive ExhaustiveSettings
251251
Gofumpt GofumptSettings
252+
ErrorLint ErrorLintSettings
252253

253254
Custom map[string]CustomLinterSettings
254255
}
@@ -360,6 +361,10 @@ type GofumptSettings struct {
360361
ExtraRules bool `mapstructure:"extra-rules"`
361362
}
362363

364+
type ErrorLintSettings struct {
365+
Errorf bool `mapstructure:"errorf"`
366+
}
367+
363368
var defaultLintersSettings = LintersSettings{
364369
Lll: LllSettings{
365370
LineLength: 120,
@@ -416,6 +421,9 @@ var defaultLintersSettings = LintersSettings{
416421
Gofumpt: GofumptSettings{
417422
ExtraRules: false,
418423
},
424+
ErrorLint: ErrorLintSettings{
425+
Errorf: true,
426+
},
419427
}
420428

421429
type CustomLinterSettings struct {

pkg/golinters/errorlint.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package golinters
2+
3+
import (
4+
"github.com/polyfloyd/go-errorlint/errorlint"
5+
"golang.org/x/tools/go/analysis"
6+
7+
"github.com/golangci/golangci-lint/pkg/config"
8+
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
9+
)
10+
11+
func NewErrorLint(cfg *config.ErrorLintSettings) *goanalysis.Linter {
12+
a := errorlint.NewAnalyzer()
13+
cfgMap := map[string]map[string]interface{}{}
14+
if cfg != nil {
15+
cfgMap[a.Name] = map[string]interface{}{
16+
"errorf": cfg.Errorf,
17+
}
18+
}
19+
return goanalysis.NewLinter(
20+
"errorlint",
21+
"go-errorlint is a source code linter for Go software "+
22+
"that can be used to find code that will cause problems"+
23+
"with the error wrapping scheme introduced in Go 1.13.",
24+
[]*analysis.Analyzer{a},
25+
cfgMap,
26+
).WithLoadMode(goanalysis.LoadModeTypesInfo)
27+
}

pkg/lint/lintersdb/manager.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,12 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
9090
var govetCfg *config.GovetSettings
9191
var testpackageCfg *config.TestpackageSettings
9292
var exhaustiveCfg *config.ExhaustiveSettings
93+
var errorlintCfg *config.ErrorLintSettings
9394
if m.cfg != nil {
9495
govetCfg = &m.cfg.LintersSettings.Govet
9596
testpackageCfg = &m.cfg.LintersSettings.Testpackage
9697
exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive
98+
errorlintCfg = &m.cfg.LintersSettings.ErrorLint
9799
}
98100
const megacheckName = "megacheck"
99101
lcs := []*linter.Config{
@@ -315,6 +317,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
315317
WithPresets(linter.PresetStyle).
316318
WithLoadForGoAnalysis().
317319
WithURL("https://github.com/moricho/tparallel"),
320+
linter.NewConfig(golinters.NewErrorLint(errorlintCfg)).
321+
WithPresets(linter.PresetBugs).
322+
WithLoadForGoAnalysis().
323+
WithURL("https://github.com/polyfloyd/go-errorlint"),
318324

319325
// nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives
320326
linter.NewConfig(golinters.NewNoLintLint()).

test/testdata/errorlint.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//args: -Eerrorlint
2+
package testdata
3+
4+
import (
5+
"errors"
6+
"log"
7+
)
8+
9+
var errFoo = errors.New("foo")
10+
11+
func doThing() error {
12+
return errFoo
13+
}
14+
15+
func compare() {
16+
err := doThing()
17+
if errors.Is(err, errFoo) {
18+
log.Println("ErrFoo")
19+
}
20+
if err == nil {
21+
log.Println("nil")
22+
}
23+
if err != nil {
24+
log.Println("nil")
25+
}
26+
if nil == err {
27+
log.Println("nil")
28+
}
29+
if nil != err {
30+
log.Println("nil")
31+
}
32+
if err == errFoo { // ERROR "comparing with == will fail on wrapped errors. Use errors.Is to check for a specific error"
33+
log.Println("errFoo")
34+
}
35+
if err != errFoo { // ERROR "comparing with != will fail on wrapped errors. Use errors.Is to check for a specific error"
36+
log.Println("not errFoo")
37+
}
38+
if errFoo == err { // ERROR "comparing with == will fail on wrapped errors. Use errors.Is to check for a specific error"
39+
log.Println("errFoo")
40+
}
41+
if errFoo != err { // ERROR "comparing with != will fail on wrapped errors. Use errors.Is to check for a specific error"
42+
log.Println("not errFoo")
43+
}
44+
switch err { // ERROR "switch on an error will fail on wrapped errors. Use errors.Is to check for specific errors"
45+
case errFoo:
46+
log.Println("errFoo")
47+
}
48+
switch doThing() { // ERROR "switch on an error will fail on wrapped errors. Use errors.Is to check for specific errors"
49+
case errFoo:
50+
log.Println("errFoo")
51+
}
52+
}
53+
54+
type myError struct{}
55+
56+
func (*myError) Error() string {
57+
return "foo"
58+
}
59+
60+
func doAnotherThing() error {
61+
return &myError{}
62+
}
63+
64+
func typeCheck() {
65+
err := doAnotherThing()
66+
var me *myError
67+
if errors.As(err, &me) {
68+
log.Println("myError")
69+
}
70+
_, ok := err.(*myError) // ERROR "type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors"
71+
if ok {
72+
log.Println("myError")
73+
}
74+
switch err.(type) { // ERROR "type switch on error will fail on wrapped errors. Use errors.As to check for specific errors"
75+
case *myError:
76+
log.Println("myError")
77+
}
78+
switch doAnotherThing().(type) { // ERROR "type switch on error will fail on wrapped errors. Use errors.As to check for specific errors"
79+
case *myError:
80+
log.Println("myError")
81+
}
82+
switch t := err.(type) { // ERROR "type switch on error will fail on wrapped errors. Use errors.As to check for specific errors"
83+
case *myError:
84+
log.Println("myError", t)
85+
}
86+
switch t := doAnotherThing().(type) { // ERROR "type switch on error will fail on wrapped errors. Use errors.As to check for specific errors"
87+
case *myError:
88+
log.Println("myError", t)
89+
}
90+
}

test/testdata/errorlint_errorf.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//args: -Eerrorlint
2+
//config: linters-settings.errorlint.errorf=true
3+
package testdata
4+
5+
import (
6+
"errors"
7+
"fmt"
8+
)
9+
10+
type customError struct{}
11+
12+
func (customError) Error() string {
13+
return "oops"
14+
}
15+
16+
func wraps() {
17+
err := errors.New("oops")
18+
fmt.Errorf("error: %w", err)
19+
fmt.Errorf("error: %v", err) // ERROR "non-wrapping format verb for fmt.Errorf. Use `%w` to format errors"
20+
fmt.Errorf("%v %v", err, err) // ERROR "non-wrapping format verb for fmt.Errorf. Use `%w` to format errors"
21+
fmt.Errorf("error: %s", err.Error()) // ERROR "non-wrapping format verb for fmt.Errorf. Use `%w` to format errors"
22+
customError := customError{}
23+
fmt.Errorf("error: %s", customError.Error()) // ERROR "non-wrapping format verb for fmt.Errorf. Use `%w` to format errors"
24+
strErr := "oops"
25+
fmt.Errorf("%v", strErr)
26+
}

0 commit comments

Comments
 (0)