1
0

Update module spf13/viper to v1.7.0 (#494)

Update module spf13/viper to v1.7.0

Reviewed-on: https://kolaente.dev/vikunja/api/pulls/494
This commit is contained in:
renovate
2020-05-09 13:44:17 +00:00
committed by konrad
parent 18f6e31b54
commit def2362682
89 changed files with 9018 additions and 4502 deletions

View File

@ -0,0 +1,111 @@
package stylecheck
import (
"flag"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"honnef.co/go/tools/config"
"honnef.co/go/tools/facts"
"honnef.co/go/tools/internal/passes/buildssa"
"honnef.co/go/tools/lint/lintutil"
)
func newFlagSet() flag.FlagSet {
fs := flag.NewFlagSet("", flag.PanicOnError)
fs.Var(lintutil.NewVersionFlag(), "go", "Target Go version")
return *fs
}
var Analyzers = map[string]*analysis.Analyzer{
"ST1000": {
Name: "ST1000",
Run: CheckPackageComment,
Doc: Docs["ST1000"].String(),
Requires: []*analysis.Analyzer{},
Flags: newFlagSet(),
},
"ST1001": {
Name: "ST1001",
Run: CheckDotImports,
Doc: Docs["ST1001"].String(),
Requires: []*analysis.Analyzer{facts.Generated, config.Analyzer},
Flags: newFlagSet(),
},
"ST1003": {
Name: "ST1003",
Run: CheckNames,
Doc: Docs["ST1003"].String(),
Requires: []*analysis.Analyzer{facts.Generated, config.Analyzer},
Flags: newFlagSet(),
},
"ST1005": {
Name: "ST1005",
Run: CheckErrorStrings,
Doc: Docs["ST1005"].String(),
Requires: []*analysis.Analyzer{buildssa.Analyzer},
Flags: newFlagSet(),
},
"ST1006": {
Name: "ST1006",
Run: CheckReceiverNames,
Doc: Docs["ST1006"].String(),
Requires: []*analysis.Analyzer{buildssa.Analyzer, facts.Generated},
Flags: newFlagSet(),
},
"ST1008": {
Name: "ST1008",
Run: CheckErrorReturn,
Doc: Docs["ST1008"].String(),
Requires: []*analysis.Analyzer{buildssa.Analyzer},
Flags: newFlagSet(),
},
"ST1011": {
Name: "ST1011",
Run: CheckTimeNames,
Doc: Docs["ST1011"].String(),
Flags: newFlagSet(),
},
"ST1012": {
Name: "ST1012",
Run: CheckErrorVarNames,
Doc: Docs["ST1012"].String(),
Requires: []*analysis.Analyzer{config.Analyzer},
Flags: newFlagSet(),
},
"ST1013": {
Name: "ST1013",
Run: CheckHTTPStatusCodes,
Doc: Docs["ST1013"].String(),
Requires: []*analysis.Analyzer{facts.Generated, facts.TokenFile, config.Analyzer},
Flags: newFlagSet(),
},
"ST1015": {
Name: "ST1015",
Run: CheckDefaultCaseOrder,
Doc: Docs["ST1015"].String(),
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated, facts.TokenFile},
Flags: newFlagSet(),
},
"ST1016": {
Name: "ST1016",
Run: CheckReceiverNamesIdentical,
Doc: Docs["ST1016"].String(),
Requires: []*analysis.Analyzer{buildssa.Analyzer},
Flags: newFlagSet(),
},
"ST1017": {
Name: "ST1017",
Run: CheckYodaConditions,
Doc: Docs["ST1017"].String(),
Requires: []*analysis.Analyzer{inspect.Analyzer, facts.Generated, facts.TokenFile},
Flags: newFlagSet(),
},
"ST1018": {
Name: "ST1018",
Run: CheckInvisibleCharacters,
Doc: Docs["ST1018"].String(),
Requires: []*analysis.Analyzer{inspect.Analyzer},
Flags: newFlagSet(),
},
}

View File

@ -1,18 +1,20 @@
package stylecheck
var docST1000 = `Incorrect or missing package comment
import "honnef.co/go/tools/lint"
Packages must have a package comment that is formatted according to
var Docs = map[string]*lint.Documentation{
"ST1000": &lint.Documentation{
Title: `Incorrect or missing package comment`,
Text: `Packages must have a package comment that is formatted according to
the guidelines laid out in
https://github.com/golang/go/wiki/CodeReviewComments#package-comments.
https://github.com/golang/go/wiki/CodeReviewComments#package-comments.`,
Since: "2019.1",
NonDefault: true,
},
Available since
2019.1, non-default
`
var docST1001 = `Dot imports are discouraged
Dot imports that aren't in external test packages are discouraged.
"ST1001": &lint.Documentation{
Title: `Dot imports are discouraged`,
Text: `Dot imports that aren't in external test packages are discouraged.
The dot_import_whitelist option can be used to whitelist certain
imports.
@ -35,36 +37,29 @@ Quoting Go Code Review Comments:
it is not. Except for this one case, do not use import . in your
programs. It makes the programs much harder to read because it is
unclear whether a name like Quux is a top-level identifier in the
current package or in an imported package.
current package or in an imported package.`,
Since: "2019.1",
Options: []string{"dot_import_whitelist"},
},
Available since
2019.1
Options
dot_import_whitelist
`
var docST1003 = `Poorly chosen identifier
Identifiers, such as variable and package names, follow certain rules.
"ST1003": &lint.Documentation{
Title: `Poorly chosen identifier`,
Text: `Identifiers, such as variable and package names, follow certain rules.
See the following links for details:
http://golang.org/doc/effective_go.html#package-names
http://golang.org/doc/effective_go.html#mixed-caps
https://github.com/golang/go/wiki/CodeReviewComments#initialisms
https://github.com/golang/go/wiki/CodeReviewComments#variable-names
- https://golang.org/doc/effective_go.html#package-names
- https://golang.org/doc/effective_go.html#mixed-caps
- https://github.com/golang/go/wiki/CodeReviewComments#initialisms
- https://github.com/golang/go/wiki/CodeReviewComments#variable-names`,
Since: "2019.1",
NonDefault: true,
Options: []string{"initialisms"},
},
Available since
2019.1, non-default
Options
initialisms
`
var docST1005 = `Incorrectly formatted error string
Error strings follow a set of guidelines to ensure uniformity and good
"ST1005": &lint.Documentation{
Title: `Incorrectly formatted error string`,
Text: `Error strings follow a set of guidelines to ensure uniformity and good
composability.
Quoting Go Code Review Comments:
@ -74,15 +69,13 @@ Quoting Go Code Review Comments:
usually printed following other context. That is, use
fmt.Errorf("something bad") not fmt.Errorf("Something bad"), so
that log.Printf("Reading %s: %v", filename, err) formats without a
spurious capital letter mid-message.
spurious capital letter mid-message.`,
Since: "2019.1",
},
Available since
2019.1
`
var docST1006 = `Poorly chosen receiver name
Quoting Go Code Review Comments:
"ST1006": &lint.Documentation{
Title: `Poorly chosen receiver name`,
Text: `Quoting Go Code Review Comments:
The name of a method's receiver should be a reflection of its
identity; often a one or two letter abbreviation of its type
@ -94,77 +87,68 @@ Quoting Go Code Review Comments:
documentary purpose. It can be very short as it will appear on
almost every line of every method of the type; familiarity admits
brevity. Be consistent, too: if you call the receiver "c" in one
method, don't call it "cl" in another.
method, don't call it "cl" in another.`,
Since: "2019.1",
},
Available since
2019.1
`
"ST1008": &lint.Documentation{
Title: `A function's error value should be its last return value`,
Text: `A function's error value should be its last return value.`,
Since: `2019.1`,
},
var docST1008 = `A function's error value should be its last return value
A function's error value should be its last return value.
Available since
2019.1
`
var docST1011 = `Poorly chosen name for variable of type time.Duration
time.Duration values represent an amount of time, which is represented
"ST1011": &lint.Documentation{
Title: `Poorly chosen name for variable of type time.Duration`,
Text: `time.Duration values represent an amount of time, which is represented
as a count of nanoseconds. An expression like 5 * time.Microsecond
yields the value 5000. It is therefore not appropriate to suffix a
variable of type time.Duration with any time unit, such as Msec or
Milli.
Milli.`,
Since: `2019.1`,
},
Available since
2019.1
`
"ST1012": &lint.Documentation{
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",
},
var docST1012 = `Poorly chosen name for error variable
Error variables that are part of an API should be called errFoo or
ErrFoo.
Available since
2019.1
`
var docST1013 = `Should use constants for HTTP error codes, not magic numbers
HTTP has a tremendous number of status codes. While some of those are
"ST1013": &lint.Documentation{
Title: `Should use constants for HTTP error codes, not magic numbers`,
Text: `HTTP has a tremendous number of status codes. While some of those are
well known (200, 400, 404, 500), most of them are not. The net/http
package provides constants for all status codes that are part of the
various specifications. It is recommended to use these constants
instead of hard-coding magic numbers, to vastly improve the
readability of your code.
readability of your code.`,
Since: "2019.1",
Options: []string{"http_status_code_whitelist"},
},
Available since
2019.1
"ST1015": &lint.Documentation{
Title: `A switch's default case should be the first or last case`,
Since: "2019.1",
},
Options
http_status_code_whitelist
`
"ST1016": &lint.Documentation{
Title: `Use consistent method receiver names`,
Since: "2019.1",
NonDefault: true,
},
var docST1015 = `A switch's default case should be the first or last case
"ST1017": &lint.Documentation{
Title: `Don't use Yoda conditions`,
Text: `Yoda conditions are conditions of the kind 'if 42 == x', where the
literal is on the left side of the comparison. These are a common
idiom in languages in which assignment is an expression, to avoid bugs
of the kind 'if (x = 42)'. In Go, which doesn't allow for this kind of
bug, we prefer the more idiomatic 'if x == 42'.`,
Since: "2019.2",
},
Available since
2019.1
`
var docST1016 = `Use consistent method receiver names
Available since
2019.1, non-default
`
var docST1017 = `Don't use Yoda conditions
Available since
Unreleased
`
var docST1018 = `Avoid zero-width and control characters in string literals
Available since
Unreleased
`
"ST1018": &lint.Documentation{
Title: `Avoid zero-width and control characters in string literals`,
Since: "2019.2",
},
}

View File

@ -11,49 +11,18 @@ import (
"unicode"
"unicode/utf8"
"honnef.co/go/tools/lint"
"honnef.co/go/tools/config"
"honnef.co/go/tools/internal/passes/buildssa"
. "honnef.co/go/tools/lint/lintdsl"
"honnef.co/go/tools/ssa"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/go/types/typeutil"
)
type Checker struct {
CheckGenerated bool
}
func NewChecker() *Checker {
return &Checker{}
}
func (*Checker) Name() string { return "stylecheck" }
func (*Checker) Prefix() string { return "ST" }
func (c *Checker) Init(prog *lint.Program) {}
func (c *Checker) Checks() []lint.Check {
return []lint.Check{
{ID: "ST1000", FilterGenerated: false, Fn: c.CheckPackageComment, Doc: docST1000},
{ID: "ST1001", FilterGenerated: true, Fn: c.CheckDotImports, Doc: docST1001},
// {ID: "ST1002", FilterGenerated: true, Fn: c.CheckBlankImports, Doc: docST1002},
{ID: "ST1003", FilterGenerated: true, Fn: c.CheckNames, Doc: docST1003},
// {ID: "ST1004", FilterGenerated: false, Fn: nil, , Doc: docST1004},
{ID: "ST1005", FilterGenerated: false, Fn: c.CheckErrorStrings, Doc: docST1005},
{ID: "ST1006", FilterGenerated: false, Fn: c.CheckReceiverNames, Doc: docST1006},
// {ID: "ST1007", FilterGenerated: true, Fn: c.CheckIncDec, Doc: docST1007},
{ID: "ST1008", FilterGenerated: false, Fn: c.CheckErrorReturn, Doc: docST1008},
// {ID: "ST1009", FilterGenerated: false, Fn: c.CheckUnexportedReturn, Doc: docST1009},
// {ID: "ST1010", FilterGenerated: false, Fn: c.CheckContextFirstArg, Doc: docST1010},
{ID: "ST1011", FilterGenerated: false, Fn: c.CheckTimeNames, Doc: docST1011},
{ID: "ST1012", FilterGenerated: false, Fn: c.CheckErrorVarNames, Doc: docST1012},
{ID: "ST1013", FilterGenerated: true, Fn: c.CheckHTTPStatusCodes, Doc: docST1013},
{ID: "ST1015", FilterGenerated: true, Fn: c.CheckDefaultCaseOrder, Doc: docST1015},
{ID: "ST1016", FilterGenerated: false, Fn: c.CheckReceiverNamesIdentical, Doc: docST1016},
{ID: "ST1017", FilterGenerated: true, Fn: c.CheckYodaConditions, Doc: docST1017},
{ID: "ST1018", FilterGenerated: false, Fn: c.CheckInvisibleCharacters, Doc: docST1018},
}
}
func (c *Checker) CheckPackageComment(j *lint.Job) {
func CheckPackageComment(pass *analysis.Pass) (interface{}, error) {
// - At least one file in a non-main package should have a package comment
//
// - The comment should be of the form
@ -62,57 +31,59 @@ func (c *Checker) CheckPackageComment(j *lint.Job) {
// which case they get appended. But that doesn't happen a lot in
// the real world.
if j.Pkg.Name == "main" {
return
if pass.Pkg.Name() == "main" {
return nil, nil
}
hasDocs := false
for _, f := range j.Pkg.Syntax {
if IsInTest(j, f) {
for _, f := range pass.Files {
if IsInTest(pass, f) {
continue
}
if f.Doc != nil && len(f.Doc.List) > 0 {
hasDocs = true
prefix := "Package " + f.Name.Name + " "
if !strings.HasPrefix(strings.TrimSpace(f.Doc.Text()), prefix) {
j.Errorf(f.Doc, `package comment should be of the form "%s..."`, prefix)
ReportNodef(pass, f.Doc, `package comment should be of the form "%s..."`, prefix)
}
f.Doc.Text()
}
}
if !hasDocs {
for _, f := range j.Pkg.Syntax {
if IsInTest(j, f) {
for _, f := range pass.Files {
if IsInTest(pass, f) {
continue
}
j.Errorf(f, "at least one file in a package should have a package comment")
ReportNodef(pass, f, "at least one file in a package should have a package comment")
}
}
return nil, nil
}
func (c *Checker) CheckDotImports(j *lint.Job) {
for _, f := range j.Pkg.Syntax {
func CheckDotImports(pass *analysis.Pass) (interface{}, error) {
for _, f := range pass.Files {
imports:
for _, imp := range f.Imports {
path := imp.Path.Value
path = path[1 : len(path)-1]
for _, w := range j.Pkg.Config.DotImportWhitelist {
for _, w := range config.For(pass).DotImportWhitelist {
if w == path {
continue imports
}
}
if imp.Name != nil && imp.Name.Name == "." && !IsInTest(j, f) {
j.Errorf(imp, "should not use dot imports")
if imp.Name != nil && imp.Name.Name == "." && !IsInTest(pass, f) {
ReportNodefFG(pass, imp, "should not use dot imports")
}
}
}
return nil, nil
}
func (c *Checker) CheckBlankImports(j *lint.Job) {
fset := j.Pkg.Fset
for _, f := range j.Pkg.Syntax {
if IsInMain(j, f) || IsInTest(j, f) {
func CheckBlankImports(pass *analysis.Pass) (interface{}, error) {
fset := pass.Fset
for _, f := range pass.Files {
if IsInMain(pass, f) || IsInTest(pass, f) {
continue
}
@ -161,13 +132,14 @@ func (c *Checker) CheckBlankImports(j *lint.Job) {
}
if imp.Doc == nil && imp.Comment == nil && !skip[imp] {
j.Errorf(imp, "a blank import should be only in a main or test package, or have a comment justifying it")
ReportNodef(pass, imp, "a blank import should be only in a main or test package, or have a comment justifying it")
}
}
}
return nil, nil
}
func (c *Checker) CheckIncDec(j *lint.Job) {
func CheckIncDec(pass *analysis.Pass) (interface{}, error) {
// TODO(dh): this can be noisy for function bodies that look like this:
// x += 3
// ...
@ -192,14 +164,15 @@ func (c *Checker) CheckIncDec(j *lint.Job) {
suffix = "--"
}
j.Errorf(assign, "should replace %s with %s%s", Render(j, assign), Render(j, assign.Lhs[0]), suffix)
ReportNodef(pass, assign, "should replace %s with %s%s", Render(pass, assign), Render(pass, assign.Lhs[0]), suffix)
}
j.Pkg.Inspector.Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn)
pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn)
return nil, nil
}
func (c *Checker) CheckErrorReturn(j *lint.Job) {
func CheckErrorReturn(pass *analysis.Pass) (interface{}, error) {
fnLoop:
for _, fn := range j.Pkg.InitialFunctions {
for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
sig := fn.Type().(*types.Signature)
rets := sig.Results()
if rets == nil || rets.Len() < 2 {
@ -213,21 +186,22 @@ fnLoop:
}
for i := rets.Len() - 2; i >= 0; i-- {
if rets.At(i).Type() == types.Universe.Lookup("error").Type() {
j.Errorf(rets.At(i), "error should be returned as the last argument")
pass.Reportf(rets.At(i).Pos(), "error should be returned as the last argument")
continue fnLoop
}
}
}
return nil, nil
}
// CheckUnexportedReturn checks that exported functions on exported
// types do not return unexported types.
func (c *Checker) CheckUnexportedReturn(j *lint.Job) {
for _, fn := range j.Pkg.InitialFunctions {
func CheckUnexportedReturn(pass *analysis.Pass) (interface{}, error) {
for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
if fn.Synthetic != "" || fn.Parent() != nil {
continue
}
if !ast.IsExported(fn.Name()) || IsInMain(j, fn) || IsInTest(j, fn) {
if !ast.IsExported(fn.Name()) || IsInMain(pass, fn) || IsInTest(pass, fn) {
continue
}
sig := fn.Type().(*types.Signature)
@ -239,14 +213,16 @@ func (c *Checker) CheckUnexportedReturn(j *lint.Job) {
if named, ok := DereferenceR(res.At(i).Type()).(*types.Named); ok &&
!ast.IsExported(named.Obj().Name()) &&
named != types.Universe.Lookup("error").Type() {
j.Errorf(fn, "should not return unexported type")
pass.Reportf(fn.Pos(), "should not return unexported type")
}
}
}
return nil, nil
}
func (c *Checker) CheckReceiverNames(j *lint.Job) {
for _, m := range j.Pkg.SSA.Members {
func CheckReceiverNames(pass *analysis.Pass) (interface{}, error) {
ssapkg := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).Pkg
for _, m := range ssapkg.Members {
if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() {
ms := typeutil.IntuitiveMethodSet(T.Type(), nil)
for _, sel := range ms {
@ -257,18 +233,20 @@ func (c *Checker) CheckReceiverNames(j *lint.Job) {
continue
}
if recv.Name() == "self" || recv.Name() == "this" {
j.Errorf(recv, `receiver name should be a reflection of its identity; don't use generic names such as "this" or "self"`)
ReportfFG(pass, recv.Pos(), `receiver name should be a reflection of its identity; don't use generic names such as "this" or "self"`)
}
if recv.Name() == "_" {
j.Errorf(recv, "receiver name should not be an underscore, omit the name if it is unused")
ReportfFG(pass, recv.Pos(), "receiver name should not be an underscore, omit the name if it is unused")
}
}
}
}
return nil, nil
}
func (c *Checker) CheckReceiverNamesIdentical(j *lint.Job) {
for _, m := range j.Pkg.SSA.Members {
func CheckReceiverNamesIdentical(pass *analysis.Pass) (interface{}, error) {
ssapkg := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).Pkg
for _, m := range ssapkg.Members {
names := map[string]int{}
var firstFn *types.Func
@ -296,16 +274,17 @@ func (c *Checker) CheckReceiverNamesIdentical(j *lint.Job) {
seen = append(seen, fmt.Sprintf("%dx %q", count, name))
}
j.Errorf(firstFn, "methods on the same type should have the same receiver name (seen %s)", strings.Join(seen, ", "))
pass.Reportf(firstFn.Pos(), "methods on the same type should have the same receiver name (seen %s)", strings.Join(seen, ", "))
}
}
return nil, nil
}
func (c *Checker) CheckContextFirstArg(j *lint.Job) {
func CheckContextFirstArg(pass *analysis.Pass) (interface{}, error) {
// TODO(dh): this check doesn't apply to test helpers. Example from the stdlib:
// func helperCommandContext(t *testing.T, ctx context.Context, s ...string) (cmd *exec.Cmd) {
fnLoop:
for _, fn := range j.Pkg.InitialFunctions {
for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
if fn.Synthetic != "" || fn.Parent() != nil {
continue
}
@ -319,28 +298,29 @@ fnLoop:
for i := 1; i < params.Len(); i++ {
param := params.At(i)
if types.TypeString(param.Type(), nil) == "context.Context" {
j.Errorf(param, "context.Context should be the first argument of a function")
pass.Reportf(param.Pos(), "context.Context should be the first argument of a function")
continue fnLoop
}
}
}
return nil, nil
}
func (c *Checker) CheckErrorStrings(j *lint.Job) {
func CheckErrorStrings(pass *analysis.Pass) (interface{}, error) {
objNames := map[*ssa.Package]map[string]bool{}
ssapkg := j.Pkg.SSA
ssapkg := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).Pkg
objNames[ssapkg] = map[string]bool{}
for _, m := range ssapkg.Members {
if typ, ok := m.(*ssa.Type); ok {
objNames[ssapkg][typ.Name()] = true
}
}
for _, fn := range j.Pkg.InitialFunctions {
for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
objNames[fn.Package()][fn.Name()] = true
}
for _, fn := range j.Pkg.InitialFunctions {
if IsInTest(j, fn) {
for _, fn := range pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs {
if IsInTest(pass, fn) {
// We don't care about malformed error messages in tests;
// they're usually for direct human consumption, not part
// of an API
@ -368,7 +348,7 @@ func (c *Checker) CheckErrorStrings(j *lint.Job) {
}
switch s[len(s)-1] {
case '.', ':', '!', '\n':
j.Errorf(call, "error strings should not end with punctuation or a newline")
pass.Reportf(call.Pos(), "error strings should not end with punctuation or a newline")
}
idx := strings.IndexByte(s, ' ')
if idx == -1 {
@ -402,13 +382,14 @@ func (c *Checker) CheckErrorStrings(j *lint.Job) {
//
// It could still be a proper noun, though.
j.Errorf(call, "error strings should not be capitalized")
pass.Reportf(call.Pos(), "error strings should not be capitalized")
}
}
}
return nil, nil
}
func (c *Checker) CheckTimeNames(j *lint.Job) {
func CheckTimeNames(pass *analysis.Pass) (interface{}, error) {
suffixes := []string{
"Sec", "Secs", "Seconds",
"Msec", "Msecs",
@ -423,31 +404,32 @@ func (c *Checker) CheckTimeNames(j *lint.Job) {
for _, name := range names {
for _, suffix := range suffixes {
if strings.HasSuffix(name.Name, suffix) {
j.Errorf(name, "var %s is of type %v; don't use unit-specific suffix %q", name.Name, T, suffix)
ReportNodef(pass, name, "var %s is of type %v; don't use unit-specific suffix %q", name.Name, T, suffix)
break
}
}
}
}
for _, f := range j.Pkg.Syntax {
for _, f := range pass.Files {
ast.Inspect(f, func(node ast.Node) bool {
switch node := node.(type) {
case *ast.ValueSpec:
T := j.Pkg.TypesInfo.TypeOf(node.Type)
T := pass.TypesInfo.TypeOf(node.Type)
fn(T, node.Names)
case *ast.FieldList:
for _, field := range node.List {
T := j.Pkg.TypesInfo.TypeOf(field.Type)
T := pass.TypesInfo.TypeOf(field.Type)
fn(T, field.Names)
}
}
return true
})
}
return nil, nil
}
func (c *Checker) CheckErrorVarNames(j *lint.Job) {
for _, f := range j.Pkg.Syntax {
func CheckErrorVarNames(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 {
@ -461,7 +443,7 @@ func (c *Checker) CheckErrorVarNames(j *lint.Job) {
for i, name := range spec.Names {
val := spec.Values[i]
if !IsCallToAST(j, val, "errors.New") && !IsCallToAST(j, val, "fmt.Errorf") {
if !IsCallToAST(pass, val, "errors.New") && !IsCallToAST(pass, val, "fmt.Errorf") {
continue
}
@ -470,12 +452,13 @@ func (c *Checker) CheckErrorVarNames(j *lint.Job) {
prefix = "Err"
}
if !strings.HasPrefix(name.Name, prefix) {
j.Errorf(name, "error var %s should have name of the form %sFoo", name.Name, prefix)
ReportNodef(pass, name, "error var %s should have name of the form %sFoo", name.Name, prefix)
}
}
}
}
}
return nil, nil
}
var httpStatusCodes = map[int]string{
@ -540,19 +523,22 @@ var httpStatusCodes = map[int]string{
511: "StatusNetworkAuthenticationRequired",
}
func (c *Checker) CheckHTTPStatusCodes(j *lint.Job) {
func CheckHTTPStatusCodes(pass *analysis.Pass) (interface{}, error) {
whitelist := map[string]bool{}
for _, code := range j.Pkg.Config.HTTPStatusCodeWhitelist {
for _, code := range config.For(pass).HTTPStatusCodeWhitelist {
whitelist[code] = true
}
fn := func(node ast.Node) bool {
if node == nil {
return true
}
call, ok := node.(*ast.CallExpr)
if !ok {
return true
}
var arg int
switch CallNameAST(j, call) {
switch CallNameAST(pass, call) {
case "net/http.Error":
arg = 2
case "net/http.Redirect":
@ -580,29 +566,32 @@ func (c *Checker) CheckHTTPStatusCodes(j *lint.Job) {
if !ok {
return true
}
j.Errorf(lit, "should use constant http.%s instead of numeric literal %d", s, n)
ReportNodefFG(pass, lit, "should use constant http.%s instead of numeric literal %d", s, n)
return true
}
for _, f := range j.Pkg.Syntax {
// OPT(dh): replace with inspector
for _, f := range pass.Files {
ast.Inspect(f, fn)
}
return nil, nil
}
func (c *Checker) CheckDefaultCaseOrder(j *lint.Job) {
func CheckDefaultCaseOrder(pass *analysis.Pass) (interface{}, error) {
fn := func(node ast.Node) {
stmt := node.(*ast.SwitchStmt)
list := stmt.Body.List
for i, c := range list {
if c.(*ast.CaseClause).List == nil && i != 0 && i != len(list)-1 {
j.Errorf(c, "default case should be first or last in switch statement")
ReportNodefFG(pass, c, "default case should be first or last in switch statement")
break
}
}
}
j.Pkg.Inspector.Preorder([]ast.Node{(*ast.SwitchStmt)(nil)}, fn)
pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.SwitchStmt)(nil)}, fn)
return nil, nil
}
func (c *Checker) CheckYodaConditions(j *lint.Job) {
func CheckYodaConditions(pass *analysis.Pass) (interface{}, error) {
fn := func(node ast.Node) {
cond := node.(*ast.BinaryExpr)
if cond.Op != token.EQL && cond.Op != token.NEQ {
@ -615,12 +604,13 @@ func (c *Checker) CheckYodaConditions(j *lint.Job) {
// Don't flag lit == lit conditions, just in case
return
}
j.Errorf(cond, "don't use Yoda conditions")
ReportNodefFG(pass, cond, "don't use Yoda conditions")
}
j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn)
pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn)
return nil, nil
}
func (c *Checker) CheckInvisibleCharacters(j *lint.Job) {
func CheckInvisibleCharacters(pass *analysis.Pass) (interface{}, error) {
fn := func(node ast.Node) {
lit := node.(*ast.BasicLit)
if lit.Kind != token.STRING {
@ -628,11 +618,12 @@ func (c *Checker) CheckInvisibleCharacters(j *lint.Job) {
}
for _, r := range lit.Value {
if unicode.Is(unicode.Cf, r) {
j.Errorf(lit, "string literal contains the Unicode format character %U, consider using the %q escape sequence", r, r)
ReportNodef(pass, lit, "string literal contains the Unicode format character %U, consider using the %q escape sequence", r, r)
} else if unicode.Is(unicode.Cc, r) && r != '\n' && r != '\t' && r != '\r' {
j.Errorf(lit, "string literal contains the Unicode control character %U, consider using the %q escape sequence", r, r)
ReportNodef(pass, lit, "string literal contains the Unicode control character %U, consider using the %q escape sequence", r, r)
}
}
}
j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BasicLit)(nil)}, fn)
pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Preorder([]ast.Node{(*ast.BasicLit)(nil)}, fn)
return nil, nil
}

View File

@ -9,7 +9,8 @@ import (
"strings"
"unicode"
"honnef.co/go/tools/lint"
"golang.org/x/tools/go/analysis"
"honnef.co/go/tools/config"
. "honnef.co/go/tools/lint/lintdsl"
)
@ -21,7 +22,7 @@ var knownNameExceptions = map[string]bool{
"kWh": true,
}
func (c *Checker) CheckNames(j *lint.Job) {
func CheckNames(pass *analysis.Pass) (interface{}, error) {
// A large part of this function is copied from
// github.com/golang/lint, Copyright (c) 2013 The Go Authors,
// licensed under the BSD 3-clause license.
@ -45,7 +46,7 @@ func (c *Checker) CheckNames(j *lint.Job) {
// Handle two common styles from other languages that don't belong in Go.
if len(id.Name) >= 5 && allCaps(id.Name) && strings.Contains(id.Name, "_") {
j.Errorf(id, "should not use ALL_CAPS in Go names; use CamelCase instead")
ReportfFG(pass, id.Pos(), "should not use ALL_CAPS in Go names; use CamelCase instead")
return
}
@ -55,10 +56,10 @@ func (c *Checker) CheckNames(j *lint.Job) {
}
if len(id.Name) > 2 && strings.Contains(id.Name[1:len(id.Name)-1], "_") {
j.Errorf(id, "should not use underscores in Go names; %s %s should be %s", thing, id.Name, should)
ReportfFG(pass, id.Pos(), "should not use underscores in Go names; %s %s should be %s", thing, id.Name, should)
return
}
j.Errorf(id, "%s %s should be %s", thing, id.Name, should)
ReportfFG(pass, id.Pos(), "%s %s should be %s", thing, id.Name, should)
}
checkList := func(fl *ast.FieldList, thing string, initialisms map[string]bool) {
if fl == nil {
@ -71,17 +72,18 @@ func (c *Checker) CheckNames(j *lint.Job) {
}
}
initialisms := make(map[string]bool, len(j.Pkg.Config.Initialisms))
for _, word := range j.Pkg.Config.Initialisms {
il := config.For(pass).Initialisms
initialisms := make(map[string]bool, len(il))
for _, word := range il {
initialisms[word] = true
}
for _, f := range j.Pkg.Syntax {
for _, f := range pass.Files {
// Package names need slightly different handling than other names.
if !strings.HasSuffix(f.Name.Name, "_test") && strings.Contains(f.Name.Name, "_") {
j.Errorf(f, "should not use underscores in package names")
ReportfFG(pass, f.Pos(), "should not use underscores in package names")
}
if strings.IndexFunc(f.Name.Name, unicode.IsUpper) != -1 {
j.Errorf(f, "should not use MixedCaps in package name; %s should be %s", f.Name.Name, strings.ToLower(f.Name.Name))
ReportfFG(pass, f.Pos(), "should not use MixedCaps in package name; %s should be %s", f.Name.Name, strings.ToLower(f.Name.Name))
}
ast.Inspect(f, func(node ast.Node) bool {
@ -104,7 +106,7 @@ func (c *Checker) CheckNames(j *lint.Job) {
return true
}
if IsInTest(j, v) && (strings.HasPrefix(v.Name.Name, "Example") || strings.HasPrefix(v.Name.Name, "Test") || strings.HasPrefix(v.Name.Name, "Benchmark")) {
if IsInTest(pass, v) && (strings.HasPrefix(v.Name.Name, "Example") || strings.HasPrefix(v.Name.Name, "Test") || strings.HasPrefix(v.Name.Name, "Benchmark")) {
return true
}
@ -173,6 +175,7 @@ func (c *Checker) CheckNames(j *lint.Job) {
return true
})
}
return nil, nil
}
// lintName returns a different name if it should be different.