Skip to content

Commit 351e25a

Browse files
authored
Switch to go/analysis (#3)
Rework linter to use go/analysis - go's static analysis interface Move test data to for convenient use of analysistest
1 parent cff715c commit 351e25a

File tree

9 files changed

+138
-223
lines changed

9 files changed

+138
-223
lines changed

errorlint/analysis.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package errorlint
2+
3+
import (
4+
"flag"
5+
"sort"
6+
7+
"golang.org/x/tools/go/analysis"
8+
)
9+
10+
func NewAnalyzer() *analysis.Analyzer {
11+
return &analysis.Analyzer{
12+
Name: "errorlint",
13+
Doc: "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.",
14+
Run: run,
15+
Flags: flagSet,
16+
}
17+
}
18+
19+
var (
20+
flagSet flag.FlagSet
21+
checkErrorf bool
22+
)
23+
24+
func init() {
25+
flagSet.BoolVar(&checkErrorf, "errorf", false, "Check whether fmt.Errorf uses the %w verb for formatting errors. See the readme for caveats")
26+
}
27+
28+
func run(pass *analysis.Pass) (interface{}, error) {
29+
lints := []Lint{}
30+
if checkErrorf {
31+
l := LintFmtErrorfCalls(pass.Fset, *pass.TypesInfo)
32+
lints = append(lints, l...)
33+
}
34+
l := LintErrorComparisons(pass.Fset, *pass.TypesInfo)
35+
lints = append(lints, l...)
36+
l = LintErrorTypeAssertions(pass.Fset, *pass.TypesInfo)
37+
lints = append(lints, l...)
38+
sort.Sort(ByPosition(lints))
39+
40+
for _, l := range lints {
41+
pass.Report(analysis.Diagnostic{Pos: l.Pos, Message: l.Message})
42+
}
43+
return nil, nil
44+
}

errorlint/analysis_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package errorlint
2+
3+
import (
4+
"log"
5+
"testing"
6+
7+
"golang.org/x/tools/go/analysis/analysistest"
8+
)
9+
10+
func TestErrorsAs(t *testing.T) {
11+
analysistest.Run(t, analysistest.TestData(), NewAnalyzer(), "errorsas")
12+
}
13+
14+
func TestErrorsIs(t *testing.T) {
15+
analysistest.Run(t, analysistest.TestData(), NewAnalyzer(), "errorsis")
16+
}
17+
18+
func TestFmtErrorf(t *testing.T) {
19+
analyzer := NewAnalyzer()
20+
err := analyzer.Flags.Set("errorf", "true")
21+
if err != nil {
22+
log.Fatal(err)
23+
}
24+
analysistest.Run(t, analysistest.TestData(), analyzer, "fmterrorf")
25+
}

errorlint/lint.go

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111

1212
type Lint struct {
1313
Message string
14-
Pos token.Position
14+
Pos token.Pos
1515
}
1616

1717
type ByPosition []Lint
@@ -20,11 +20,7 @@ func (l ByPosition) Len() int { return len(l) }
2020
func (l ByPosition) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
2121

2222
func (l ByPosition) Less(i, j int) bool {
23-
a, b := l[i].Pos, l[j].Pos
24-
if a.Filename == b.Filename {
25-
return a.Offset < b.Offset
26-
}
27-
return a.Filename < b.Filename
23+
return l[i].Pos < l[j].Pos
2824
}
2925

3026
func LintFmtErrorfCalls(fset *token.FileSet, info types.Info) []Lint {
@@ -55,7 +51,7 @@ func LintFmtErrorfCalls(fset *token.FileSet, info types.Info) []Lint {
5551
if len(formatVerbs) >= i && formatVerbs[i] != "%w" {
5652
lints = append(lints, Lint{
5753
Message: "non-wrapping format verb for fmt.Errorf. Use `%w` to format errors",
58-
Pos: fset.Position(arg.Pos()),
54+
Pos: arg.Pos(),
5955
})
6056
}
6157
}
@@ -136,7 +132,7 @@ func LintErrorComparisons(fset *token.FileSet, info types.Info) []Lint {
136132

137133
lints = append(lints, Lint{
138134
Message: fmt.Sprintf("comparing with %s will fail on wrapped errors. Use errors.Is to check for a specific error", binExpr.Op),
139-
Pos: fset.Position(binExpr.Pos()),
135+
Pos: binExpr.Pos(),
140136
})
141137
}
142138

@@ -157,7 +153,7 @@ func LintErrorComparisons(fset *token.FileSet, info types.Info) []Lint {
157153

158154
lints = append(lints, Lint{
159155
Message: "switch on an error will fail on wrapped errors. Use errors.Is to check for specific errors",
160-
Pos: fset.Position(switchStmt.Pos()),
156+
Pos: switchStmt.Pos(),
161157
})
162158
}
163159

@@ -197,7 +193,7 @@ func LintErrorTypeAssertions(fset *token.FileSet, info types.Info) []Lint {
197193

198194
lints = append(lints, Lint{
199195
Message: "type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors",
200-
Pos: fset.Position(typeAssert.Pos()),
196+
Pos: typeAssert.Pos(),
201197
})
202198
}
203199

@@ -224,7 +220,7 @@ func LintErrorTypeAssertions(fset *token.FileSet, info types.Info) []Lint {
224220

225221
lints = append(lints, Lint{
226222
Message: "type switch on error will fail on wrapped errors. Use errors.As to check for specific errors",
227-
Pos: fset.Position(typeAssert.Pos()),
223+
Pos: typeAssert.Pos(),
228224
})
229225
}
230226

errorlint/lint_test.go

Lines changed: 0 additions & 112 deletions
This file was deleted.

testdata/errorsas.go renamed to errorlint/testdata/src/errorsas/errorsas.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package testdata
1+
package errorsas
22

33
import (
44
"errors"
@@ -25,37 +25,37 @@ func TypeCheckGood() {
2525

2626
func TypeAssertion() {
2727
err := doAnotherThing()
28-
_, ok := err.(*MyError) // bad
28+
_, ok := err.(*MyError) // want `type assertion on error will fail on wrapped errors. Use errors.As to check for specific errors`
2929
if ok {
3030
fmt.Println("MyError")
3131
}
3232
}
3333

3434
func TypeSwitch() {
3535
err := doAnotherThing()
36-
switch err.(type) { // bad
36+
switch err.(type) { // want `type switch on error will fail on wrapped errors. Use errors.As to check for specific errors`
3737
case *MyError:
3838
fmt.Println("MyError")
3939
}
4040
}
4141

4242
func TypeSwitchInline() {
43-
switch doAnotherThing().(type) { // bad
43+
switch doAnotherThing().(type) { // want `type switch on error will fail on wrapped errors. Use errors.As to check for specific errors`
4444
case *MyError:
4545
fmt.Println("MyError")
4646
}
4747
}
4848

4949
func TypeSwitchAssign() {
5050
err := doAnotherThing()
51-
switch t := err.(type) { // bad
51+
switch t := err.(type) { // want `type switch on error will fail on wrapped errors. Use errors.As to check for specific errors`
5252
case *MyError:
5353
fmt.Println("MyError", t)
5454
}
5555
}
5656

5757
func TypeSwitchAssignInline() {
58-
switch t := doAnotherThing().(type) { // bad
58+
switch t := doAnotherThing().(type) { // want `type switch on error will fail on wrapped errors. Use errors.As to check for specific errors`
5959
case *MyError:
6060
fmt.Println("MyError", t)
6161
}

testdata/errorsis.go renamed to errorlint/testdata/src/errorsis/errorsis.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package testdata
1+
package errorsis
22

33
import (
44
"errors"
@@ -48,42 +48,42 @@ func CompareOperatorNotNilYodaGood() {
4848

4949
func EqualOperator() {
5050
err := doThing()
51-
if err == ErrFoo { // bad
51+
if err == ErrFoo { // want `comparing with == will fail on wrapped errors. Use errors.Is to check for a specific error`
5252
fmt.Println("ErrFoo")
5353
}
5454
}
5555

5656
func NotEqualOperator() {
5757
err := doThing()
58-
if err != ErrFoo { // bad
58+
if err != ErrFoo { // want `comparing with != will fail on wrapped errors. Use errors.Is to check for a specific error`
5959
fmt.Println("not ErrFoo")
6060
}
6161
}
6262

6363
func EqualOperatorYoda() {
6464
err := doThing()
65-
if ErrFoo == err { // bad
65+
if ErrFoo == err { // want `comparing with == will fail on wrapped errors. Use errors.Is to check for a specific error`
6666
fmt.Println("ErrFoo")
6767
}
6868
}
6969

7070
func NotEqualOperatorYoda() {
7171
err := doThing()
72-
if ErrFoo != err { // bad
72+
if ErrFoo != err { // want `comparing with != will fail on wrapped errors. Use errors.Is to check for a specific error`
7373
fmt.Println("not ErrFoo")
7474
}
7575
}
7676

7777
func CompareSwitch() {
7878
err := doThing()
79-
switch err { // bad
79+
switch err { // want `switch on an error will fail on wrapped errors. Use errors.Is to check for specific errors`
8080
case ErrFoo:
8181
fmt.Println("ErrFoo")
8282
}
8383
}
8484

8585
func CompareSwitchInline() {
86-
switch doThing() { // bad
86+
switch doThing() { // want `switch on an error will fail on wrapped errors. Use errors.Is to check for specific errors`
8787
case ErrFoo:
8888
fmt.Println("ErrFoo")
8989
}

0 commit comments

Comments
 (0)