1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
|
package st1012
import (
"fmt"
"go/ast"
"go/token"
"strings"
"honnef.co/go/tools/analysis/code"
"honnef.co/go/tools/analysis/lint"
"honnef.co/go/tools/analysis/report"
"golang.org/x/tools/go/analysis"
)
var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
Analyzer: &analysis.Analyzer{
Name: "ST1012",
Run: run,
},
Doc: &lint.RawDocumentation{
Title: `Poorly chosen name for error variable`,
Text: `Error variables that are part of an API should be called \'errFoo\' or
\'ErrFoo\'.`,
Since: "2019.1",
MergeIf: lint.MergeIfAny,
},
})
var Analyzer = SCAnalyzer.Analyzer
func run(pass *analysis.Pass) (interface{}, error) {
for _, f := range pass.Files {
for _, decl := range f.Decls {
gen, ok := decl.(*ast.GenDecl)
if !ok || gen.Tok != token.VAR {
continue
}
for _, spec := range gen.Specs {
spec := spec.(*ast.ValueSpec)
if len(spec.Names) != len(spec.Values) {
continue
}
for i, name := range spec.Names {
val := spec.Values[i]
if !code.IsCallToAny(pass, val, "errors.New", "fmt.Errorf") {
continue
}
if pass.Pkg.Path() == "net/http" && strings.HasPrefix(name.Name, "http2err") {
// special case for internal variable names of
// bundled HTTP 2 code in net/http
continue
}
prefix := "err"
if name.IsExported() {
prefix = "Err"
}
if !strings.HasPrefix(name.Name, prefix) {
report.Report(pass, name, fmt.Sprintf("error var %s should have name of the form %sFoo", name.Name, prefix))
}
}
}
}
}
return nil, nil
}
|