Updated staticcheck
This commit is contained in:
170
vendor/honnef.co/go/tools/stylecheck/doc.go
vendored
Normal file
170
vendor/honnef.co/go/tools/stylecheck/doc.go
vendored
Normal file
@ -0,0 +1,170 @@
|
||||
package stylecheck
|
||||
|
||||
var docST1000 = `Incorrect or missing package comment
|
||||
|
||||
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.
|
||||
|
||||
Available since
|
||||
2019.1, non-default
|
||||
`
|
||||
|
||||
var docST1001 = `Dot imports are discouraged
|
||||
|
||||
Dot imports that aren't in external test packages are discouraged.
|
||||
|
||||
The dot_import_whitelist option can be used to whitelist certain
|
||||
imports.
|
||||
|
||||
Quoting Go Code Review Comments:
|
||||
|
||||
The import . form can be useful in tests that, due to circular
|
||||
dependencies, cannot be made part of the package being tested:
|
||||
|
||||
package foo_test
|
||||
|
||||
import (
|
||||
"bar/testutil" // also imports "foo"
|
||||
. "foo"
|
||||
)
|
||||
|
||||
In this case, the test file cannot be in package foo because it
|
||||
uses bar/testutil, which imports foo. So we use the 'import .'
|
||||
form to let the file pretend to be part of package foo even though
|
||||
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.
|
||||
|
||||
Available since
|
||||
2019.1
|
||||
|
||||
Options
|
||||
dot_import_whitelist
|
||||
`
|
||||
|
||||
var docST1003 = `Poorly chosen identifier
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
composability.
|
||||
|
||||
Quoting Go Code Review Comments:
|
||||
|
||||
Error strings should not be capitalized (unless beginning with
|
||||
proper nouns or acronyms) or end with punctuation, since they are
|
||||
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.
|
||||
|
||||
Available since
|
||||
2019.1
|
||||
`
|
||||
|
||||
var docST1006 = `Poorly chosen receiver name
|
||||
|
||||
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
|
||||
suffices (such as "c" or "cl" for "Client"). Don't use generic
|
||||
names such as "me", "this" or "self", identifiers typical of
|
||||
object-oriented languages that place more emphasis on methods as
|
||||
opposed to functions. The name need not be as descriptive as that
|
||||
of a method argument, as its role is obvious and serves no
|
||||
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.
|
||||
|
||||
Available 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
|
||||
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.
|
||||
|
||||
Available 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
|
||||
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.
|
||||
|
||||
Available since
|
||||
2019.1
|
||||
|
||||
Options
|
||||
http_status_code_whitelist
|
||||
`
|
||||
|
||||
var docST1015 = `A switch's default case should be the first or last case
|
||||
|
||||
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
|
||||
`
|
367
vendor/honnef.co/go/tools/stylecheck/lint.go
vendored
367
vendor/honnef.co/go/tools/stylecheck/lint.go
vendored
@ -32,23 +32,24 @@ func (c *Checker) Init(prog *lint.Program) {}
|
||||
|
||||
func (c *Checker) Checks() []lint.Check {
|
||||
return []lint.Check{
|
||||
{ID: "ST1000", FilterGenerated: false, Fn: c.CheckPackageComment},
|
||||
{ID: "ST1001", FilterGenerated: true, Fn: c.CheckDotImports},
|
||||
// {ID: "ST1002", FilterGenerated: true, Fn: c.CheckBlankImports},
|
||||
{ID: "ST1003", FilterGenerated: true, Fn: c.CheckNames},
|
||||
// {ID: "ST1004", FilterGenerated: false, Fn: nil, },
|
||||
{ID: "ST1005", FilterGenerated: false, Fn: c.CheckErrorStrings},
|
||||
{ID: "ST1006", FilterGenerated: false, Fn: c.CheckReceiverNames},
|
||||
// {ID: "ST1007", FilterGenerated: true, Fn: c.CheckIncDec},
|
||||
{ID: "ST1008", FilterGenerated: false, Fn: c.CheckErrorReturn},
|
||||
// {ID: "ST1009", FilterGenerated: false, Fn: c.CheckUnexportedReturn},
|
||||
// {ID: "ST1010", FilterGenerated: false, Fn: c.CheckContextFirstArg},
|
||||
{ID: "ST1011", FilterGenerated: false, Fn: c.CheckTimeNames},
|
||||
{ID: "ST1012", FilterGenerated: false, Fn: c.CheckErrorVarNames},
|
||||
{ID: "ST1013", FilterGenerated: true, Fn: c.CheckHTTPStatusCodes},
|
||||
{ID: "ST1015", FilterGenerated: true, Fn: c.CheckDefaultCaseOrder},
|
||||
{ID: "ST1016", FilterGenerated: false, Fn: c.CheckReceiverNamesIdentical},
|
||||
{ID: "ST1017", FilterGenerated: true, Fn: c.CheckYodaConditions},
|
||||
{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},
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,60 +62,56 @@ func (c *Checker) CheckPackageComment(j *lint.Job) {
|
||||
// which case they get appended. But that doesn't happen a lot in
|
||||
// the real world.
|
||||
|
||||
for _, pkg := range j.Program.InitialPackages {
|
||||
if pkg.Name == "main" {
|
||||
if j.Pkg.Name == "main" {
|
||||
return
|
||||
}
|
||||
hasDocs := false
|
||||
for _, f := range j.Pkg.Syntax {
|
||||
if IsInTest(j, f) {
|
||||
continue
|
||||
}
|
||||
hasDocs := false
|
||||
for _, f := range pkg.Syntax {
|
||||
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)
|
||||
}
|
||||
f.Doc.Text()
|
||||
}
|
||||
}
|
||||
|
||||
if !hasDocs {
|
||||
for _, f := range j.Pkg.Syntax {
|
||||
if IsInTest(j, 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)
|
||||
}
|
||||
f.Doc.Text()
|
||||
}
|
||||
}
|
||||
|
||||
if !hasDocs {
|
||||
for _, f := range pkg.Syntax {
|
||||
if IsInTest(j, f) {
|
||||
continue
|
||||
}
|
||||
j.Errorf(f, "at least one file in a package should have a package comment")
|
||||
}
|
||||
j.Errorf(f, "at least one file in a package should have a package comment")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Checker) CheckDotImports(j *lint.Job) {
|
||||
for _, pkg := range j.Program.InitialPackages {
|
||||
for _, f := range pkg.Syntax {
|
||||
imports:
|
||||
for _, imp := range f.Imports {
|
||||
path := imp.Path.Value
|
||||
path = path[1 : len(path)-1]
|
||||
for _, w := range pkg.Config.DotImportWhitelist {
|
||||
if w == path {
|
||||
continue imports
|
||||
}
|
||||
for _, f := range j.Pkg.Syntax {
|
||||
imports:
|
||||
for _, imp := range f.Imports {
|
||||
path := imp.Path.Value
|
||||
path = path[1 : len(path)-1]
|
||||
for _, w := range j.Pkg.Config.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(j, f) {
|
||||
j.Errorf(imp, "should not use dot imports")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Checker) CheckBlankImports(j *lint.Job) {
|
||||
fset := j.Program.Fset()
|
||||
for _, f := range j.Program.Files {
|
||||
fset := j.Pkg.Fset
|
||||
for _, f := range j.Pkg.Syntax {
|
||||
if IsInMain(j, f) || IsInTest(j, f) {
|
||||
continue
|
||||
}
|
||||
@ -177,14 +174,14 @@ func (c *Checker) CheckIncDec(j *lint.Job) {
|
||||
// x += 2
|
||||
// ...
|
||||
// x += 1
|
||||
fn := func(node ast.Node) bool {
|
||||
assign, ok := node.(*ast.AssignStmt)
|
||||
if !ok || (assign.Tok != token.ADD_ASSIGN && assign.Tok != token.SUB_ASSIGN) {
|
||||
return true
|
||||
fn := func(node ast.Node) {
|
||||
assign := node.(*ast.AssignStmt)
|
||||
if assign.Tok != token.ADD_ASSIGN && assign.Tok != token.SUB_ASSIGN {
|
||||
return
|
||||
}
|
||||
if (len(assign.Lhs) != 1 || len(assign.Rhs) != 1) ||
|
||||
!IsIntLiteral(assign.Rhs[0], "1") {
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
||||
suffix := ""
|
||||
@ -196,16 +193,13 @@ func (c *Checker) CheckIncDec(j *lint.Job) {
|
||||
}
|
||||
|
||||
j.Errorf(assign, "should replace %s with %s%s", Render(j, assign), Render(j, assign.Lhs[0]), suffix)
|
||||
return true
|
||||
}
|
||||
for _, f := range j.Program.Files {
|
||||
ast.Inspect(f, fn)
|
||||
}
|
||||
j.Pkg.Inspector.Preorder([]ast.Node{(*ast.AssignStmt)(nil)}, fn)
|
||||
}
|
||||
|
||||
func (c *Checker) CheckErrorReturn(j *lint.Job) {
|
||||
fnLoop:
|
||||
for _, fn := range j.Program.InitialFunctions {
|
||||
for _, fn := range j.Pkg.InitialFunctions {
|
||||
sig := fn.Type().(*types.Signature)
|
||||
rets := sig.Results()
|
||||
if rets == nil || rets.Len() < 2 {
|
||||
@ -229,7 +223,7 @@ fnLoop:
|
||||
// CheckUnexportedReturn checks that exported functions on exported
|
||||
// types do not return unexported types.
|
||||
func (c *Checker) CheckUnexportedReturn(j *lint.Job) {
|
||||
for _, fn := range j.Program.InitialFunctions {
|
||||
for _, fn := range j.Pkg.InitialFunctions {
|
||||
if fn.Synthetic != "" || fn.Parent() != nil {
|
||||
continue
|
||||
}
|
||||
@ -252,23 +246,21 @@ func (c *Checker) CheckUnexportedReturn(j *lint.Job) {
|
||||
}
|
||||
|
||||
func (c *Checker) CheckReceiverNames(j *lint.Job) {
|
||||
for _, pkg := range j.Program.InitialPackages {
|
||||
for _, m := range pkg.SSA.Members {
|
||||
if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() {
|
||||
ms := typeutil.IntuitiveMethodSet(T.Type(), nil)
|
||||
for _, sel := range ms {
|
||||
fn := sel.Obj().(*types.Func)
|
||||
recv := fn.Type().(*types.Signature).Recv()
|
||||
if Dereference(recv.Type()) != T.Type() {
|
||||
// skip embedded methods
|
||||
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"`)
|
||||
}
|
||||
if recv.Name() == "_" {
|
||||
j.Errorf(recv, "receiver name should not be an underscore, omit the name if it is unused")
|
||||
}
|
||||
for _, m := range j.Pkg.SSA.Members {
|
||||
if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() {
|
||||
ms := typeutil.IntuitiveMethodSet(T.Type(), nil)
|
||||
for _, sel := range ms {
|
||||
fn := sel.Obj().(*types.Func)
|
||||
recv := fn.Type().(*types.Signature).Recv()
|
||||
if Dereference(recv.Type()) != T.Type() {
|
||||
// skip embedded methods
|
||||
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"`)
|
||||
}
|
||||
if recv.Name() == "_" {
|
||||
j.Errorf(recv, "receiver name should not be an underscore, omit the name if it is unused")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -276,37 +268,35 @@ func (c *Checker) CheckReceiverNames(j *lint.Job) {
|
||||
}
|
||||
|
||||
func (c *Checker) CheckReceiverNamesIdentical(j *lint.Job) {
|
||||
for _, pkg := range j.Program.InitialPackages {
|
||||
for _, m := range pkg.SSA.Members {
|
||||
names := map[string]int{}
|
||||
for _, m := range j.Pkg.SSA.Members {
|
||||
names := map[string]int{}
|
||||
|
||||
var firstFn *types.Func
|
||||
if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() {
|
||||
ms := typeutil.IntuitiveMethodSet(T.Type(), nil)
|
||||
for _, sel := range ms {
|
||||
fn := sel.Obj().(*types.Func)
|
||||
recv := fn.Type().(*types.Signature).Recv()
|
||||
if Dereference(recv.Type()) != T.Type() {
|
||||
// skip embedded methods
|
||||
continue
|
||||
}
|
||||
if firstFn == nil {
|
||||
firstFn = fn
|
||||
}
|
||||
if recv.Name() != "" && recv.Name() != "_" {
|
||||
names[recv.Name()]++
|
||||
}
|
||||
var firstFn *types.Func
|
||||
if T, ok := m.Object().(*types.TypeName); ok && !T.IsAlias() {
|
||||
ms := typeutil.IntuitiveMethodSet(T.Type(), nil)
|
||||
for _, sel := range ms {
|
||||
fn := sel.Obj().(*types.Func)
|
||||
recv := fn.Type().(*types.Signature).Recv()
|
||||
if Dereference(recv.Type()) != T.Type() {
|
||||
// skip embedded methods
|
||||
continue
|
||||
}
|
||||
if firstFn == nil {
|
||||
firstFn = fn
|
||||
}
|
||||
if recv.Name() != "" && recv.Name() != "_" {
|
||||
names[recv.Name()]++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(names) > 1 {
|
||||
var seen []string
|
||||
for name, count := range names {
|
||||
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, ", "))
|
||||
if len(names) > 1 {
|
||||
var seen []string
|
||||
for name, count := range names {
|
||||
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, ", "))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -315,7 +305,7 @@ func (c *Checker) CheckContextFirstArg(j *lint.Job) {
|
||||
// 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.Program.InitialFunctions {
|
||||
for _, fn := range j.Pkg.InitialFunctions {
|
||||
if fn.Synthetic != "" || fn.Parent() != nil {
|
||||
continue
|
||||
}
|
||||
@ -337,17 +327,19 @@ fnLoop:
|
||||
}
|
||||
|
||||
func (c *Checker) CheckErrorStrings(j *lint.Job) {
|
||||
fnNames := map[*ssa.Package]map[string]bool{}
|
||||
for _, fn := range j.Program.InitialFunctions {
|
||||
m := fnNames[fn.Package()]
|
||||
if m == nil {
|
||||
m = map[string]bool{}
|
||||
fnNames[fn.Package()] = m
|
||||
objNames := map[*ssa.Package]map[string]bool{}
|
||||
ssapkg := j.Pkg.SSA
|
||||
objNames[ssapkg] = map[string]bool{}
|
||||
for _, m := range ssapkg.Members {
|
||||
if typ, ok := m.(*ssa.Type); ok {
|
||||
objNames[ssapkg][typ.Name()] = true
|
||||
}
|
||||
m[fn.Name()] = true
|
||||
}
|
||||
for _, fn := range j.Pkg.InitialFunctions {
|
||||
objNames[fn.Package()][fn.Name()] = true
|
||||
}
|
||||
|
||||
for _, fn := range j.Program.InitialFunctions {
|
||||
for _, fn := range j.Pkg.InitialFunctions {
|
||||
if IsInTest(j, fn) {
|
||||
// We don't care about malformed error messages in tests;
|
||||
// they're usually for direct human consumption, not part
|
||||
@ -399,8 +391,8 @@ func (c *Checker) CheckErrorStrings(j *lint.Job) {
|
||||
}
|
||||
|
||||
word = strings.TrimRightFunc(word, func(r rune) bool { return unicode.IsPunct(r) })
|
||||
if fnNames[fn.Package()][word] {
|
||||
// Word is probably the name of a function in this package
|
||||
if objNames[fn.Package()][word] {
|
||||
// Word is probably the name of a function or type in this package
|
||||
continue
|
||||
}
|
||||
// First word in error starts with a capital
|
||||
@ -437,15 +429,15 @@ func (c *Checker) CheckTimeNames(j *lint.Job) {
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, f := range j.Program.Files {
|
||||
for _, f := range j.Pkg.Syntax {
|
||||
ast.Inspect(f, func(node ast.Node) bool {
|
||||
switch node := node.(type) {
|
||||
case *ast.ValueSpec:
|
||||
T := TypeOf(j, node.Type)
|
||||
T := j.Pkg.TypesInfo.TypeOf(node.Type)
|
||||
fn(T, node.Names)
|
||||
case *ast.FieldList:
|
||||
for _, field := range node.List {
|
||||
T := TypeOf(j, field.Type)
|
||||
T := j.Pkg.TypesInfo.TypeOf(field.Type)
|
||||
fn(T, field.Names)
|
||||
}
|
||||
}
|
||||
@ -455,7 +447,7 @@ func (c *Checker) CheckTimeNames(j *lint.Job) {
|
||||
}
|
||||
|
||||
func (c *Checker) CheckErrorVarNames(j *lint.Job) {
|
||||
for _, f := range j.Program.Files {
|
||||
for _, f := range j.Pkg.Syntax {
|
||||
for _, decl := range f.Decls {
|
||||
gen, ok := decl.(*ast.GenDecl)
|
||||
if !ok || gen.Tok != token.VAR {
|
||||
@ -549,61 +541,56 @@ var httpStatusCodes = map[int]string{
|
||||
}
|
||||
|
||||
func (c *Checker) CheckHTTPStatusCodes(j *lint.Job) {
|
||||
for _, pkg := range j.Program.InitialPackages {
|
||||
whitelist := map[string]bool{}
|
||||
for _, code := range pkg.Config.HTTPStatusCodeWhitelist {
|
||||
whitelist[code] = true
|
||||
}
|
||||
fn := func(node ast.Node) bool {
|
||||
call, ok := node.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
var arg int
|
||||
switch CallNameAST(j, call) {
|
||||
case "net/http.Error":
|
||||
arg = 2
|
||||
case "net/http.Redirect":
|
||||
arg = 3
|
||||
case "net/http.StatusText":
|
||||
arg = 0
|
||||
case "net/http.RedirectHandler":
|
||||
arg = 1
|
||||
default:
|
||||
return true
|
||||
}
|
||||
lit, ok := call.Args[arg].(*ast.BasicLit)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if whitelist[lit.Value] {
|
||||
return true
|
||||
}
|
||||
|
||||
n, err := strconv.Atoi(lit.Value)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
s, ok := httpStatusCodes[n]
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
j.Errorf(lit, "should use constant http.%s instead of numeric literal %d", s, n)
|
||||
whitelist := map[string]bool{}
|
||||
for _, code := range j.Pkg.Config.HTTPStatusCodeWhitelist {
|
||||
whitelist[code] = true
|
||||
}
|
||||
fn := func(node ast.Node) bool {
|
||||
call, ok := node.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
for _, f := range pkg.Syntax {
|
||||
ast.Inspect(f, fn)
|
||||
|
||||
var arg int
|
||||
switch CallNameAST(j, call) {
|
||||
case "net/http.Error":
|
||||
arg = 2
|
||||
case "net/http.Redirect":
|
||||
arg = 3
|
||||
case "net/http.StatusText":
|
||||
arg = 0
|
||||
case "net/http.RedirectHandler":
|
||||
arg = 1
|
||||
default:
|
||||
return true
|
||||
}
|
||||
lit, ok := call.Args[arg].(*ast.BasicLit)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if whitelist[lit.Value] {
|
||||
return true
|
||||
}
|
||||
|
||||
n, err := strconv.Atoi(lit.Value)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
s, ok := httpStatusCodes[n]
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
j.Errorf(lit, "should use constant http.%s instead of numeric literal %d", s, n)
|
||||
return true
|
||||
}
|
||||
for _, f := range j.Pkg.Syntax {
|
||||
ast.Inspect(f, fn)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Checker) CheckDefaultCaseOrder(j *lint.Job) {
|
||||
fn := func(node ast.Node) bool {
|
||||
stmt, ok := node.(*ast.SwitchStmt)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
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 {
|
||||
@ -611,33 +598,41 @@ func (c *Checker) CheckDefaultCaseOrder(j *lint.Job) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
for _, f := range j.Program.Files {
|
||||
ast.Inspect(f, fn)
|
||||
}
|
||||
j.Pkg.Inspector.Preorder([]ast.Node{(*ast.SwitchStmt)(nil)}, fn)
|
||||
}
|
||||
|
||||
func (c *Checker) CheckYodaConditions(j *lint.Job) {
|
||||
fn := func(node ast.Node) bool {
|
||||
cond, ok := node.(*ast.BinaryExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
fn := func(node ast.Node) {
|
||||
cond := node.(*ast.BinaryExpr)
|
||||
if cond.Op != token.EQL && cond.Op != token.NEQ {
|
||||
return true
|
||||
return
|
||||
}
|
||||
if _, ok := cond.X.(*ast.BasicLit); !ok {
|
||||
return true
|
||||
return
|
||||
}
|
||||
if _, ok := cond.Y.(*ast.BasicLit); ok {
|
||||
// Don't flag lit == lit conditions, just in case
|
||||
return true
|
||||
return
|
||||
}
|
||||
j.Errorf(cond, "don't use Yoda conditions")
|
||||
return true
|
||||
}
|
||||
for _, f := range j.Program.Files {
|
||||
ast.Inspect(f, fn)
|
||||
}
|
||||
j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BinaryExpr)(nil)}, fn)
|
||||
}
|
||||
|
||||
func (c *Checker) CheckInvisibleCharacters(j *lint.Job) {
|
||||
fn := func(node ast.Node) {
|
||||
lit := node.(*ast.BasicLit)
|
||||
if lit.Kind != token.STRING {
|
||||
return
|
||||
}
|
||||
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)
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
j.Pkg.Inspector.Preorder([]ast.Node{(*ast.BasicLit)(nil)}, fn)
|
||||
}
|
||||
|
184
vendor/honnef.co/go/tools/stylecheck/names.go
vendored
184
vendor/honnef.co/go/tools/stylecheck/names.go
vendored
@ -71,109 +71,107 @@ func (c *Checker) CheckNames(j *lint.Job) {
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range j.Program.InitialPackages {
|
||||
initialisms := make(map[string]bool, len(pkg.Config.Initialisms))
|
||||
for _, word := range pkg.Config.Initialisms {
|
||||
initialisms[word] = true
|
||||
initialisms := make(map[string]bool, len(j.Pkg.Config.Initialisms))
|
||||
for _, word := range j.Pkg.Config.Initialisms {
|
||||
initialisms[word] = true
|
||||
}
|
||||
for _, f := range j.Pkg.Syntax {
|
||||
// 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")
|
||||
}
|
||||
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))
|
||||
}
|
||||
for _, f := range pkg.Syntax {
|
||||
// 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")
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
ast.Inspect(f, func(node ast.Node) bool {
|
||||
switch v := node.(type) {
|
||||
case *ast.AssignStmt:
|
||||
if v.Tok != token.DEFINE {
|
||||
return true
|
||||
}
|
||||
for _, exp := range v.Lhs {
|
||||
if id, ok := exp.(*ast.Ident); ok {
|
||||
check(id, "var", initialisms)
|
||||
}
|
||||
}
|
||||
case *ast.FuncDecl:
|
||||
// Functions with no body are defined elsewhere (in
|
||||
// assembly, or via go:linkname). These are likely to
|
||||
// be something very low level (such as the runtime),
|
||||
// where our rules don't apply.
|
||||
if v.Body == nil {
|
||||
return true
|
||||
ast.Inspect(f, func(node ast.Node) bool {
|
||||
switch v := node.(type) {
|
||||
case *ast.AssignStmt:
|
||||
if v.Tok != token.DEFINE {
|
||||
return true
|
||||
}
|
||||
for _, exp := range v.Lhs {
|
||||
if id, ok := exp.(*ast.Ident); ok {
|
||||
check(id, "var", initialisms)
|
||||
}
|
||||
}
|
||||
case *ast.FuncDecl:
|
||||
// Functions with no body are defined elsewhere (in
|
||||
// assembly, or via go:linkname). These are likely to
|
||||
// be something very low level (such as the runtime),
|
||||
// where our rules don't apply.
|
||||
if v.Body == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if IsInTest(j, v) && (strings.HasPrefix(v.Name.Name, "Example") || strings.HasPrefix(v.Name.Name, "Test") || strings.HasPrefix(v.Name.Name, "Benchmark")) {
|
||||
return true
|
||||
}
|
||||
if IsInTest(j, v) && (strings.HasPrefix(v.Name.Name, "Example") || strings.HasPrefix(v.Name.Name, "Test") || strings.HasPrefix(v.Name.Name, "Benchmark")) {
|
||||
return true
|
||||
}
|
||||
|
||||
thing := "func"
|
||||
if v.Recv != nil {
|
||||
thing = "method"
|
||||
}
|
||||
thing := "func"
|
||||
if v.Recv != nil {
|
||||
thing = "method"
|
||||
}
|
||||
|
||||
if !isTechnicallyExported(v) {
|
||||
check(v.Name, thing, initialisms)
|
||||
}
|
||||
if !isTechnicallyExported(v) {
|
||||
check(v.Name, thing, initialisms)
|
||||
}
|
||||
|
||||
checkList(v.Type.Params, thing+" parameter", initialisms)
|
||||
checkList(v.Type.Results, thing+" result", initialisms)
|
||||
case *ast.GenDecl:
|
||||
if v.Tok == token.IMPORT {
|
||||
return true
|
||||
}
|
||||
var thing string
|
||||
switch v.Tok {
|
||||
case token.CONST:
|
||||
thing = "const"
|
||||
case token.TYPE:
|
||||
thing = "type"
|
||||
case token.VAR:
|
||||
thing = "var"
|
||||
}
|
||||
for _, spec := range v.Specs {
|
||||
switch s := spec.(type) {
|
||||
case *ast.TypeSpec:
|
||||
check(s.Name, thing, initialisms)
|
||||
case *ast.ValueSpec:
|
||||
for _, id := range s.Names {
|
||||
check(id, thing, initialisms)
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.InterfaceType:
|
||||
// Do not check interface method names.
|
||||
// They are often constrainted by the method names of concrete types.
|
||||
for _, x := range v.Methods.List {
|
||||
ft, ok := x.Type.(*ast.FuncType)
|
||||
if !ok { // might be an embedded interface name
|
||||
continue
|
||||
}
|
||||
checkList(ft.Params, "interface method parameter", initialisms)
|
||||
checkList(ft.Results, "interface method result", initialisms)
|
||||
}
|
||||
case *ast.RangeStmt:
|
||||
if v.Tok == token.ASSIGN {
|
||||
return true
|
||||
}
|
||||
if id, ok := v.Key.(*ast.Ident); ok {
|
||||
check(id, "range var", initialisms)
|
||||
}
|
||||
if id, ok := v.Value.(*ast.Ident); ok {
|
||||
check(id, "range var", initialisms)
|
||||
}
|
||||
case *ast.StructType:
|
||||
for _, f := range v.Fields.List {
|
||||
for _, id := range f.Names {
|
||||
check(id, "struct field", initialisms)
|
||||
checkList(v.Type.Params, thing+" parameter", initialisms)
|
||||
checkList(v.Type.Results, thing+" result", initialisms)
|
||||
case *ast.GenDecl:
|
||||
if v.Tok == token.IMPORT {
|
||||
return true
|
||||
}
|
||||
var thing string
|
||||
switch v.Tok {
|
||||
case token.CONST:
|
||||
thing = "const"
|
||||
case token.TYPE:
|
||||
thing = "type"
|
||||
case token.VAR:
|
||||
thing = "var"
|
||||
}
|
||||
for _, spec := range v.Specs {
|
||||
switch s := spec.(type) {
|
||||
case *ast.TypeSpec:
|
||||
check(s.Name, thing, initialisms)
|
||||
case *ast.ValueSpec:
|
||||
for _, id := range s.Names {
|
||||
check(id, thing, initialisms)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
case *ast.InterfaceType:
|
||||
// Do not check interface method names.
|
||||
// They are often constrainted by the method names of concrete types.
|
||||
for _, x := range v.Methods.List {
|
||||
ft, ok := x.Type.(*ast.FuncType)
|
||||
if !ok { // might be an embedded interface name
|
||||
continue
|
||||
}
|
||||
checkList(ft.Params, "interface method parameter", initialisms)
|
||||
checkList(ft.Results, "interface method result", initialisms)
|
||||
}
|
||||
case *ast.RangeStmt:
|
||||
if v.Tok == token.ASSIGN {
|
||||
return true
|
||||
}
|
||||
if id, ok := v.Key.(*ast.Ident); ok {
|
||||
check(id, "range var", initialisms)
|
||||
}
|
||||
if id, ok := v.Value.(*ast.Ident); ok {
|
||||
check(id, "range var", initialisms)
|
||||
}
|
||||
case *ast.StructType:
|
||||
for _, f := range v.Fields.List {
|
||||
for _, id := range f.Names {
|
||||
check(id, "struct field", initialisms)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user