Updated staticcheck
This commit is contained in:
parent
5dc1fd0f86
commit
99f83542f6
34 changed files with 6144 additions and 2537 deletions
2
go.mod
2
go.mod
|
@ -73,6 +73,6 @@ require (
|
|||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/testfixtures.v2 v2.5.3
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
honnef.co/go/tools v0.0.0-20190215041234-466a0476246c
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a
|
||||
src.techknowlogick.com/xormigrate v0.0.0-20190321151057-24497c23c09c
|
||||
)
|
||||
|
|
3
go.sum
3
go.sum
|
@ -31,6 +31,7 @@ github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJ
|
|||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/cweill/gotests v1.5.2 h1:kKqmKmS2wCV3tuLnfpbiuN8OlkosQZTpCfiqmiuNAsA=
|
||||
github.com/cweill/gotests v1.5.3 h1:k3t4wW/x/YNixWZJhUIn+mivmK5iV1tJVOwVYkx0UcU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -266,5 +267,7 @@ honnef.co/go/tools v0.0.0-20190128043916-71123fcbb8fe h1:/GZ/onp6W295MEgrIwtlbnx
|
|||
honnef.co/go/tools v0.0.0-20190128043916-71123fcbb8fe/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190215041234-466a0476246c h1:z+UFwlQ7KVwdlQTE5JjvDvfZmyyAVrEiiwau20b7X8k=
|
||||
honnef.co/go/tools v0.0.0-20190215041234-466a0476246c/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a h1:LJwr7TCTghdatWv40WobzlKXc9c4s8oGa7QKJUtHhWA=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
src.techknowlogick.com/xormigrate v0.0.0-20190321151057-24497c23c09c h1:fTwL7EZ3ouk3xeiPiRBYEjSPWTREb9T57bjzpRBNOpQ=
|
||||
src.techknowlogick.com/xormigrate v0.0.0-20190321151057-24497c23c09c/go.mod h1:B2NutmcRaDDw4EGe7DoCwyWCELA8W+KxXPhLtgqFUaU=
|
||||
|
|
182
vendor/golang.org/x/tools/go/ast/inspector/inspector.go
generated
vendored
Normal file
182
vendor/golang.org/x/tools/go/ast/inspector/inspector.go
generated
vendored
Normal file
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package inspector provides helper functions for traversal over the
|
||||
// syntax trees of a package, including node filtering by type, and
|
||||
// materialization of the traversal stack.
|
||||
//
|
||||
// During construction, the inspector does a complete traversal and
|
||||
// builds a list of push/pop events and their node type. Subsequent
|
||||
// method calls that request a traversal scan this list, rather than walk
|
||||
// the AST, and perform type filtering using efficient bit sets.
|
||||
//
|
||||
// Experiments suggest the inspector's traversals are about 2.5x faster
|
||||
// than ast.Inspect, but it may take around 5 traversals for this
|
||||
// benefit to amortize the inspector's construction cost.
|
||||
// If efficiency is the primary concern, do not use use Inspector for
|
||||
// one-off traversals.
|
||||
package inspector
|
||||
|
||||
// There are four orthogonal features in a traversal:
|
||||
// 1 type filtering
|
||||
// 2 pruning
|
||||
// 3 postorder calls to f
|
||||
// 4 stack
|
||||
// Rather than offer all of them in the API,
|
||||
// only a few combinations are exposed:
|
||||
// - Preorder is the fastest and has fewest features,
|
||||
// but is the most commonly needed traversal.
|
||||
// - Nodes and WithStack both provide pruning and postorder calls,
|
||||
// even though few clients need it, because supporting two versions
|
||||
// is not justified.
|
||||
// More combinations could be supported by expressing them as
|
||||
// wrappers around a more generic traversal, but this was measured
|
||||
// and found to degrade performance significantly (30%).
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
)
|
||||
|
||||
// An Inspector provides methods for inspecting
|
||||
// (traversing) the syntax trees of a package.
|
||||
type Inspector struct {
|
||||
events []event
|
||||
}
|
||||
|
||||
// New returns an Inspector for the specified syntax trees.
|
||||
func New(files []*ast.File) *Inspector {
|
||||
return &Inspector{traverse(files)}
|
||||
}
|
||||
|
||||
// An event represents a push or a pop
|
||||
// of an ast.Node during a traversal.
|
||||
type event struct {
|
||||
node ast.Node
|
||||
typ uint64 // typeOf(node)
|
||||
index int // 1 + index of corresponding pop event, or 0 if this is a pop
|
||||
}
|
||||
|
||||
// Preorder visits all the nodes of the files supplied to New in
|
||||
// depth-first order. It calls f(n) for each node n before it visits
|
||||
// n's children.
|
||||
//
|
||||
// The types argument, if non-empty, enables type-based filtering of
|
||||
// events. The function f if is called only for nodes whose type
|
||||
// matches an element of the types slice.
|
||||
func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) {
|
||||
// Because it avoids postorder calls to f, and the pruning
|
||||
// check, Preorder is almost twice as fast as Nodes. The two
|
||||
// features seem to contribute similar slowdowns (~1.4x each).
|
||||
|
||||
mask := maskOf(types)
|
||||
for i := 0; i < len(in.events); {
|
||||
ev := in.events[i]
|
||||
if ev.typ&mask != 0 {
|
||||
if ev.index > 0 {
|
||||
f(ev.node)
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// Nodes visits the nodes of the files supplied to New in depth-first
|
||||
// order. It calls f(n, true) for each node n before it visits n's
|
||||
// children. If f returns true, Nodes invokes f recursively for each
|
||||
// of the non-nil children of the node, followed by a call of
|
||||
// f(n, false).
|
||||
//
|
||||
// The types argument, if non-empty, enables type-based filtering of
|
||||
// events. The function f if is called only for nodes whose type
|
||||
// matches an element of the types slice.
|
||||
func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (prune bool)) {
|
||||
mask := maskOf(types)
|
||||
for i := 0; i < len(in.events); {
|
||||
ev := in.events[i]
|
||||
if ev.typ&mask != 0 {
|
||||
if ev.index > 0 {
|
||||
// push
|
||||
if !f(ev.node, true) {
|
||||
i = ev.index // jump to corresponding pop + 1
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// pop
|
||||
f(ev.node, false)
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// WithStack visits nodes in a similar manner to Nodes, but it
|
||||
// supplies each call to f an additional argument, the current
|
||||
// traversal stack. The stack's first element is the outermost node,
|
||||
// an *ast.File; its last is the innermost, n.
|
||||
func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, stack []ast.Node) (prune bool)) {
|
||||
mask := maskOf(types)
|
||||
var stack []ast.Node
|
||||
for i := 0; i < len(in.events); {
|
||||
ev := in.events[i]
|
||||
if ev.index > 0 {
|
||||
// push
|
||||
stack = append(stack, ev.node)
|
||||
if ev.typ&mask != 0 {
|
||||
if !f(ev.node, true, stack) {
|
||||
i = ev.index
|
||||
stack = stack[:len(stack)-1]
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// pop
|
||||
if ev.typ&mask != 0 {
|
||||
f(ev.node, false, stack)
|
||||
}
|
||||
stack = stack[:len(stack)-1]
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// traverse builds the table of events representing a traversal.
|
||||
func traverse(files []*ast.File) []event {
|
||||
// Preallocate approximate number of events
|
||||
// based on source file extent.
|
||||
// This makes traverse faster by 4x (!).
|
||||
var extent int
|
||||
for _, f := range files {
|
||||
extent += int(f.End() - f.Pos())
|
||||
}
|
||||
// This estimate is based on the net/http package.
|
||||
events := make([]event, 0, extent*33/100)
|
||||
|
||||
var stack []event
|
||||
for _, f := range files {
|
||||
ast.Inspect(f, func(n ast.Node) bool {
|
||||
if n != nil {
|
||||
// push
|
||||
ev := event{
|
||||
node: n,
|
||||
typ: typeOf(n),
|
||||
index: len(events), // push event temporarily holds own index
|
||||
}
|
||||
stack = append(stack, ev)
|
||||
events = append(events, ev)
|
||||
} else {
|
||||
// pop
|
||||
ev := stack[len(stack)-1]
|
||||
stack = stack[:len(stack)-1]
|
||||
|
||||
events[ev.index].index = len(events) + 1 // make push refer to pop
|
||||
|
||||
ev.index = 0 // turn ev into a pop event
|
||||
events = append(events, ev)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
return events
|
||||
}
|
216
vendor/golang.org/x/tools/go/ast/inspector/typeof.go
generated
vendored
Normal file
216
vendor/golang.org/x/tools/go/ast/inspector/typeof.go
generated
vendored
Normal file
|
@ -0,0 +1,216 @@
|
|||
package inspector
|
||||
|
||||
// This file defines func typeOf(ast.Node) uint64.
|
||||
//
|
||||
// The initial map-based implementation was too slow;
|
||||
// see https://go-review.googlesource.com/c/tools/+/135655/1/go/ast/inspector/inspector.go#196
|
||||
|
||||
import "go/ast"
|
||||
|
||||
const (
|
||||
nArrayType = iota
|
||||
nAssignStmt
|
||||
nBadDecl
|
||||
nBadExpr
|
||||
nBadStmt
|
||||
nBasicLit
|
||||
nBinaryExpr
|
||||
nBlockStmt
|
||||
nBranchStmt
|
||||
nCallExpr
|
||||
nCaseClause
|
||||
nChanType
|
||||
nCommClause
|
||||
nComment
|
||||
nCommentGroup
|
||||
nCompositeLit
|
||||
nDeclStmt
|
||||
nDeferStmt
|
||||
nEllipsis
|
||||
nEmptyStmt
|
||||
nExprStmt
|
||||
nField
|
||||
nFieldList
|
||||
nFile
|
||||
nForStmt
|
||||
nFuncDecl
|
||||
nFuncLit
|
||||
nFuncType
|
||||
nGenDecl
|
||||
nGoStmt
|
||||
nIdent
|
||||
nIfStmt
|
||||
nImportSpec
|
||||
nIncDecStmt
|
||||
nIndexExpr
|
||||
nInterfaceType
|
||||
nKeyValueExpr
|
||||
nLabeledStmt
|
||||
nMapType
|
||||
nPackage
|
||||
nParenExpr
|
||||
nRangeStmt
|
||||
nReturnStmt
|
||||
nSelectStmt
|
||||
nSelectorExpr
|
||||
nSendStmt
|
||||
nSliceExpr
|
||||
nStarExpr
|
||||
nStructType
|
||||
nSwitchStmt
|
||||
nTypeAssertExpr
|
||||
nTypeSpec
|
||||
nTypeSwitchStmt
|
||||
nUnaryExpr
|
||||
nValueSpec
|
||||
)
|
||||
|
||||
// typeOf returns a distinct single-bit value that represents the type of n.
|
||||
//
|
||||
// Various implementations were benchmarked with BenchmarkNewInspector:
|
||||
// GOGC=off
|
||||
// - type switch 4.9-5.5ms 2.1ms
|
||||
// - binary search over a sorted list of types 5.5-5.9ms 2.5ms
|
||||
// - linear scan, frequency-ordered list 5.9-6.1ms 2.7ms
|
||||
// - linear scan, unordered list 6.4ms 2.7ms
|
||||
// - hash table 6.5ms 3.1ms
|
||||
// A perfect hash seemed like overkill.
|
||||
//
|
||||
// The compiler's switch statement is the clear winner
|
||||
// as it produces a binary tree in code,
|
||||
// with constant conditions and good branch prediction.
|
||||
// (Sadly it is the most verbose in source code.)
|
||||
// Binary search suffered from poor branch prediction.
|
||||
//
|
||||
func typeOf(n ast.Node) uint64 {
|
||||
// Fast path: nearly half of all nodes are identifiers.
|
||||
if _, ok := n.(*ast.Ident); ok {
|
||||
return 1 << nIdent
|
||||
}
|
||||
|
||||
// These cases include all nodes encountered by ast.Inspect.
|
||||
switch n.(type) {
|
||||
case *ast.ArrayType:
|
||||
return 1 << nArrayType
|
||||
case *ast.AssignStmt:
|
||||
return 1 << nAssignStmt
|
||||
case *ast.BadDecl:
|
||||
return 1 << nBadDecl
|
||||
case *ast.BadExpr:
|
||||
return 1 << nBadExpr
|
||||
case *ast.BadStmt:
|
||||
return 1 << nBadStmt
|
||||
case *ast.BasicLit:
|
||||
return 1 << nBasicLit
|
||||
case *ast.BinaryExpr:
|
||||
return 1 << nBinaryExpr
|
||||
case *ast.BlockStmt:
|
||||
return 1 << nBlockStmt
|
||||
case *ast.BranchStmt:
|
||||
return 1 << nBranchStmt
|
||||
case *ast.CallExpr:
|
||||
return 1 << nCallExpr
|
||||
case *ast.CaseClause:
|
||||
return 1 << nCaseClause
|
||||
case *ast.ChanType:
|
||||
return 1 << nChanType
|
||||
case *ast.CommClause:
|
||||
return 1 << nCommClause
|
||||
case *ast.Comment:
|
||||
return 1 << nComment
|
||||
case *ast.CommentGroup:
|
||||
return 1 << nCommentGroup
|
||||
case *ast.CompositeLit:
|
||||
return 1 << nCompositeLit
|
||||
case *ast.DeclStmt:
|
||||
return 1 << nDeclStmt
|
||||
case *ast.DeferStmt:
|
||||
return 1 << nDeferStmt
|
||||
case *ast.Ellipsis:
|
||||
return 1 << nEllipsis
|
||||
case *ast.EmptyStmt:
|
||||
return 1 << nEmptyStmt
|
||||
case *ast.ExprStmt:
|
||||
return 1 << nExprStmt
|
||||
case *ast.Field:
|
||||
return 1 << nField
|
||||
case *ast.FieldList:
|
||||
return 1 << nFieldList
|
||||
case *ast.File:
|
||||
return 1 << nFile
|
||||
case *ast.ForStmt:
|
||||
return 1 << nForStmt
|
||||
case *ast.FuncDecl:
|
||||
return 1 << nFuncDecl
|
||||
case *ast.FuncLit:
|
||||
return 1 << nFuncLit
|
||||
case *ast.FuncType:
|
||||
return 1 << nFuncType
|
||||
case *ast.GenDecl:
|
||||
return 1 << nGenDecl
|
||||
case *ast.GoStmt:
|
||||
return 1 << nGoStmt
|
||||
case *ast.Ident:
|
||||
return 1 << nIdent
|
||||
case *ast.IfStmt:
|
||||
return 1 << nIfStmt
|
||||
case *ast.ImportSpec:
|
||||
return 1 << nImportSpec
|
||||
case *ast.IncDecStmt:
|
||||
return 1 << nIncDecStmt
|
||||
case *ast.IndexExpr:
|
||||
return 1 << nIndexExpr
|
||||
case *ast.InterfaceType:
|
||||
return 1 << nInterfaceType
|
||||
case *ast.KeyValueExpr:
|
||||
return 1 << nKeyValueExpr
|
||||
case *ast.LabeledStmt:
|
||||
return 1 << nLabeledStmt
|
||||
case *ast.MapType:
|
||||
return 1 << nMapType
|
||||
case *ast.Package:
|
||||
return 1 << nPackage
|
||||
case *ast.ParenExpr:
|
||||
return 1 << nParenExpr
|
||||
case *ast.RangeStmt:
|
||||
return 1 << nRangeStmt
|
||||
case *ast.ReturnStmt:
|
||||
return 1 << nReturnStmt
|
||||
case *ast.SelectStmt:
|
||||
return 1 << nSelectStmt
|
||||
case *ast.SelectorExpr:
|
||||
return 1 << nSelectorExpr
|
||||
case *ast.SendStmt:
|
||||
return 1 << nSendStmt
|
||||
case *ast.SliceExpr:
|
||||
return 1 << nSliceExpr
|
||||
case *ast.StarExpr:
|
||||
return 1 << nStarExpr
|
||||
case *ast.StructType:
|
||||
return 1 << nStructType
|
||||
case *ast.SwitchStmt:
|
||||
return 1 << nSwitchStmt
|
||||
case *ast.TypeAssertExpr:
|
||||
return 1 << nTypeAssertExpr
|
||||
case *ast.TypeSpec:
|
||||
return 1 << nTypeSpec
|
||||
case *ast.TypeSwitchStmt:
|
||||
return 1 << nTypeSwitchStmt
|
||||
case *ast.UnaryExpr:
|
||||
return 1 << nUnaryExpr
|
||||
case *ast.ValueSpec:
|
||||
return 1 << nValueSpec
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func maskOf(nodes []ast.Node) uint64 {
|
||||
if nodes == nil {
|
||||
return 1<<64 - 1 // match all node types
|
||||
}
|
||||
var mask uint64
|
||||
for _, n := range nodes {
|
||||
mask |= typeOf(n)
|
||||
}
|
||||
return mask
|
||||
}
|
|
@ -20,11 +20,8 @@ func main() {
|
|||
simple.NewChecker(),
|
||||
staticcheck.NewChecker(),
|
||||
stylecheck.NewChecker(),
|
||||
&unused.Checker{},
|
||||
}
|
||||
|
||||
uc := unused.NewChecker(unused.CheckAll)
|
||||
uc.ConsiderReflection = true
|
||||
checkers = append(checkers, unused.NewLintChecker(uc))
|
||||
|
||||
lintutil.ProcessFlagSet(checkers, fs)
|
||||
}
|
||||
|
|
2
vendor/honnef.co/go/tools/config/config.go
vendored
2
vendor/honnef.co/go/tools/config/config.go
vendored
|
@ -82,7 +82,7 @@ var defaultConfig = Config{
|
|||
"SMTP", "SQL", "SSH", "TCP", "TLS", "TTL",
|
||||
"UDP", "UI", "GID", "UID", "UUID", "URI",
|
||||
"URL", "UTF8", "VM", "XML", "XMPP", "XSRF",
|
||||
"XSS",
|
||||
"XSS", "SIP", "RTP",
|
||||
},
|
||||
DotImportWhitelist: []string{},
|
||||
HTTPStatusCodeWhitelist: []string{"200", "400", "404", "500"},
|
||||
|
|
|
@ -5,6 +5,6 @@ initialisms = ["ACL", "API", "ASCII", "CPU", "CSS", "DNS",
|
|||
"SMTP", "SQL", "SSH", "TCP", "TLS", "TTL",
|
||||
"UDP", "UI", "GID", "UID", "UUID", "URI",
|
||||
"URL", "UTF8", "VM", "XML", "XMPP", "XSRF",
|
||||
"XSS"]
|
||||
"XSS", "SIP", "RTP"]
|
||||
dot_import_whitelist = []
|
||||
http_status_code_whitelist = ["200", "400", "404", "500"]
|
||||
|
|
142
vendor/honnef.co/go/tools/deprecated/stdlib.go
vendored
142
vendor/honnef.co/go/tools/deprecated/stdlib.go
vendored
|
@ -9,46 +9,104 @@ var Stdlib = map[string]Deprecation{
|
|||
"image/jpeg.Reader": {4, 0},
|
||||
// FIXME(dh): AllowBinary isn't being detected as deprecated
|
||||
// because the comment has a newline right after "Deprecated:"
|
||||
"go/build.AllowBinary": {7, 7},
|
||||
"(archive/zip.FileHeader).CompressedSize": {1, 1},
|
||||
"(archive/zip.FileHeader).UncompressedSize": {1, 1},
|
||||
"(go/doc.Package).Bugs": {1, 1},
|
||||
"os.SEEK_SET": {7, 7},
|
||||
"os.SEEK_CUR": {7, 7},
|
||||
"os.SEEK_END": {7, 7},
|
||||
"(net.Dialer).Cancel": {7, 7},
|
||||
"runtime.CPUProfile": {9, 0},
|
||||
"compress/flate.ReadError": {6, 6},
|
||||
"compress/flate.WriteError": {6, 6},
|
||||
"path/filepath.HasPrefix": {0, 0},
|
||||
"(net/http.Transport).Dial": {7, 7},
|
||||
"(*net/http.Transport).CancelRequest": {6, 5},
|
||||
"net/http.ErrWriteAfterFlush": {7, 0},
|
||||
"net/http.ErrHeaderTooLong": {8, 0},
|
||||
"net/http.ErrShortBody": {8, 0},
|
||||
"net/http.ErrMissingContentLength": {8, 0},
|
||||
"net/http/httputil.ErrPersistEOF": {0, 0},
|
||||
"net/http/httputil.ErrClosed": {0, 0},
|
||||
"net/http/httputil.ErrPipeline": {0, 0},
|
||||
"net/http/httputil.ServerConn": {0, 0},
|
||||
"net/http/httputil.NewServerConn": {0, 0},
|
||||
"net/http/httputil.ClientConn": {0, 0},
|
||||
"net/http/httputil.NewClientConn": {0, 0},
|
||||
"net/http/httputil.NewProxyClientConn": {0, 0},
|
||||
"(net/http.Request).Cancel": {7, 7},
|
||||
"(text/template/parse.PipeNode).Line": {1, 1},
|
||||
"(text/template/parse.ActionNode).Line": {1, 1},
|
||||
"(text/template/parse.BranchNode).Line": {1, 1},
|
||||
"(text/template/parse.TemplateNode).Line": {1, 1},
|
||||
"database/sql/driver.ColumnConverter": {9, 9},
|
||||
"database/sql/driver.Execer": {8, 8},
|
||||
"database/sql/driver.Queryer": {8, 8},
|
||||
"(database/sql/driver.Conn).Begin": {8, 8},
|
||||
"(database/sql/driver.Stmt).Exec": {8, 8},
|
||||
"(database/sql/driver.Stmt).Query": {8, 8},
|
||||
"syscall.StringByteSlice": {1, 1},
|
||||
"syscall.StringBytePtr": {1, 1},
|
||||
"syscall.StringSlicePtr": {1, 1},
|
||||
"syscall.StringToUTF16": {1, 1},
|
||||
"syscall.StringToUTF16Ptr": {1, 1},
|
||||
"go/build.AllowBinary": {7, 7},
|
||||
"(archive/zip.FileHeader).CompressedSize": {1, 1},
|
||||
"(archive/zip.FileHeader).UncompressedSize": {1, 1},
|
||||
"(archive/zip.FileHeader).ModifiedTime": {10, 10},
|
||||
"(archive/zip.FileHeader).ModifiedDate": {10, 10},
|
||||
"(*archive/zip.FileHeader).ModTime": {10, 10},
|
||||
"(*archive/zip.FileHeader).SetModTime": {10, 10},
|
||||
"(go/doc.Package).Bugs": {1, 1},
|
||||
"os.SEEK_SET": {7, 7},
|
||||
"os.SEEK_CUR": {7, 7},
|
||||
"os.SEEK_END": {7, 7},
|
||||
"(net.Dialer).Cancel": {7, 7},
|
||||
"runtime.CPUProfile": {9, 0},
|
||||
"compress/flate.ReadError": {6, 6},
|
||||
"compress/flate.WriteError": {6, 6},
|
||||
"path/filepath.HasPrefix": {0, 0},
|
||||
"(net/http.Transport).Dial": {7, 7},
|
||||
"(*net/http.Transport).CancelRequest": {6, 5},
|
||||
"net/http.ErrWriteAfterFlush": {7, 0},
|
||||
"net/http.ErrHeaderTooLong": {8, 0},
|
||||
"net/http.ErrShortBody": {8, 0},
|
||||
"net/http.ErrMissingContentLength": {8, 0},
|
||||
"net/http/httputil.ErrPersistEOF": {0, 0},
|
||||
"net/http/httputil.ErrClosed": {0, 0},
|
||||
"net/http/httputil.ErrPipeline": {0, 0},
|
||||
"net/http/httputil.ServerConn": {0, 0},
|
||||
"net/http/httputil.NewServerConn": {0, 0},
|
||||
"net/http/httputil.ClientConn": {0, 0},
|
||||
"net/http/httputil.NewClientConn": {0, 0},
|
||||
"net/http/httputil.NewProxyClientConn": {0, 0},
|
||||
"(net/http.Request).Cancel": {7, 7},
|
||||
"(text/template/parse.PipeNode).Line": {1, 1},
|
||||
"(text/template/parse.ActionNode).Line": {1, 1},
|
||||
"(text/template/parse.BranchNode).Line": {1, 1},
|
||||
"(text/template/parse.TemplateNode).Line": {1, 1},
|
||||
"database/sql/driver.ColumnConverter": {9, 9},
|
||||
"database/sql/driver.Execer": {8, 8},
|
||||
"database/sql/driver.Queryer": {8, 8},
|
||||
"(database/sql/driver.Conn).Begin": {8, 8},
|
||||
"(database/sql/driver.Stmt).Exec": {8, 8},
|
||||
"(database/sql/driver.Stmt).Query": {8, 8},
|
||||
"syscall.StringByteSlice": {1, 1},
|
||||
"syscall.StringBytePtr": {1, 1},
|
||||
"syscall.StringSlicePtr": {1, 1},
|
||||
"syscall.StringToUTF16": {1, 1},
|
||||
"syscall.StringToUTF16Ptr": {1, 1},
|
||||
"(*regexp.Regexp).Copy": {12, 12},
|
||||
"(archive/tar.Header).Xattrs": {10, 10},
|
||||
"archive/tar.TypeRegA": {11, 1},
|
||||
"go/types.NewInterface": {11, 11},
|
||||
"(*go/types.Interface).Embedded": {11, 11},
|
||||
"go/importer.For": {12, 12},
|
||||
"encoding/json.InvalidUTF8Error": {2, 2},
|
||||
"encoding/json.UnmarshalFieldError": {2, 2},
|
||||
"encoding/csv.ErrTrailingComma": {2, 2},
|
||||
"(encoding/csv.Reader).TrailingComma": {2, 2},
|
||||
"(net.Dialer).DualStack": {12, 12},
|
||||
"net/http.ErrUnexpectedTrailer": {12, 12},
|
||||
"net/http.CloseNotifier": {11, 7},
|
||||
"net/http.ProtocolError": {8, 8},
|
||||
"(crypto/x509.CertificateRequest).Attributes": {5, 3},
|
||||
// This function has no alternative, but also no purpose.
|
||||
"(*crypto/rc4.Cipher).Reset": {12, 0},
|
||||
"(net/http/httptest.ResponseRecorder).HeaderMap": {11, 7},
|
||||
|
||||
// All of these have been deprecated in favour of external libraries
|
||||
"syscall.AttachLsf": {7, 0},
|
||||
"syscall.DetachLsf": {7, 0},
|
||||
"syscall.LsfSocket": {7, 0},
|
||||
"syscall.SetLsfPromisc": {7, 0},
|
||||
"syscall.LsfJump": {7, 0},
|
||||
"syscall.LsfStmt": {7, 0},
|
||||
"syscall.BpfStmt": {7, 0},
|
||||
"syscall.BpfJump": {7, 0},
|
||||
"syscall.BpfBuflen": {7, 0},
|
||||
"syscall.SetBpfBuflen": {7, 0},
|
||||
"syscall.BpfDatalink": {7, 0},
|
||||
"syscall.SetBpfDatalink": {7, 0},
|
||||
"syscall.SetBpfPromisc": {7, 0},
|
||||
"syscall.FlushBpf": {7, 0},
|
||||
"syscall.BpfInterface": {7, 0},
|
||||
"syscall.SetBpfInterface": {7, 0},
|
||||
"syscall.BpfTimeout": {7, 0},
|
||||
"syscall.SetBpfTimeout": {7, 0},
|
||||
"syscall.BpfStats": {7, 0},
|
||||
"syscall.SetBpfImmediate": {7, 0},
|
||||
"syscall.SetBpf": {7, 0},
|
||||
"syscall.CheckBpfVersion": {7, 0},
|
||||
"syscall.BpfHeadercmpl": {7, 0},
|
||||
"syscall.SetBpfHeadercmpl": {7, 0},
|
||||
"syscall.RouteRIB": {8, 0},
|
||||
"syscall.RoutingMessage": {8, 0},
|
||||
"syscall.RouteMessage": {8, 0},
|
||||
"syscall.InterfaceMessage": {8, 0},
|
||||
"syscall.InterfaceAddrMessage": {8, 0},
|
||||
"syscall.ParseRoutingMessage": {8, 0},
|
||||
"syscall.ParseRoutingSockaddr": {8, 0},
|
||||
"InterfaceAnnounceMessage": {7, 0},
|
||||
"InterfaceMulticastAddrMessage": {7, 0},
|
||||
"syscall.FormatMessage": {5, 0},
|
||||
}
|
||||
|
|
46
vendor/honnef.co/go/tools/go/types/typeutil/callee.go
vendored
Normal file
46
vendor/honnef.co/go/tools/go/types/typeutil/callee.go
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package typeutil
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// Callee returns the named target of a function call, if any:
|
||||
// a function, method, builtin, or variable.
|
||||
func Callee(info *types.Info, call *ast.CallExpr) types.Object {
|
||||
var obj types.Object
|
||||
switch fun := astutil.Unparen(call.Fun).(type) {
|
||||
case *ast.Ident:
|
||||
obj = info.Uses[fun] // type, var, builtin, or declared func
|
||||
case *ast.SelectorExpr:
|
||||
if sel, ok := info.Selections[fun]; ok {
|
||||
obj = sel.Obj() // method or field
|
||||
} else {
|
||||
obj = info.Uses[fun.Sel] // qualified identifier?
|
||||
}
|
||||
}
|
||||
if _, ok := obj.(*types.TypeName); ok {
|
||||
return nil // T(x) is a conversion, not a call
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// StaticCallee returns the target (function or method) of a static
|
||||
// function call, if any. It returns nil for calls to builtins.
|
||||
func StaticCallee(info *types.Info, call *ast.CallExpr) *types.Func {
|
||||
if f, ok := Callee(info, call).(*types.Func); ok && !interfaceMethod(f) {
|
||||
return f
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func interfaceMethod(f *types.Func) bool {
|
||||
recv := f.Type().(*types.Signature).Recv()
|
||||
return recv != nil && types.IsInterface(recv.Type())
|
||||
}
|
29
vendor/honnef.co/go/tools/go/types/typeutil/identical.go
vendored
Normal file
29
vendor/honnef.co/go/tools/go/types/typeutil/identical.go
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
package typeutil
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
)
|
||||
|
||||
// Identical reports whether x and y are identical types.
|
||||
// Unlike types.Identical, receivers of Signature types are not ignored.
|
||||
func Identical(x, y types.Type) (ret bool) {
|
||||
if !types.Identical(x, y) {
|
||||
return false
|
||||
}
|
||||
sigX, ok := x.(*types.Signature)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
sigY, ok := y.(*types.Signature)
|
||||
if !ok {
|
||||
// should be impossible
|
||||
return true
|
||||
}
|
||||
if sigX.Recv() == sigY.Recv() {
|
||||
return true
|
||||
}
|
||||
if sigX.Recv() == nil || sigY.Recv() == nil {
|
||||
return false
|
||||
}
|
||||
return Identical(sigX.Recv().Type(), sigY.Recv().Type())
|
||||
}
|
31
vendor/honnef.co/go/tools/go/types/typeutil/imports.go
vendored
Normal file
31
vendor/honnef.co/go/tools/go/types/typeutil/imports.go
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package typeutil
|
||||
|
||||
import "go/types"
|
||||
|
||||
// Dependencies returns all dependencies of the specified packages.
|
||||
//
|
||||
// Dependent packages appear in topological order: if package P imports
|
||||
// package Q, Q appears earlier than P in the result.
|
||||
// The algorithm follows import statements in the order they
|
||||
// appear in the source code, so the result is a total order.
|
||||
//
|
||||
func Dependencies(pkgs ...*types.Package) []*types.Package {
|
||||
var result []*types.Package
|
||||
seen := make(map[*types.Package]bool)
|
||||
var visit func(pkgs []*types.Package)
|
||||
visit = func(pkgs []*types.Package) {
|
||||
for _, p := range pkgs {
|
||||
if !seen[p] {
|
||||
seen[p] = true
|
||||
visit(p.Imports())
|
||||
result = append(result, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(pkgs)
|
||||
return result
|
||||
}
|
315
vendor/honnef.co/go/tools/go/types/typeutil/map.go
vendored
Normal file
315
vendor/honnef.co/go/tools/go/types/typeutil/map.go
vendored
Normal file
|
@ -0,0 +1,315 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package typeutil defines various utilities for types, such as Map,
|
||||
// a mapping from types.Type to interface{} values.
|
||||
package typeutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Map is a hash-table-based mapping from types (types.Type) to
|
||||
// arbitrary interface{} values. The concrete types that implement
|
||||
// the Type interface are pointers. Since they are not canonicalized,
|
||||
// == cannot be used to check for equivalence, and thus we cannot
|
||||
// simply use a Go map.
|
||||
//
|
||||
// Just as with map[K]V, a nil *Map is a valid empty map.
|
||||
//
|
||||
// Not thread-safe.
|
||||
//
|
||||
// This fork handles Signatures correctly, respecting method receivers.
|
||||
//
|
||||
type Map struct {
|
||||
hasher Hasher // shared by many Maps
|
||||
table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
|
||||
length int // number of map entries
|
||||
}
|
||||
|
||||
// entry is an entry (key/value association) in a hash bucket.
|
||||
type entry struct {
|
||||
key types.Type
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// SetHasher sets the hasher used by Map.
|
||||
//
|
||||
// All Hashers are functionally equivalent but contain internal state
|
||||
// used to cache the results of hashing previously seen types.
|
||||
//
|
||||
// A single Hasher created by MakeHasher() may be shared among many
|
||||
// Maps. This is recommended if the instances have many keys in
|
||||
// common, as it will amortize the cost of hash computation.
|
||||
//
|
||||
// A Hasher may grow without bound as new types are seen. Even when a
|
||||
// type is deleted from the map, the Hasher never shrinks, since other
|
||||
// types in the map may reference the deleted type indirectly.
|
||||
//
|
||||
// Hashers are not thread-safe, and read-only operations such as
|
||||
// Map.Lookup require updates to the hasher, so a full Mutex lock (not a
|
||||
// read-lock) is require around all Map operations if a shared
|
||||
// hasher is accessed from multiple threads.
|
||||
//
|
||||
// If SetHasher is not called, the Map will create a private hasher at
|
||||
// the first call to Insert.
|
||||
//
|
||||
func (m *Map) SetHasher(hasher Hasher) {
|
||||
m.hasher = hasher
|
||||
}
|
||||
|
||||
// Delete removes the entry with the given key, if any.
|
||||
// It returns true if the entry was found.
|
||||
//
|
||||
func (m *Map) Delete(key types.Type) bool {
|
||||
if m != nil && m.table != nil {
|
||||
hash := m.hasher.Hash(key)
|
||||
bucket := m.table[hash]
|
||||
for i, e := range bucket {
|
||||
if e.key != nil && Identical(key, e.key) {
|
||||
// We can't compact the bucket as it
|
||||
// would disturb iterators.
|
||||
bucket[i] = entry{}
|
||||
m.length--
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// At returns the map entry for the given key.
|
||||
// The result is nil if the entry is not present.
|
||||
//
|
||||
func (m *Map) At(key types.Type) interface{} {
|
||||
if m != nil && m.table != nil {
|
||||
for _, e := range m.table[m.hasher.Hash(key)] {
|
||||
if e.key != nil && Identical(key, e.key) {
|
||||
return e.value
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set sets the map entry for key to val,
|
||||
// and returns the previous entry, if any.
|
||||
func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) {
|
||||
if m.table != nil {
|
||||
hash := m.hasher.Hash(key)
|
||||
bucket := m.table[hash]
|
||||
var hole *entry
|
||||
for i, e := range bucket {
|
||||
if e.key == nil {
|
||||
hole = &bucket[i]
|
||||
} else if Identical(key, e.key) {
|
||||
prev = e.value
|
||||
bucket[i].value = value
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if hole != nil {
|
||||
*hole = entry{key, value} // overwrite deleted entry
|
||||
} else {
|
||||
m.table[hash] = append(bucket, entry{key, value})
|
||||
}
|
||||
} else {
|
||||
if m.hasher.memo == nil {
|
||||
m.hasher = MakeHasher()
|
||||
}
|
||||
hash := m.hasher.Hash(key)
|
||||
m.table = map[uint32][]entry{hash: {entry{key, value}}}
|
||||
}
|
||||
|
||||
m.length++
|
||||
return
|
||||
}
|
||||
|
||||
// Len returns the number of map entries.
|
||||
func (m *Map) Len() int {
|
||||
if m != nil {
|
||||
return m.length
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Iterate calls function f on each entry in the map in unspecified order.
|
||||
//
|
||||
// If f should mutate the map, Iterate provides the same guarantees as
|
||||
// Go maps: if f deletes a map entry that Iterate has not yet reached,
|
||||
// f will not be invoked for it, but if f inserts a map entry that
|
||||
// Iterate has not yet reached, whether or not f will be invoked for
|
||||
// it is unspecified.
|
||||
//
|
||||
func (m *Map) Iterate(f func(key types.Type, value interface{})) {
|
||||
if m != nil {
|
||||
for _, bucket := range m.table {
|
||||
for _, e := range bucket {
|
||||
if e.key != nil {
|
||||
f(e.key, e.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keys returns a new slice containing the set of map keys.
|
||||
// The order is unspecified.
|
||||
func (m *Map) Keys() []types.Type {
|
||||
keys := make([]types.Type, 0, m.Len())
|
||||
m.Iterate(func(key types.Type, _ interface{}) {
|
||||
keys = append(keys, key)
|
||||
})
|
||||
return keys
|
||||
}
|
||||
|
||||
func (m *Map) toString(values bool) string {
|
||||
if m == nil {
|
||||
return "{}"
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, "{")
|
||||
sep := ""
|
||||
m.Iterate(func(key types.Type, value interface{}) {
|
||||
fmt.Fprint(&buf, sep)
|
||||
sep = ", "
|
||||
fmt.Fprint(&buf, key)
|
||||
if values {
|
||||
fmt.Fprintf(&buf, ": %q", value)
|
||||
}
|
||||
})
|
||||
fmt.Fprint(&buf, "}")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// String returns a string representation of the map's entries.
|
||||
// Values are printed using fmt.Sprintf("%v", v).
|
||||
// Order is unspecified.
|
||||
//
|
||||
func (m *Map) String() string {
|
||||
return m.toString(true)
|
||||
}
|
||||
|
||||
// KeysString returns a string representation of the map's key set.
|
||||
// Order is unspecified.
|
||||
//
|
||||
func (m *Map) KeysString() string {
|
||||
return m.toString(false)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Hasher
|
||||
|
||||
// A Hasher maps each type to its hash value.
|
||||
// For efficiency, a hasher uses memoization; thus its memory
|
||||
// footprint grows monotonically over time.
|
||||
// Hashers are not thread-safe.
|
||||
// Hashers have reference semantics.
|
||||
// Call MakeHasher to create a Hasher.
|
||||
type Hasher struct {
|
||||
memo map[types.Type]uint32
|
||||
}
|
||||
|
||||
// MakeHasher returns a new Hasher instance.
|
||||
func MakeHasher() Hasher {
|
||||
return Hasher{make(map[types.Type]uint32)}
|
||||
}
|
||||
|
||||
// Hash computes a hash value for the given type t such that
|
||||
// Identical(t, t') => Hash(t) == Hash(t').
|
||||
func (h Hasher) Hash(t types.Type) uint32 {
|
||||
hash, ok := h.memo[t]
|
||||
if !ok {
|
||||
hash = h.hashFor(t)
|
||||
h.memo[t] = hash
|
||||
}
|
||||
return hash
|
||||
}
|
||||
|
||||
// hashString computes the Fowler–Noll–Vo hash of s.
|
||||
func hashString(s string) uint32 {
|
||||
var h uint32
|
||||
for i := 0; i < len(s); i++ {
|
||||
h ^= uint32(s[i])
|
||||
h *= 16777619
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// hashFor computes the hash of t.
|
||||
func (h Hasher) hashFor(t types.Type) uint32 {
|
||||
// See Identical for rationale.
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
return uint32(t.Kind())
|
||||
|
||||
case *types.Array:
|
||||
return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
|
||||
|
||||
case *types.Slice:
|
||||
return 9049 + 2*h.Hash(t.Elem())
|
||||
|
||||
case *types.Struct:
|
||||
var hash uint32 = 9059
|
||||
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||
f := t.Field(i)
|
||||
if f.Anonymous() {
|
||||
hash += 8861
|
||||
}
|
||||
hash += hashString(t.Tag(i))
|
||||
hash += hashString(f.Name()) // (ignore f.Pkg)
|
||||
hash += h.Hash(f.Type())
|
||||
}
|
||||
return hash
|
||||
|
||||
case *types.Pointer:
|
||||
return 9067 + 2*h.Hash(t.Elem())
|
||||
|
||||
case *types.Signature:
|
||||
var hash uint32 = 9091
|
||||
if t.Variadic() {
|
||||
hash *= 8863
|
||||
}
|
||||
return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
|
||||
|
||||
case *types.Interface:
|
||||
var hash uint32 = 9103
|
||||
for i, n := 0, t.NumMethods(); i < n; i++ {
|
||||
// See go/types.identicalMethods for rationale.
|
||||
// Method order is not significant.
|
||||
// Ignore m.Pkg().
|
||||
m := t.Method(i)
|
||||
hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type())
|
||||
}
|
||||
return hash
|
||||
|
||||
case *types.Map:
|
||||
return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem())
|
||||
|
||||
case *types.Chan:
|
||||
return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
|
||||
|
||||
case *types.Named:
|
||||
// Not safe with a copying GC; objects may move.
|
||||
return uint32(reflect.ValueOf(t.Obj()).Pointer())
|
||||
|
||||
case *types.Tuple:
|
||||
return h.hashTuple(t)
|
||||
}
|
||||
panic(t)
|
||||
}
|
||||
|
||||
func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
|
||||
// See go/types.identicalTypes for rationale.
|
||||
n := tuple.Len()
|
||||
var hash uint32 = 9137 + 2*uint32(n)
|
||||
for i := 0; i < n; i++ {
|
||||
hash += 3 * h.Hash(tuple.At(i).Type())
|
||||
}
|
||||
return hash
|
||||
}
|
72
vendor/honnef.co/go/tools/go/types/typeutil/methodsetcache.go
vendored
Normal file
72
vendor/honnef.co/go/tools/go/types/typeutil/methodsetcache.go
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file implements a cache of method sets.
|
||||
|
||||
package typeutil
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A MethodSetCache records the method set of each type T for which
|
||||
// MethodSet(T) is called so that repeat queries are fast.
|
||||
// The zero value is a ready-to-use cache instance.
|
||||
type MethodSetCache struct {
|
||||
mu sync.Mutex
|
||||
named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N
|
||||
others map[types.Type]*types.MethodSet // all other types
|
||||
}
|
||||
|
||||
// MethodSet returns the method set of type T. It is thread-safe.
|
||||
//
|
||||
// If cache is nil, this function is equivalent to types.NewMethodSet(T).
|
||||
// Utility functions can thus expose an optional *MethodSetCache
|
||||
// parameter to clients that care about performance.
|
||||
//
|
||||
func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
|
||||
if cache == nil {
|
||||
return types.NewMethodSet(T)
|
||||
}
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
switch T := T.(type) {
|
||||
case *types.Named:
|
||||
return cache.lookupNamed(T).value
|
||||
|
||||
case *types.Pointer:
|
||||
if N, ok := T.Elem().(*types.Named); ok {
|
||||
return cache.lookupNamed(N).pointer
|
||||
}
|
||||
}
|
||||
|
||||
// all other types
|
||||
// (The map uses pointer equivalence, not type identity.)
|
||||
mset := cache.others[T]
|
||||
if mset == nil {
|
||||
mset = types.NewMethodSet(T)
|
||||
if cache.others == nil {
|
||||
cache.others = make(map[types.Type]*types.MethodSet)
|
||||
}
|
||||
cache.others[T] = mset
|
||||
}
|
||||
return mset
|
||||
}
|
||||
|
||||
func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
|
||||
if cache.named == nil {
|
||||
cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
|
||||
}
|
||||
// Avoid recomputing mset(*T) for each distinct Pointer
|
||||
// instance whose underlying type is a named type.
|
||||
msets, ok := cache.named[named]
|
||||
if !ok {
|
||||
msets.value = types.NewMethodSet(named)
|
||||
msets.pointer = types.NewMethodSet(types.NewPointer(named))
|
||||
cache.named[named] = msets
|
||||
}
|
||||
return msets
|
||||
}
|
52
vendor/honnef.co/go/tools/go/types/typeutil/ui.go
vendored
Normal file
52
vendor/honnef.co/go/tools/go/types/typeutil/ui.go
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package typeutil
|
||||
|
||||
// This file defines utilities for user interfaces that display types.
|
||||
|
||||
import "go/types"
|
||||
|
||||
// IntuitiveMethodSet returns the intuitive method set of a type T,
|
||||
// which is the set of methods you can call on an addressable value of
|
||||
// that type.
|
||||
//
|
||||
// The result always contains MethodSet(T), and is exactly MethodSet(T)
|
||||
// for interface types and for pointer-to-concrete types.
|
||||
// For all other concrete types T, the result additionally
|
||||
// contains each method belonging to *T if there is no identically
|
||||
// named method on T itself.
|
||||
//
|
||||
// This corresponds to user intuition about method sets;
|
||||
// this function is intended only for user interfaces.
|
||||
//
|
||||
// The order of the result is as for types.MethodSet(T).
|
||||
//
|
||||
func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
|
||||
isPointerToConcrete := func(T types.Type) bool {
|
||||
ptr, ok := T.(*types.Pointer)
|
||||
return ok && !types.IsInterface(ptr.Elem())
|
||||
}
|
||||
|
||||
var result []*types.Selection
|
||||
mset := msets.MethodSet(T)
|
||||
if types.IsInterface(T) || isPointerToConcrete(T) {
|
||||
for i, n := 0, mset.Len(); i < n; i++ {
|
||||
result = append(result, mset.At(i))
|
||||
}
|
||||
} else {
|
||||
// T is some other concrete type.
|
||||
// Report methods of T and *T, preferring those of T.
|
||||
pmset := msets.MethodSet(types.NewPointer(T))
|
||||
for i, n := 0, pmset.Len(); i < n; i++ {
|
||||
meth := pmset.At(i)
|
||||
if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
|
||||
meth = m
|
||||
}
|
||||
result = append(result, meth)
|
||||
}
|
||||
|
||||
}
|
||||
return result
|
||||
}
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
func CheckRangeStringRunes(j *lint.Job) {
|
||||
for _, ssafn := range j.Program.InitialFunctions {
|
||||
for _, ssafn := range j.Pkg.InitialFunctions {
|
||||
fn := func(node ast.Node) bool {
|
||||
rng, ok := node.(*ast.RangeStmt)
|
||||
if !ok || !IsBlank(rng.Key) {
|
||||
|
|
10
vendor/honnef.co/go/tools/lint/generated.go
vendored
10
vendor/honnef.co/go/tools/lint/generated.go
vendored
|
@ -4,6 +4,7 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -15,8 +16,13 @@ var (
|
|||
crnl = []byte("\r\n")
|
||||
)
|
||||
|
||||
func isGenerated(r io.Reader) bool {
|
||||
br := bufio.NewReader(r)
|
||||
func isGenerated(path string) bool {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
br := bufio.NewReader(f)
|
||||
for {
|
||||
s, err := br.ReadBytes('\n')
|
||||
if err != nil && err != io.EOF {
|
||||
|
|
305
vendor/honnef.co/go/tools/lint/lint.go
vendored
305
vendor/honnef.co/go/tools/lint/lint.go
vendored
|
@ -2,6 +2,7 @@
|
|||
package lint // import "honnef.co/go/tools/lint"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
|
@ -9,12 +10,14 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/go/packages"
|
||||
"honnef.co/go/tools/config"
|
||||
"honnef.co/go/tools/ssa"
|
||||
|
@ -22,9 +25,9 @@ import (
|
|||
)
|
||||
|
||||
type Job struct {
|
||||
Program *Program
|
||||
Pkg *Pkg
|
||||
GoVersion int
|
||||
|
||||
checker string
|
||||
check Check
|
||||
problems []Problem
|
||||
|
||||
|
@ -106,20 +109,10 @@ func (gi *GlobIgnore) Match(p Problem) bool {
|
|||
}
|
||||
|
||||
type Program struct {
|
||||
SSA *ssa.Program
|
||||
InitialPackages []*Pkg
|
||||
InitialFunctions []*ssa.Function
|
||||
AllPackages []*packages.Package
|
||||
AllFunctions []*ssa.Function
|
||||
Files []*ast.File
|
||||
GoVersion int
|
||||
|
||||
tokenFileMap map[*token.File]*ast.File
|
||||
astFileMap map[*ast.File]*Pkg
|
||||
packagesMap map[string]*packages.Package
|
||||
|
||||
genMu sync.RWMutex
|
||||
generatedMap map[string]bool
|
||||
SSA *ssa.Program
|
||||
InitialPackages []*Pkg
|
||||
AllPackages []*packages.Package
|
||||
AllFunctions []*ssa.Function
|
||||
}
|
||||
|
||||
func (prog *Program) Fset() *token.FileSet {
|
||||
|
@ -141,7 +134,6 @@ type Problem struct {
|
|||
Position token.Position // position in source file
|
||||
Text string // the prose that describes the problem
|
||||
Check string
|
||||
Checker string
|
||||
Package *Pkg
|
||||
Severity Severity
|
||||
}
|
||||
|
@ -164,6 +156,7 @@ type Check struct {
|
|||
Fn Func
|
||||
ID string
|
||||
FilterGenerated bool
|
||||
Doc string
|
||||
}
|
||||
|
||||
// A Linter lints Go source code.
|
||||
|
@ -205,12 +198,8 @@ func (l *Linter) ignore(p Problem) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (prog *Program) File(node Positioner) *ast.File {
|
||||
return prog.tokenFileMap[prog.SSA.Fset.File(node.Pos())]
|
||||
}
|
||||
|
||||
func (j *Job) File(node Positioner) *ast.File {
|
||||
return j.Program.File(node)
|
||||
return j.Pkg.tokenFileMap[j.Pkg.Fset.File(node.Pos())]
|
||||
}
|
||||
|
||||
func parseDirective(s string) (cmd string, args []string) {
|
||||
|
@ -266,6 +255,7 @@ func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem {
|
|||
if stats != nil {
|
||||
stats.SSABuild = time.Since(t)
|
||||
}
|
||||
runtime.GC()
|
||||
|
||||
t = time.Now()
|
||||
pkgMap := map[*ssa.Package]*Pkg{}
|
||||
|
@ -291,9 +281,19 @@ func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem {
|
|||
}
|
||||
|
||||
pkg := &Pkg{
|
||||
SSA: ssapkg,
|
||||
Package: pkg,
|
||||
Config: cfg,
|
||||
SSA: ssapkg,
|
||||
Package: pkg,
|
||||
Config: cfg,
|
||||
Generated: map[string]bool{},
|
||||
tokenFileMap: map[*token.File]*ast.File{},
|
||||
}
|
||||
pkg.Inspector = inspector.New(pkg.Syntax)
|
||||
for _, f := range pkg.Syntax {
|
||||
tf := pkg.Fset.File(f.Pos())
|
||||
pkg.tokenFileMap[tf] = f
|
||||
|
||||
path := DisplayPosition(pkg.Fset, f.Pos()).Filename
|
||||
pkg.Generated[path] = isGenerated(path)
|
||||
}
|
||||
pkgMap[ssapkg] = pkg
|
||||
pkgs = append(pkgs, pkg)
|
||||
|
@ -303,42 +303,15 @@ func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem {
|
|||
SSA: ssaprog,
|
||||
InitialPackages: pkgs,
|
||||
AllPackages: allPkgs,
|
||||
GoVersion: l.GoVersion,
|
||||
tokenFileMap: map[*token.File]*ast.File{},
|
||||
astFileMap: map[*ast.File]*Pkg{},
|
||||
generatedMap: map[string]bool{},
|
||||
}
|
||||
prog.packagesMap = map[string]*packages.Package{}
|
||||
for _, pkg := range allPkgs {
|
||||
prog.packagesMap[pkg.Types.Path()] = pkg
|
||||
}
|
||||
|
||||
isInitial := map[*types.Package]struct{}{}
|
||||
for _, pkg := range pkgs {
|
||||
isInitial[pkg.Types] = struct{}{}
|
||||
}
|
||||
for fn := range ssautil.AllFunctions(ssaprog) {
|
||||
prog.AllFunctions = append(prog.AllFunctions, fn)
|
||||
if fn.Pkg == nil {
|
||||
continue
|
||||
}
|
||||
prog.AllFunctions = append(prog.AllFunctions, fn)
|
||||
if _, ok := isInitial[fn.Pkg.Pkg]; ok {
|
||||
prog.InitialFunctions = append(prog.InitialFunctions, fn)
|
||||
}
|
||||
}
|
||||
for _, pkg := range pkgs {
|
||||
prog.Files = append(prog.Files, pkg.Syntax...)
|
||||
|
||||
ssapkg := ssaprog.Package(pkg.Types)
|
||||
for _, f := range pkg.Syntax {
|
||||
prog.astFileMap[f] = pkgMap[ssapkg]
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range allPkgs {
|
||||
for _, f := range pkg.Syntax {
|
||||
tf := pkg.Fset.File(f.Pos())
|
||||
prog.tokenFileMap[tf] = f
|
||||
if pkg, ok := pkgMap[fn.Pkg]; ok {
|
||||
pkg.InitialFunctions = append(pkg.InitialFunctions, fn)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -346,6 +319,19 @@ func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem {
|
|||
l.automaticIgnores = nil
|
||||
for _, pkg := range initial {
|
||||
for _, f := range pkg.Syntax {
|
||||
found := false
|
||||
commentLoop:
|
||||
for _, cg := range f.Comments {
|
||||
for _, c := range cg.List {
|
||||
if strings.Contains(c.Text, "//lint:") {
|
||||
found = true
|
||||
break commentLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
cm := ast.NewCommentMap(pkg.Fset, f, f.Comments)
|
||||
for node, cgs := range cm {
|
||||
for _, cg := range cgs {
|
||||
|
@ -359,10 +345,9 @@ func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem {
|
|||
if len(args) < 2 {
|
||||
// FIXME(dh): this causes duplicated warnings when using megacheck
|
||||
p := Problem{
|
||||
Position: prog.DisplayPosition(c.Pos()),
|
||||
Position: DisplayPosition(prog.Fset(), c.Pos()),
|
||||
Text: "malformed linter directive; missing the required reason field?",
|
||||
Check: "",
|
||||
Checker: "lint",
|
||||
Package: nil,
|
||||
}
|
||||
out = append(out, p)
|
||||
|
@ -373,7 +358,7 @@ func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem {
|
|||
continue
|
||||
}
|
||||
checks := strings.Split(args[0], ",")
|
||||
pos := prog.DisplayPosition(node.Pos())
|
||||
pos := DisplayPosition(prog.Fset(), node.Pos())
|
||||
var ig Ignore
|
||||
switch cmd {
|
||||
case "ignore":
|
||||
|
@ -396,23 +381,6 @@ func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem {
|
|||
}
|
||||
}
|
||||
|
||||
sizes := struct {
|
||||
types int
|
||||
defs int
|
||||
uses int
|
||||
implicits int
|
||||
selections int
|
||||
scopes int
|
||||
}{}
|
||||
for _, pkg := range pkgs {
|
||||
sizes.types += len(pkg.TypesInfo.Types)
|
||||
sizes.defs += len(pkg.TypesInfo.Defs)
|
||||
sizes.uses += len(pkg.TypesInfo.Uses)
|
||||
sizes.implicits += len(pkg.TypesInfo.Implicits)
|
||||
sizes.selections += len(pkg.TypesInfo.Selections)
|
||||
sizes.scopes += len(pkg.TypesInfo.Scopes)
|
||||
}
|
||||
|
||||
if stats != nil {
|
||||
stats.OtherInitWork = time.Since(t)
|
||||
}
|
||||
|
@ -428,41 +396,31 @@ func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem {
|
|||
var jobs []*Job
|
||||
var allChecks []string
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, checker := range l.Checkers {
|
||||
checks := checker.Checks()
|
||||
for _, check := range checks {
|
||||
for _, check := range checker.Checks() {
|
||||
allChecks = append(allChecks, check.ID)
|
||||
j := &Job{
|
||||
Program: prog,
|
||||
checker: checker.Name(),
|
||||
check: check,
|
||||
if check.Fn == nil {
|
||||
continue
|
||||
}
|
||||
for _, pkg := range pkgs {
|
||||
j := &Job{
|
||||
Pkg: pkg,
|
||||
check: check,
|
||||
GoVersion: l.GoVersion,
|
||||
}
|
||||
jobs = append(jobs, j)
|
||||
wg.Add(1)
|
||||
go func(check Check, j *Job) {
|
||||
t := time.Now()
|
||||
check.Fn(j)
|
||||
j.duration = time.Since(t)
|
||||
wg.Done()
|
||||
}(check, j)
|
||||
}
|
||||
jobs = append(jobs, j)
|
||||
}
|
||||
}
|
||||
|
||||
max := len(jobs)
|
||||
if l.MaxConcurrentJobs > 0 {
|
||||
max = l.MaxConcurrentJobs
|
||||
}
|
||||
|
||||
sem := make(chan struct{}, max)
|
||||
wg := &sync.WaitGroup{}
|
||||
for _, j := range jobs {
|
||||
wg.Add(1)
|
||||
go func(j *Job) {
|
||||
defer wg.Done()
|
||||
sem <- struct{}{}
|
||||
defer func() { <-sem }()
|
||||
fn := j.check.Fn
|
||||
if fn == nil {
|
||||
return
|
||||
}
|
||||
t := time.Now()
|
||||
fn(j)
|
||||
j.duration = time.Since(t)
|
||||
}(j)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for _, j := range jobs {
|
||||
|
@ -470,6 +428,9 @@ func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem {
|
|||
stats.Jobs = append(stats.Jobs, JobStat{j.check.ID, j.duration})
|
||||
}
|
||||
for _, p := range j.problems {
|
||||
if p.Package == nil {
|
||||
panic(fmt.Sprintf("internal error: problem at position %s has nil package", p.Position))
|
||||
}
|
||||
allowedChecks := FilterChecks(allChecks, p.Package.Config.Checks)
|
||||
|
||||
if l.ignore(p) {
|
||||
|
@ -498,19 +459,21 @@ func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem {
|
|||
}
|
||||
|
||||
couldveMatched := false
|
||||
for f, pkg := range prog.astFileMap {
|
||||
if prog.Fset().Position(f.Pos()).Filename != ig.File {
|
||||
continue
|
||||
}
|
||||
allowedChecks := FilterChecks(allChecks, pkg.Config.Checks)
|
||||
for _, c := range ig.Checks {
|
||||
if !allowedChecks[c] {
|
||||
for _, pkg := range pkgs {
|
||||
for _, f := range pkg.tokenFileMap {
|
||||
if prog.Fset().Position(f.Pos()).Filename != ig.File {
|
||||
continue
|
||||
}
|
||||
couldveMatched = true
|
||||
allowedChecks := FilterChecks(allChecks, pkg.Config.Checks)
|
||||
for _, c := range ig.Checks {
|
||||
if !allowedChecks[c] {
|
||||
continue
|
||||
}
|
||||
couldveMatched = true
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if !couldveMatched {
|
||||
|
@ -519,10 +482,9 @@ func (l *Linter) Lint(initial []*packages.Package, stats *PerfStats) []Problem {
|
|||
continue
|
||||
}
|
||||
p := Problem{
|
||||
Position: prog.DisplayPosition(ig.pos),
|
||||
Position: DisplayPosition(prog.Fset(), ig.pos),
|
||||
Text: "this linter directive didn't match anything; should it be removed?",
|
||||
Check: "",
|
||||
Checker: "lint",
|
||||
Package: nil,
|
||||
}
|
||||
out = append(out, p)
|
||||
|
@ -609,28 +571,30 @@ func FilterChecks(allChecks []string, checks []string) map[string]bool {
|
|||
return allowedChecks
|
||||
}
|
||||
|
||||
func (prog *Program) Package(path string) *packages.Package {
|
||||
return prog.packagesMap[path]
|
||||
}
|
||||
|
||||
// Pkg represents a package being linted.
|
||||
type Pkg struct {
|
||||
SSA *ssa.Package
|
||||
SSA *ssa.Package
|
||||
InitialFunctions []*ssa.Function
|
||||
*packages.Package
|
||||
Config config.Config
|
||||
Config config.Config
|
||||
Inspector *inspector.Inspector
|
||||
// TODO(dh): this map should probably map from *ast.File, not string
|
||||
Generated map[string]bool
|
||||
|
||||
tokenFileMap map[*token.File]*ast.File
|
||||
}
|
||||
|
||||
type Positioner interface {
|
||||
Pos() token.Pos
|
||||
}
|
||||
|
||||
func (prog *Program) DisplayPosition(p token.Pos) token.Position {
|
||||
func DisplayPosition(fset *token.FileSet, p token.Pos) token.Position {
|
||||
// Only use the adjusted position if it points to another Go file.
|
||||
// This means we'll point to the original file for cgo files, but
|
||||
// we won't point to a YACC grammar file.
|
||||
|
||||
pos := prog.Fset().PositionFor(p, false)
|
||||
adjPos := prog.Fset().PositionFor(p, true)
|
||||
pos := fset.PositionFor(p, false)
|
||||
adjPos := fset.PositionFor(p, true)
|
||||
|
||||
if filepath.Ext(adjPos.Filename) == ".go" {
|
||||
return adjPos
|
||||
|
@ -638,60 +602,21 @@ func (prog *Program) DisplayPosition(p token.Pos) token.Position {
|
|||
return pos
|
||||
}
|
||||
|
||||
func (prog *Program) isGenerated(path string) bool {
|
||||
// This function isn't very efficient in terms of lock contention
|
||||
// and lack of parallelism, but it really shouldn't matter.
|
||||
// Projects consists of thousands of files, and have hundreds of
|
||||
// errors. That's not a lot of calls to isGenerated.
|
||||
|
||||
prog.genMu.RLock()
|
||||
if b, ok := prog.generatedMap[path]; ok {
|
||||
prog.genMu.RUnlock()
|
||||
return b
|
||||
}
|
||||
prog.genMu.RUnlock()
|
||||
prog.genMu.Lock()
|
||||
defer prog.genMu.Unlock()
|
||||
// recheck to avoid doing extra work in case of race
|
||||
if b, ok := prog.generatedMap[path]; ok {
|
||||
return b
|
||||
}
|
||||
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer f.Close()
|
||||
b := isGenerated(f)
|
||||
prog.generatedMap[path] = b
|
||||
return b
|
||||
}
|
||||
|
||||
func (j *Job) Errorf(n Positioner, format string, args ...interface{}) *Problem {
|
||||
tf := j.Program.SSA.Fset.File(n.Pos())
|
||||
f := j.Program.tokenFileMap[tf]
|
||||
pkg := j.Program.astFileMap[f]
|
||||
|
||||
pos := j.Program.DisplayPosition(n.Pos())
|
||||
if j.Program.isGenerated(pos.Filename) && j.check.FilterGenerated {
|
||||
pos := DisplayPosition(j.Pkg.Fset, n.Pos())
|
||||
if j.Pkg.Generated[pos.Filename] && j.check.FilterGenerated {
|
||||
return nil
|
||||
}
|
||||
problem := Problem{
|
||||
Position: pos,
|
||||
Text: fmt.Sprintf(format, args...),
|
||||
Check: j.check.ID,
|
||||
Checker: j.checker,
|
||||
Package: pkg,
|
||||
Package: j.Pkg,
|
||||
}
|
||||
j.problems = append(j.problems, problem)
|
||||
return &j.problems[len(j.problems)-1]
|
||||
}
|
||||
|
||||
func (j *Job) NodePackage(node Positioner) *Pkg {
|
||||
f := j.File(node)
|
||||
return j.Program.astFileMap[f]
|
||||
}
|
||||
|
||||
func allPackages(pkgs []*packages.Package) []*packages.Package {
|
||||
var out []*packages.Package
|
||||
packages.Visit(
|
||||
|
@ -704,3 +629,51 @@ func allPackages(pkgs []*packages.Package) []*packages.Package {
|
|||
)
|
||||
return out
|
||||
}
|
||||
|
||||
var bufferPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
buf.Grow(64)
|
||||
return buf
|
||||
},
|
||||
}
|
||||
|
||||
func FuncName(f *types.Func) string {
|
||||
buf := bufferPool.Get().(*bytes.Buffer)
|
||||
buf.Reset()
|
||||
if f.Type() != nil {
|
||||
sig := f.Type().(*types.Signature)
|
||||
if recv := sig.Recv(); recv != nil {
|
||||
buf.WriteByte('(')
|
||||
if _, ok := recv.Type().(*types.Interface); ok {
|
||||
// gcimporter creates abstract methods of
|
||||
// named interfaces using the interface type
|
||||
// (not the named type) as the receiver.
|
||||
// Don't print it in full.
|
||||
buf.WriteString("interface")
|
||||
} else {
|
||||
types.WriteType(buf, recv.Type(), nil)
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
buf.WriteByte('.')
|
||||
} else if f.Pkg() != nil {
|
||||
writePackage(buf, f.Pkg())
|
||||
}
|
||||
}
|
||||
buf.WriteString(f.Name())
|
||||
s := buf.String()
|
||||
bufferPool.Put(buf)
|
||||
return s
|
||||
}
|
||||
|
||||
func writePackage(buf *bytes.Buffer, pkg *types.Package) {
|
||||
if pkg == nil {
|
||||
return
|
||||
}
|
||||
var s string
|
||||
s = pkg.Path()
|
||||
if s != "" {
|
||||
buf.WriteString(s)
|
||||
buf.WriteByte('.')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ func CallName(call *ssa.CallCommon) string {
|
|||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return fn.FullName()
|
||||
return lint.FuncName(fn)
|
||||
case *ssa.Builtin:
|
||||
return v.Name()
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ func IsExample(fn *ssa.Function) bool {
|
|||
|
||||
func IsPointerLike(T types.Type) bool {
|
||||
switch T := T.Underlying().(type) {
|
||||
case *types.Interface, *types.Chan, *types.Map, *types.Pointer:
|
||||
case *types.Interface, *types.Chan, *types.Map, *types.Signature, *types.Pointer:
|
||||
return true
|
||||
case *types.Basic:
|
||||
return T.Kind() == types.UnsafePointer
|
||||
|
@ -103,26 +103,14 @@ func IsZero(expr ast.Expr) bool {
|
|||
return IsIntLiteral(expr, "0")
|
||||
}
|
||||
|
||||
func TypeOf(j *lint.Job, expr ast.Expr) types.Type {
|
||||
if expr == nil {
|
||||
return nil
|
||||
}
|
||||
return j.NodePackage(expr).TypesInfo.TypeOf(expr)
|
||||
}
|
||||
|
||||
func IsOfType(j *lint.Job, expr ast.Expr, name string) bool { return IsType(TypeOf(j, expr), name) }
|
||||
|
||||
func ObjectOf(j *lint.Job, ident *ast.Ident) types.Object {
|
||||
if ident == nil {
|
||||
return nil
|
||||
}
|
||||
return j.NodePackage(ident).TypesInfo.ObjectOf(ident)
|
||||
func IsOfType(j *lint.Job, expr ast.Expr, name string) bool {
|
||||
return IsType(j.Pkg.TypesInfo.TypeOf(expr), name)
|
||||
}
|
||||
|
||||
func IsInTest(j *lint.Job, node lint.Positioner) bool {
|
||||
// FIXME(dh): this doesn't work for global variables with
|
||||
// initializers
|
||||
f := j.Program.SSA.Fset.File(node.Pos())
|
||||
f := j.Pkg.Fset.File(node.Pos())
|
||||
return f != nil && strings.HasSuffix(f.Name(), "_test.go")
|
||||
}
|
||||
|
||||
|
@ -130,15 +118,11 @@ func IsInMain(j *lint.Job, node lint.Positioner) bool {
|
|||
if node, ok := node.(packager); ok {
|
||||
return node.Package().Pkg.Name() == "main"
|
||||
}
|
||||
pkg := j.NodePackage(node)
|
||||
if pkg == nil {
|
||||
return false
|
||||
}
|
||||
return pkg.Types.Name() == "main"
|
||||
return j.Pkg.Types.Name() == "main"
|
||||
}
|
||||
|
||||
func SelectorName(j *lint.Job, expr *ast.SelectorExpr) string {
|
||||
info := j.NodePackage(expr).TypesInfo
|
||||
info := j.Pkg.TypesInfo
|
||||
sel := info.Selections[expr]
|
||||
if sel == nil {
|
||||
if x, ok := expr.X.(*ast.Ident); ok {
|
||||
|
@ -155,11 +139,11 @@ func SelectorName(j *lint.Job, expr *ast.SelectorExpr) string {
|
|||
}
|
||||
|
||||
func IsNil(j *lint.Job, expr ast.Expr) bool {
|
||||
return j.NodePackage(expr).TypesInfo.Types[expr].IsNil()
|
||||
return j.Pkg.TypesInfo.Types[expr].IsNil()
|
||||
}
|
||||
|
||||
func BoolConst(j *lint.Job, expr ast.Expr) bool {
|
||||
val := j.NodePackage(expr).TypesInfo.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val()
|
||||
val := j.Pkg.TypesInfo.ObjectOf(expr.(*ast.Ident)).(*types.Const).Val()
|
||||
return constant.BoolVal(val)
|
||||
}
|
||||
|
||||
|
@ -172,7 +156,7 @@ func IsBoolConst(j *lint.Job, expr ast.Expr) bool {
|
|||
if !ok {
|
||||
return false
|
||||
}
|
||||
obj := j.NodePackage(expr).TypesInfo.ObjectOf(ident)
|
||||
obj := j.Pkg.TypesInfo.ObjectOf(ident)
|
||||
c, ok := obj.(*types.Const)
|
||||
if !ok {
|
||||
return false
|
||||
|
@ -188,7 +172,7 @@ func IsBoolConst(j *lint.Job, expr ast.Expr) bool {
|
|||
}
|
||||
|
||||
func ExprToInt(j *lint.Job, expr ast.Expr) (int64, bool) {
|
||||
tv := j.NodePackage(expr).TypesInfo.Types[expr]
|
||||
tv := j.Pkg.TypesInfo.Types[expr]
|
||||
if tv.Value == nil {
|
||||
return 0, false
|
||||
}
|
||||
|
@ -199,7 +183,7 @@ func ExprToInt(j *lint.Job, expr ast.Expr) (int64, bool) {
|
|||
}
|
||||
|
||||
func ExprToString(j *lint.Job, expr ast.Expr) (string, bool) {
|
||||
val := j.NodePackage(expr).TypesInfo.Types[expr].Value
|
||||
val := j.Pkg.TypesInfo.Types[expr].Value
|
||||
if val == nil {
|
||||
return "", false
|
||||
}
|
||||
|
@ -229,22 +213,22 @@ func DereferenceR(T types.Type) types.Type {
|
|||
}
|
||||
|
||||
func IsGoVersion(j *lint.Job, minor int) bool {
|
||||
return j.Program.GoVersion >= minor
|
||||
return j.GoVersion >= minor
|
||||
}
|
||||
|
||||
func CallNameAST(j *lint.Job, call *ast.CallExpr) string {
|
||||
switch fun := call.Fun.(type) {
|
||||
case *ast.SelectorExpr:
|
||||
fn, ok := ObjectOf(j, fun.Sel).(*types.Func)
|
||||
fn, ok := j.Pkg.TypesInfo.ObjectOf(fun.Sel).(*types.Func)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return fn.FullName()
|
||||
return lint.FuncName(fn)
|
||||
case *ast.Ident:
|
||||
obj := ObjectOf(j, fun)
|
||||
obj := j.Pkg.TypesInfo.ObjectOf(fun)
|
||||
switch obj := obj.(type) {
|
||||
case *types.Func:
|
||||
return obj.FullName()
|
||||
return lint.FuncName(obj)
|
||||
case *types.Builtin:
|
||||
return obj.Name()
|
||||
default:
|
||||
|
@ -273,9 +257,8 @@ func IsCallToAnyAST(j *lint.Job, node ast.Node, names ...string) bool {
|
|||
}
|
||||
|
||||
func Render(j *lint.Job, x interface{}) string {
|
||||
fset := j.Program.SSA.Fset
|
||||
var buf bytes.Buffer
|
||||
if err := printer.Fprint(&buf, fset, x); err != nil {
|
||||
if err := printer.Fprint(&buf, j.Pkg.Fset, x); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.String()
|
||||
|
@ -311,11 +294,10 @@ func Inspect(node ast.Node, fn func(node ast.Node) bool) {
|
|||
ast.Inspect(node, fn)
|
||||
}
|
||||
|
||||
func GroupSpecs(j *lint.Job, specs []ast.Spec) [][]ast.Spec {
|
||||
func GroupSpecs(fset *token.FileSet, specs []ast.Spec) [][]ast.Spec {
|
||||
if len(specs) == 0 {
|
||||
return nil
|
||||
}
|
||||
fset := j.Program.SSA.Fset
|
||||
groups := make([][]ast.Spec, 1)
|
||||
groups[0] = append(groups[0], specs[0])
|
||||
|
||||
|
|
34
vendor/honnef.co/go/tools/lint/lintutil/util.go
vendored
34
vendor/honnef.co/go/tools/lint/lintutil/util.go
vendored
|
@ -17,6 +17,7 @@ import (
|
|||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"runtime/pprof"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -109,6 +110,7 @@ func FlagSet(name string) *flag.FlagSet {
|
|||
flags.Bool("version", false, "Print version and exit")
|
||||
flags.Bool("show-ignored", false, "Don't filter ignored problems")
|
||||
flags.String("f", "text", "Output `format` (valid choices are 'stylish', 'text' and 'json')")
|
||||
flags.String("explain", "", "Print description of `check`")
|
||||
|
||||
flags.Int("debug.max-concurrent-jobs", 0, "Number of jobs to run concurrently")
|
||||
flags.Bool("debug.print-stats", false, "Print debug statistics")
|
||||
|
@ -131,7 +133,22 @@ func FlagSet(name string) *flag.FlagSet {
|
|||
return flags
|
||||
}
|
||||
|
||||
func findCheck(cs []lint.Checker, check string) (lint.Check, bool) {
|
||||
for _, c := range cs {
|
||||
for _, cc := range c.Checks() {
|
||||
if cc.ID == check {
|
||||
return cc, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return lint.Check{}, false
|
||||
}
|
||||
|
||||
func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) {
|
||||
if _, ok := os.LookupEnv("GOGC"); !ok {
|
||||
debug.SetGCPercent(50)
|
||||
}
|
||||
|
||||
tags := fs.Lookup("tags").Value.(flag.Getter).Get().(string)
|
||||
ignore := fs.Lookup("ignore").Value.(flag.Getter).Get().(string)
|
||||
tests := fs.Lookup("tests").Value.(flag.Getter).Get().(bool)
|
||||
|
@ -139,6 +156,7 @@ func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) {
|
|||
formatter := fs.Lookup("f").Value.(flag.Getter).Get().(string)
|
||||
printVersion := fs.Lookup("version").Value.(flag.Getter).Get().(bool)
|
||||
showIgnored := fs.Lookup("show-ignored").Value.(flag.Getter).Get().(bool)
|
||||
explain := fs.Lookup("explain").Value.(flag.Getter).Get().(string)
|
||||
|
||||
maxConcurrentJobs := fs.Lookup("debug.max-concurrent-jobs").Value.(flag.Getter).Get().(int)
|
||||
printStats := fs.Lookup("debug.print-stats").Value.(flag.Getter).Get().(bool)
|
||||
|
@ -175,6 +193,20 @@ func ProcessFlagSet(cs []lint.Checker, fs *flag.FlagSet) {
|
|||
exit(0)
|
||||
}
|
||||
|
||||
if explain != "" {
|
||||
check, ok := findCheck(cs, explain)
|
||||
if !ok {
|
||||
fmt.Fprintln(os.Stderr, "Couldn't find check", explain)
|
||||
exit(1)
|
||||
}
|
||||
if check.Doc == "" {
|
||||
fmt.Fprintln(os.Stderr, explain, "has no documentation")
|
||||
exit(1)
|
||||
}
|
||||
fmt.Println(check.Doc)
|
||||
exit(0)
|
||||
}
|
||||
|
||||
ps, err := Lint(cs, fs.Args(), &Options{
|
||||
Tags: strings.Fields(tags),
|
||||
LintTests: tests,
|
||||
|
@ -279,6 +311,7 @@ func Lint(cs []lint.Checker, paths []string, opt *Options) ([]lint.Problem, erro
|
|||
return nil, err
|
||||
}
|
||||
stats.PackageLoading = time.Since(t)
|
||||
runtime.GC()
|
||||
|
||||
var problems []lint.Problem
|
||||
workingPkgs := make([]*packages.Package, 0, len(pkgs))
|
||||
|
@ -346,7 +379,6 @@ func compileErrors(pkg *packages.Package) []lint.Problem {
|
|||
p := lint.Problem{
|
||||
Position: parsePos(err.Pos),
|
||||
Text: err.Msg,
|
||||
Checker: "compiler",
|
||||
Check: "compile",
|
||||
}
|
||||
ps = append(ps, p)
|
||||
|
|
11
vendor/honnef.co/go/tools/printf/fuzz.go
vendored
Normal file
11
vendor/honnef.co/go/tools/printf/fuzz.go
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
// +build gofuzz
|
||||
|
||||
package printf
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
_, err := Parse(string(data))
|
||||
if err == nil {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
197
vendor/honnef.co/go/tools/printf/printf.go
vendored
Normal file
197
vendor/honnef.co/go/tools/printf/printf.go
vendored
Normal file
|
@ -0,0 +1,197 @@
|
|||
// Package printf implements a parser for fmt.Printf-style format
|
||||
// strings.
|
||||
//
|
||||
// It parses verbs according to the following syntax:
|
||||
// Numeric -> '0'-'9'
|
||||
// Letter -> 'a'-'z' | 'A'-'Z'
|
||||
// Index -> '[' Numeric+ ']'
|
||||
// Star -> '*'
|
||||
// Star -> Index '*'
|
||||
//
|
||||
// Precision -> Numeric+ | Star
|
||||
// Width -> Numeric+ | Star
|
||||
//
|
||||
// WidthAndPrecision -> Width '.' Precision
|
||||
// WidthAndPrecision -> Width '.'
|
||||
// WidthAndPrecision -> Width
|
||||
// WidthAndPrecision -> '.' Precision
|
||||
// WidthAndPrecision -> '.'
|
||||
//
|
||||
// Flag -> '+' | '-' | '#' | ' ' | '0'
|
||||
// Verb -> Letter | '%'
|
||||
//
|
||||
// Input -> '%' [ Flag+ ] [ WidthAndPrecision ] [ Index ] Verb
|
||||
package printf
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrInvalid is returned for invalid format strings or verbs.
|
||||
var ErrInvalid = errors.New("invalid format string")
|
||||
|
||||
type Verb struct {
|
||||
Letter rune
|
||||
Flags string
|
||||
|
||||
Width Argument
|
||||
Precision Argument
|
||||
// Which value in the argument list the verb uses.
|
||||
// -1 denotes the next argument,
|
||||
// values > 0 denote explicit arguments.
|
||||
// The value 0 denotes that no argument is consumed. This is the case for %%.
|
||||
Value int
|
||||
|
||||
Raw string
|
||||
}
|
||||
|
||||
// Argument is an implicit or explicit width or precision.
|
||||
type Argument interface {
|
||||
isArgument()
|
||||
}
|
||||
|
||||
// The Default value, when no width or precision is provided.
|
||||
type Default struct{}
|
||||
|
||||
// Zero is the implicit zero value.
|
||||
// This value may only appear for precisions in format strings like %6.f
|
||||
type Zero struct{}
|
||||
|
||||
// Star is a * value, which may either refer to the next argument (Index == -1) or an explicit argument.
|
||||
type Star struct{ Index int }
|
||||
|
||||
// A Literal value, such as 6 in %6d.
|
||||
type Literal int
|
||||
|
||||
func (Default) isArgument() {}
|
||||
func (Zero) isArgument() {}
|
||||
func (Star) isArgument() {}
|
||||
func (Literal) isArgument() {}
|
||||
|
||||
// Parse parses f and returns a list of actions.
|
||||
// An action may either be a literal string, or a Verb.
|
||||
func Parse(f string) ([]interface{}, error) {
|
||||
var out []interface{}
|
||||
for len(f) > 0 {
|
||||
if f[0] == '%' {
|
||||
v, n, err := ParseVerb(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f = f[n:]
|
||||
out = append(out, v)
|
||||
} else {
|
||||
n := strings.IndexByte(f, '%')
|
||||
if n > -1 {
|
||||
out = append(out, f[:n])
|
||||
f = f[n:]
|
||||
} else {
|
||||
out = append(out, f)
|
||||
f = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func atoi(s string) int {
|
||||
n, _ := strconv.Atoi(s)
|
||||
return n
|
||||
}
|
||||
|
||||
// ParseVerb parses the verb at the beginning of f.
|
||||
// It returns the verb, how much of the input was consumed, and an error, if any.
|
||||
func ParseVerb(f string) (Verb, int, error) {
|
||||
if len(f) < 2 {
|
||||
return Verb{}, 0, ErrInvalid
|
||||
}
|
||||
const (
|
||||
flags = 1
|
||||
|
||||
width = 2
|
||||
widthStar = 3
|
||||
widthIndex = 5
|
||||
|
||||
dot = 6
|
||||
prec = 7
|
||||
precStar = 8
|
||||
precIndex = 10
|
||||
|
||||
verbIndex = 11
|
||||
verb = 12
|
||||
)
|
||||
|
||||
m := re.FindStringSubmatch(f)
|
||||
if m == nil {
|
||||
return Verb{}, 0, ErrInvalid
|
||||
}
|
||||
|
||||
v := Verb{
|
||||
Letter: []rune(m[verb])[0],
|
||||
Flags: m[flags],
|
||||
Raw: m[0],
|
||||
}
|
||||
|
||||
if m[width] != "" {
|
||||
// Literal width
|
||||
v.Width = Literal(atoi(m[width]))
|
||||
} else if m[widthStar] != "" {
|
||||
// Star width
|
||||
if m[widthIndex] != "" {
|
||||
v.Width = Star{atoi(m[widthIndex])}
|
||||
} else {
|
||||
v.Width = Star{-1}
|
||||
}
|
||||
} else {
|
||||
// Default width
|
||||
v.Width = Default{}
|
||||
}
|
||||
|
||||
if m[dot] == "" {
|
||||
// default precision
|
||||
v.Precision = Default{}
|
||||
} else {
|
||||
if m[prec] != "" {
|
||||
// Literal precision
|
||||
v.Precision = Literal(atoi(m[prec]))
|
||||
} else if m[precStar] != "" {
|
||||
// Star precision
|
||||
if m[precIndex] != "" {
|
||||
v.Precision = Star{atoi(m[precIndex])}
|
||||
} else {
|
||||
v.Precision = Star{-1}
|
||||
}
|
||||
} else {
|
||||
// Zero precision
|
||||
v.Precision = Zero{}
|
||||
}
|
||||
}
|
||||
|
||||
if m[verb] == "%" {
|
||||
v.Value = 0
|
||||
} else if m[verbIndex] != "" {
|
||||
v.Value = atoi(m[verbIndex])
|
||||
} else {
|
||||
v.Value = -1
|
||||
}
|
||||
|
||||
return v, len(m[0]), nil
|
||||
}
|
||||
|
||||
const (
|
||||
flags = `([+#0 -]*)`
|
||||
verb = `([a-zA-Z%])`
|
||||
index = `(?:\[([0-9]+)\])`
|
||||
star = `((` + index + `)?\*)`
|
||||
width1 = `([0-9]+)`
|
||||
width2 = star
|
||||
width = `(?:` + width1 + `|` + width2 + `)`
|
||||
precision = width
|
||||
widthAndPrecision = `(?:(?:` + width + `)?(?:(\.)(?:` + precision + `)?)?)`
|
||||
)
|
||||
|
||||
var re = regexp.MustCompile(`^%` + flags + widthAndPrecision + `?` + index + `?` + verb)
|
426
vendor/honnef.co/go/tools/simple/doc.go
vendored
Normal file
426
vendor/honnef.co/go/tools/simple/doc.go
vendored
Normal file
|
@ -0,0 +1,426 @@
|
|||
package simple
|
||||
|
||||
var docS1000 = `Use plain channel send or receive
|
||||
|
||||
Select statements with a single case can be replaced with a simple send or receive.
|
||||
|
||||
Before:
|
||||
|
||||
select {
|
||||
case x := <-ch:
|
||||
fmt.Println(x)
|
||||
}
|
||||
|
||||
After:
|
||||
|
||||
x := <-ch
|
||||
fmt.Println(x)
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1001 = `Replace with copy()
|
||||
|
||||
Use copy() for copying elements from one slice to another.
|
||||
|
||||
Before:
|
||||
|
||||
for i, x := range src {
|
||||
dst[i] = x
|
||||
}
|
||||
|
||||
After:
|
||||
|
||||
copy(dst, src)
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1002 = `Omit comparison with boolean constant
|
||||
|
||||
Before:
|
||||
|
||||
if x == true {}
|
||||
|
||||
After:
|
||||
|
||||
if x {}
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1003 = `Replace with strings.Contains
|
||||
|
||||
Before:
|
||||
|
||||
if strings.Index(x, y) != -1 {}
|
||||
|
||||
After:
|
||||
|
||||
if strings.Contains(x, y) {}
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1004 = `Replace with bytes.Equal
|
||||
|
||||
Before:
|
||||
|
||||
if bytes.Compare(x, y) == 0 {}
|
||||
|
||||
After:
|
||||
|
||||
if bytes.Equal(x, y) {}
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1005 = `Drop unnecessary use of the blank identifier
|
||||
|
||||
In many cases, assigning to the blank identifier is unnecessary.
|
||||
|
||||
Before:
|
||||
|
||||
for _ = range s {}
|
||||
x, _ = someMap[key]
|
||||
_ = <-ch
|
||||
|
||||
After:
|
||||
|
||||
for range s{}
|
||||
x = someMap[key]
|
||||
<-ch
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1006 = `Replace with for { ... }
|
||||
|
||||
For infinite loops, using for { ... } is the most idiomatic choice.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1007 = `Simplify regular expression by using raw string literal
|
||||
|
||||
Raw string literals use ` + "`" + ` instead of " and do not support any escape sequences. This means that the backslash (\) can be used freely, without the need of escaping.
|
||||
|
||||
Since regular expressions have their own escape sequences, raw strings can improve their readability.
|
||||
|
||||
Before:
|
||||
|
||||
regexp.Compile("\\A(\\w+) profile: total \\d+\\n\\z")
|
||||
|
||||
After:
|
||||
|
||||
regexp.Compile(` + "`" + `\A(\w+) profile: total \d+\n\z` + "`" + `)
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1008 = `Simplify returning boolean expression
|
||||
|
||||
Before:
|
||||
|
||||
if <expr> {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
After:
|
||||
|
||||
return <expr>
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1009 = `Omit redundant nil check on slices
|
||||
|
||||
The len function is defined for all slices, even nil ones, which have a length of zero. It is not necessary to check if a slice is not nil before checking that its length is not zero.
|
||||
|
||||
Before:
|
||||
|
||||
if x != nil && len(x) != 0 {}
|
||||
|
||||
After:
|
||||
|
||||
if len(x) != 0 {}
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1010 = `Omit default slice index
|
||||
|
||||
When slicing, the second index defaults to the length of the value, making s[n:len(s)] and s[n:] equivalent.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1011 = `Use a single append to concatenate two slices
|
||||
|
||||
Before:
|
||||
|
||||
for _, e := range y {
|
||||
x = append(x, e)
|
||||
}
|
||||
|
||||
After:
|
||||
|
||||
x = append(x, y...)
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1012 = `Replace with time.Since(x)
|
||||
|
||||
The time.Since helper has the same effect as using time.Now().Sub(x) but is easier to read.
|
||||
|
||||
Before:
|
||||
|
||||
time.Now().Sub(x)
|
||||
|
||||
After:
|
||||
|
||||
time.Since(x)
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1016 = `Use a type conversion
|
||||
|
||||
Two struct types with identical fields can be converted between each other. In older versions of Go, the fields had to have identical struct tags. Since Go 1.8, however, struct tags are ignored during conversions. It is thus not necessary to manually copy every field individually.
|
||||
|
||||
Before:
|
||||
|
||||
var x T1
|
||||
y := T2{
|
||||
Field1: x.Field1,
|
||||
Field2: x.Field2,
|
||||
}
|
||||
|
||||
After:
|
||||
|
||||
var x T1
|
||||
y := T2(x)
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1017 = `Replace with strings.TrimPrefix
|
||||
|
||||
Instead of using strings.HasPrefix and manual slicing, use the strings.TrimPrefix function. If the string doesn't start with the prefix, the original string will be returned. Using strings.TrimPrefix reduces complexity, and avoids common bugs, such as off-by-one mistakes.
|
||||
|
||||
Before:
|
||||
|
||||
if strings.HasPrefix(str, prefix) {
|
||||
str = str[len(prefix):]
|
||||
}
|
||||
|
||||
After:
|
||||
|
||||
str = strings.TrimPrefix(str, prefix)
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1018 = `Replace with copy()
|
||||
|
||||
copy() permits using the same source and destination slice, even with overlapping ranges. This makes it ideal for sliding elements in a slice.
|
||||
|
||||
Before:
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
bs[i] = bs[offset+i]
|
||||
}
|
||||
|
||||
After:
|
||||
|
||||
copy(bs[:n], bs[offset:])
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1019 = `Simplify make call
|
||||
|
||||
The make function has default values for the length and capacity arguments. For channels and maps, the length defaults to zero. Additionally, for slices the capacity defaults to the length.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1020 = `Omit redundant nil check in type assertion
|
||||
|
||||
Before:
|
||||
|
||||
if _, ok := i.(T); ok && i != nil {}
|
||||
|
||||
After:
|
||||
|
||||
if _, ok := i.(T); ok {}
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1021 = `Merge variable declaration and assignment
|
||||
|
||||
Before:
|
||||
|
||||
var x uint
|
||||
x = 1
|
||||
|
||||
After:
|
||||
|
||||
var x uint = 1
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
var docS1023 = `Omit redundant control flow
|
||||
|
||||
Functions that have no return value do not need a return statement as the final statement of the function.
|
||||
|
||||
Switches in Go do not have automatic fallthrough, unlike languages like C. It is not necessary to have a break statement as the final statement in a case block.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1024 = `Replace with time.Until(x)
|
||||
|
||||
The time.Until helper has the same effect as using x.Sub(time.Now()) but is easier to read.
|
||||
|
||||
Before:
|
||||
|
||||
x.Sub(time.Now())
|
||||
|
||||
After:
|
||||
|
||||
time.Until(x)
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1025 = `Don't use fmt.Sprintf("%s", x) unnecessarily
|
||||
|
||||
In many instances, there are easier and more efficient ways of getting a value's string representation. Whenever a value's underlying type is a string already, or the type has a String method, they should be used directly.
|
||||
|
||||
Given the following shared definitions
|
||||
|
||||
type T1 string
|
||||
type T2 int
|
||||
|
||||
func (T2) String() string { return "Hello, world" }
|
||||
|
||||
var x string
|
||||
var y T1
|
||||
var z T2
|
||||
|
||||
we can simplify the following
|
||||
|
||||
fmt.Sprintf("%s", x)
|
||||
fmt.Sprintf("%s", y)
|
||||
fmt.Sprintf("%s", z)
|
||||
|
||||
to
|
||||
|
||||
x
|
||||
string(y)
|
||||
z.String()
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1028 = `replace with fmt.Errorf
|
||||
|
||||
Before:
|
||||
|
||||
errors.New(fmt.Sprintf(...))
|
||||
|
||||
After:
|
||||
|
||||
fmt.Errorf(...)
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1029 = `Range over the string
|
||||
|
||||
Ranging over a string will yield byte offsets and runes. If the offset isn't used, this is functionally equivalent to converting the string to a slice of runes and ranging over that. Ranging directly over the string will be more performant, however, as it avoids allocating a new slice, the size of which depends on the length of the string.
|
||||
|
||||
Before:
|
||||
|
||||
for _, r := range []rune(s) {}
|
||||
|
||||
After:
|
||||
|
||||
for _, r := range s {}
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1030 = `Use bytes.Buffer.String or bytes.Buffer.Bytes
|
||||
|
||||
bytes.Buffer has both a String and a Bytes method. It is never necessary to use string(buf.Bytes()) or []byte(buf.String()) – simply use the other method.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1031 = `Omit redundant nil check around loop
|
||||
|
||||
You can use range on nil slices and maps, the loop will simply never execute. This makes an additional nil check around the loop unnecessary.
|
||||
|
||||
Before:
|
||||
|
||||
if s != nil {
|
||||
for _, x := range s {
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
After:
|
||||
|
||||
for _, x := range s {
|
||||
...
|
||||
}
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docS1032 = `Replace with sort.Ints(x), sort.Float64s(x), sort.Strings(x)
|
||||
|
||||
The sort.Ints, sort.Float64s and sort.Strings functions are easier to read than sort.Sort(sort.IntSlice(x)), sort.Sort(sort.Float64Slice(x)) and sort.Sort(sort.StringSlice(x)).
|
||||
|
||||
Before:
|
||||
|
||||
sort.Sort(sort.StringSlice(x))
|
||||
|
||||
After:
|
||||
|
||||
sort.Strings(x)
|
||||
|
||||
Available since
|
||||
2019.1
|
||||
`
|
1006
vendor/honnef.co/go/tools/simple/lint.go
vendored
1006
vendor/honnef.co/go/tools/simple/lint.go
vendored
File diff suppressed because it is too large
Load diff
17
vendor/honnef.co/go/tools/ssautil/ssautil.go
vendored
17
vendor/honnef.co/go/tools/ssautil/ssautil.go
vendored
|
@ -39,3 +39,20 @@ func Walk(b *ssa.BasicBlock, fn func(*ssa.BasicBlock) bool) {
|
|||
wl = append(wl, b.Succs...)
|
||||
}
|
||||
}
|
||||
|
||||
func Vararg(x *ssa.Slice) ([]ssa.Value, bool) {
|
||||
var out []ssa.Value
|
||||
slice, ok := x.X.(*ssa.Alloc)
|
||||
if !ok || slice.Comment != "varargs" {
|
||||
return nil, false
|
||||
}
|
||||
for _, ref := range *slice.Referrers() {
|
||||
idx, ok := ref.(*ssa.IndexAddr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
v := (*idx.Referrers())[0].(*ssa.Store).Val
|
||||
out = append(out, v)
|
||||
}
|
||||
return out, true
|
||||
}
|
||||
|
|
797
vendor/honnef.co/go/tools/staticcheck/doc.go
vendored
Normal file
797
vendor/honnef.co/go/tools/staticcheck/doc.go
vendored
Normal file
|
@ -0,0 +1,797 @@
|
|||
package staticcheck
|
||||
|
||||
var docSA1000 = `Invalid regular expression
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1001 = `Invalid template
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1002 = `Invalid format in time.Parse
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1003 = `Unsupported argument to functions in encoding/binary
|
||||
|
||||
The encoding/binary package can only serialize types with known sizes.
|
||||
This precludes the use of the 'int' and 'uint' types, as their sizes
|
||||
differ on different architectures. Furthermore, it doesn't support
|
||||
serializing maps, channels, strings, or functions.
|
||||
|
||||
Before Go 1.8, bool wasn't supported, either.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1004 = `Suspiciously small untyped constant in time.Sleep
|
||||
|
||||
The time.Sleep function takes a time.Duration as its only argument.
|
||||
Durations are expressed in nanoseconds. Thus, calling time.Sleep(1)
|
||||
will sleep for 1 nanosecond. This is a common source of bugs, as sleep
|
||||
functions in other languages often accept seconds or milliseconds.
|
||||
|
||||
The time package provides constants such as time.Second to express
|
||||
large durations. These can be combined with arithmetic to express
|
||||
arbitrary durations, for example '5 * time.Second' for 5 seconds.
|
||||
|
||||
If you truly meant to sleep for a tiny amount of time, use
|
||||
'n * time.Nanosecond" to signal to staticcheck that you did mean to sleep
|
||||
for some amount of nanoseconds.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1005 = `Invalid first argument to exec.Command
|
||||
|
||||
os/exec runs programs directly (using variants of the fork and exec
|
||||
system calls on Unix systems). This shouldn't be confused with running
|
||||
a command in a shell. The shell will allow for features such as input
|
||||
redirection, pipes, and general scripting. The shell is also
|
||||
responsible for splitting the user's input into a program name and its
|
||||
arguments. For example, the equivalent to
|
||||
|
||||
ls / /tmp
|
||||
|
||||
would be
|
||||
|
||||
exec.Command("ls", "/", "/tmp")
|
||||
|
||||
If you want to run a command in a shell, consider using something like
|
||||
the following – but be aware that not all systems, particularly
|
||||
Windows, will have a /bin/sh program:
|
||||
|
||||
exec.Command("/bin/sh", "-c", "ls | grep Awesome")
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1006 = `Printf with dynamic first argument and no further arguments
|
||||
|
||||
Using fmt.Printf with a dynamic first argument can lead to unexpected
|
||||
output. The first argument is a format string, where certain character
|
||||
combinations have special meaning. If, for example, a user were to
|
||||
enter a string such as
|
||||
|
||||
Interest rate: 5%
|
||||
|
||||
and you printed it with
|
||||
|
||||
fmt.Printf(s)
|
||||
|
||||
it would lead to the following output:
|
||||
|
||||
Interest rate: 5%!(NOVERB).
|
||||
|
||||
Similarly, forming the first parameter via string concatenation with
|
||||
user input should be avoided for the same reason. When printing user
|
||||
input, either use a variant of fmt.Print, or use the %s Printf verb
|
||||
and pass the string as an argument.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1007 = `Invalid URL in net/url.Parse
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1008 = `Non-canonical key in http.Header map
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1010 = `(*regexp.Regexp).FindAll called with n == 0, which will always return zero results
|
||||
|
||||
If n >= 0, the function returns at most n matches/submatches. To
|
||||
return all results, specify a negative number.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1011 = `Various methods in the strings package expect valid UTF-8, but invalid input is provided
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1012 = `A nil context.Context is being passed to a function, consider using context.TODO instead
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1013 = `io.Seeker.Seek is being called with the whence constant as the first argument, but it should be the second
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1014 = `Non-pointer value passed to Unmarshal or Decode
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1015 = `Using time.Tick in a way that will leak. Consider using time.NewTicker, and only use time.Tick in tests, commands and endless functions
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1016 = `Trapping a signal that cannot be trapped
|
||||
|
||||
Not all signals can be intercepted by a process. Speficially, on
|
||||
UNIX-like systems, the syscall.SIGKILL and syscall.SIGSTOP signals are
|
||||
never passed to the process, but instead handled directly by the
|
||||
kernel. It is therefore pointless to try and handle these signals.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1017 = `Channels used with os/signal.Notify should be buffered
|
||||
|
||||
The os/signal package uses non-blocking channel sends when delivering
|
||||
signals. If the receiving end of the channel isn't ready and the
|
||||
channel is either unbuffered or full, the signal will be dropped. To
|
||||
avoid missing signals, the channel should be buffered and of the
|
||||
appropriate size. For a channel used for notification of just one
|
||||
signal value, a buffer of size 1 is sufficient.
|
||||
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1018 = `strings.Replace called with n == 0, which does nothing
|
||||
|
||||
With n == 0, zero instances will be replaced. To replace all
|
||||
instances, use a negative number, or use strings.ReplaceAll.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1019 = `Using a deprecated function, variable, constant or field
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1020 = `Using an invalid host:port pair with a net.Listen-related function
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1021 = `Using bytes.Equal to compare two net.IP
|
||||
|
||||
A net.IP stores an IPv4 or IPv6 address as a slice of bytes. The
|
||||
length of the slice for an IPv4 address, however, can be either 4 or
|
||||
16 bytes long, using different ways of representing IPv4 addresses. In
|
||||
order to correctly compare two net.IPs, the net.IP.Equal method should
|
||||
be used, as it takes both representations into account.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1023 = `Modifying the buffer in an io.Writer implementation
|
||||
|
||||
Write must not modify the slice data, even temporarily.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1024 = `A string cutset contains duplicate characters, suggesting TrimPrefix or TrimSuffix should be used instead of TrimLeft or TrimRight
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA1025 = `It is not possible to use Reset's return value correctly
|
||||
|
||||
Available since
|
||||
2019.1
|
||||
`
|
||||
|
||||
var docSA1026 = `Cannot marshal channels or functions
|
||||
|
||||
Available since
|
||||
Unreleased
|
||||
`
|
||||
|
||||
var docSA1027 = `Atomic access to 64-bit variable must be 64-bit aligned
|
||||
|
||||
On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility to
|
||||
arrange for 64-bit alignment of 64-bit words accessed atomically. The
|
||||
first word in a variable or in an allocated struct, array, or slice
|
||||
can be relied upon to be 64-bit aligned.
|
||||
|
||||
You can use the structlayout tool to inspect the alignment of fields
|
||||
in a struct.
|
||||
|
||||
Available since
|
||||
Unreleased
|
||||
`
|
||||
|
||||
var docSA2000 = `sync.WaitGroup.Add called inside the goroutine, leading to a race condition
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA2001 = `Empty critical section, did you mean to defer the unlock?
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA2002 = `Called testing.T.FailNow or SkipNow in a goroutine, which isn't allowed
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA2003 = `Deferred Lock right after locking, likely meant to defer Unlock instead
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA3000 = `TestMain doesn't call os.Exit, hiding test failures
|
||||
|
||||
Test executables (and in turn 'go test') exit with a non-zero status
|
||||
code if any tests failed. When specifying your own TestMain function,
|
||||
it is your responsibility to arrange for this, by calling os.Exit with
|
||||
the correct code. The correct code is returned by (*testing.M).Run, so
|
||||
the usual way of implementing TestMain is to end it with
|
||||
os.Exit(m.Run()).
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA3001 = `Assigning to b.N in benchmarks distorts the results
|
||||
|
||||
The testing package dynamically sets b.N to improve the reliability of
|
||||
benchmarks and uses it in computations to determine the duration of a
|
||||
single operation. Benchmark code must not alter b.N as this would
|
||||
falsify results.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4000 = `Boolean expression has identical expressions on both sides
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4001 = `&*x gets simplified to x, it does not copy x
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4002 = `Comparing strings with known different sizes has predictable results
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4003 = `Comparing unsigned values against negative values is pointless
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4004 = `The loop exits unconditionally after one iteration
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4005 = `Field assignment that will never be observed. Did you mean to use a pointer receiver?
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4006 = `A value assigned to a variable is never read before being overwritten. Forgotten error check or dead code?
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4008 = `The variable in the loop condition never changes, are you incrementing the wrong variable?
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4009 = `A function argument is overwritten before its first use
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4010 = `The result of append will never be observed anywhere
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4011 = `Break statement with no effect. Did you mean to break out of an outer loop?
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4012 = `Comparing a value against NaN even though no value is equal to NaN
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4013 = `Negating a boolean twice (!!b) is the same as writing b. This is either redundant, or a typo.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4014 = `An if/else if chain has repeated conditions and no side-effects; if the condition didn't match the first time, it won't match the second time, either
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4015 = `Calling functions like math.Ceil on floats converted from integers doesn't do anything useful
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4016 = `Certain bitwise operations, such as x ^ 0, do not do anything useful
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4017 = `A pure function's return value is discarded, making the call pointless
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4018 = `Self-assignment of variables
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4019 = `Multiple, identical build constraints in the same file
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA4020 = `Unreachable case clause in a type switch
|
||||
|
||||
In a type switch like the following
|
||||
|
||||
type T struct{}
|
||||
func (T) Read(b []byte) (int, error) { return 0, nil }
|
||||
|
||||
var v interface{} = T{}
|
||||
|
||||
switch v.(type) {
|
||||
case io.Reader:
|
||||
// ...
|
||||
case T:
|
||||
// unreachable
|
||||
}
|
||||
|
||||
the second case clause can never be reached because T implements
|
||||
io.Reader and case clauses are evaluated in source order.
|
||||
|
||||
Another example:
|
||||
|
||||
type T struct{}
|
||||
func (T) Read(b []byte) (int, error) { return 0, nil }
|
||||
func (T) Close() error { return nil }
|
||||
|
||||
var v interface{} = T{}
|
||||
|
||||
switch v.(type) {
|
||||
case io.Reader:
|
||||
// ...
|
||||
case io.ReadCloser:
|
||||
// unreachable
|
||||
}
|
||||
|
||||
Even though T has a Close method and thus implements io.ReadCloser,
|
||||
io.Reader will always match first. The method set of io.Reader is a
|
||||
subset of io.ReadCloser. Thus it is impossible to match the second
|
||||
case without mtching the first case.
|
||||
|
||||
|
||||
Structurally equivalent interfaces
|
||||
|
||||
A special case of the previous example are structurally identical
|
||||
interfaces. Given these declarations
|
||||
|
||||
type T error
|
||||
type V error
|
||||
|
||||
func doSomething() error {
|
||||
err, ok := doAnotherThing()
|
||||
if ok {
|
||||
return T(err)
|
||||
}
|
||||
|
||||
return U(err)
|
||||
}
|
||||
|
||||
the following type switch will have an unreachable case clause:
|
||||
|
||||
switch doSomething().(type) {
|
||||
case T:
|
||||
// ...
|
||||
case V:
|
||||
// unreachable
|
||||
}
|
||||
|
||||
T will always match before V because they are structurally equivalent
|
||||
and therefore doSomething()'s return value implements both.
|
||||
|
||||
Available since
|
||||
Unreleased
|
||||
`
|
||||
|
||||
var docSA4021 = `x = append(y) is equivalent to x = y
|
||||
|
||||
Available since
|
||||
Unreleased
|
||||
`
|
||||
|
||||
var docSA5000 = `Assignment to nil map
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA5001 = `Defering Close before checking for a possible error
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA5002 = `The empty for loop (for {}) spins and can block the scheduler
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA5003 = `Defers in infinite loops will never execute
|
||||
|
||||
Defers are scoped to the surrounding function, not the surrounding
|
||||
block. In a function that never returns, i.e. one containing an
|
||||
infinite loop, defers will never execute.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA5004 = `for { select { ... with an empty default branch spins
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA5005 = `The finalizer references the finalized object, preventing garbage collection
|
||||
|
||||
A finalizer is a function associated with an object that runs when the
|
||||
garbage collector is ready to collect said object, that is when the
|
||||
object is no longer referenced by anything.
|
||||
|
||||
If the finalizer references the object, however, it will always remain
|
||||
as the final reference to that object, preventing the garbage
|
||||
collector from collecting the object. The finalizer will never run,
|
||||
and the object will never be collected, leading to a memory leak. That
|
||||
is why the finalizer should instead use its first argument to operate
|
||||
on the object. That way, the number of references can temporarily go
|
||||
to zero before the object is being passed to the finalizer.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA5006 = `Slice index out of bounds
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA5007 = `Infinite recursive call
|
||||
|
||||
A function that calls itself recursively needs to have an exit
|
||||
condition. Otherwise it will recurse forever, until the system runs
|
||||
out of memory.
|
||||
|
||||
This issue can be caused by simple bugs such as forgetting to add an
|
||||
exit condition. It can also happen "on purpose". Some languages have
|
||||
tail call optimization which makes certain infinite recursive calls
|
||||
safe to use. Go, however, does not implement TCO, and as such a loop
|
||||
should be used instead.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA6000 = `Using regexp.Match or related in a loop, should use regexp.Compile
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA6001 = `Missing an optimization opportunity when indexing maps by byte slices
|
||||
|
||||
Map keys must be comparable, which precludes the use of byte slices.
|
||||
This usually leads to using string keys and converting byte slices to
|
||||
strings.
|
||||
|
||||
Normally, a conversion of a byte slice to a string needs to copy the data and
|
||||
causes allocations. The compiler, however, recognizes m[string(b)] and
|
||||
uses the data of b directly, without copying it, because it knows that
|
||||
the data can't change during the map lookup. This leads to the
|
||||
counter-intuitive situation that
|
||||
|
||||
k := string(b)
|
||||
println(m[k])
|
||||
println(m[k])
|
||||
|
||||
will be less efficient than
|
||||
|
||||
println(m[string(b)])
|
||||
println(m[string(b)])
|
||||
|
||||
because the first version needs to copy and allocate, while the second
|
||||
one does not.
|
||||
|
||||
For some history on this optimization, check out commit
|
||||
f5f5a8b6209f84961687d993b93ea0d397f5d5bf in the Go repository.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA6002 = `Storing non-pointer values in sync.Pool allocates memory
|
||||
|
||||
A sync.Pool is used to avoid unnecessary allocations and reduce the
|
||||
amount of work the garbage collector has to do.
|
||||
|
||||
When passing a value that is not a pointer to a function that accepts
|
||||
an interface, the value needs to be placed on the heap, which means an
|
||||
additional allocation. Slices are a common thing to put in sync.Pools,
|
||||
and they're structs with 3 fields (length, capacity, and a pointer to
|
||||
an array). In order to avoid the extra allocation, one should store a
|
||||
pointer to the slice instead.
|
||||
|
||||
See the comments on https://go-review.googlesource.com/c/go/+/24371
|
||||
that discuss this problem.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA6003 = `Converting a string to a slice of runes before ranging over it
|
||||
|
||||
You may want to loop over the runes in a string. Instead of converting
|
||||
the string to a slice of runes and looping over that, you can loop
|
||||
over the string itself. That is,
|
||||
|
||||
for _, r := range s {}
|
||||
|
||||
and
|
||||
|
||||
for _, r := range []rune(s) {}
|
||||
|
||||
will yield the same values. The first version, however, will be faster
|
||||
and avoid unnecessary memory allocations.
|
||||
|
||||
Do note that if you are interested in the indices, ranging over a
|
||||
string and over a slice of runes will yield different indices. The
|
||||
first one yields byte offsets, while the second one yields indices in
|
||||
the slice of runes.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA6005 = `Inefficient string comparison with strings.ToLower or strings.ToUpper
|
||||
|
||||
Converting two strings to the same case and comparing them like so
|
||||
|
||||
if strings.ToLower(s1) == strings.ToLower(s2) {
|
||||
...
|
||||
}
|
||||
|
||||
is significantly more expensive than comparing them with
|
||||
strings.EqualFold(s1, s2). This is due to memory usage as well as
|
||||
computational complexity.
|
||||
|
||||
strings.ToLower will have to allocate memory for the new strings, as
|
||||
well as convert both strings fully, even if they differ on the very
|
||||
first byte. strings.EqualFold, on the other hand, compares the strings
|
||||
one character at a time. It doesn't need to create two intermediate
|
||||
strings and can return as soon as the first non-matching character has
|
||||
been found.
|
||||
|
||||
For a more in-depth explanation of this issue, see
|
||||
https://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/
|
||||
|
||||
Available since
|
||||
Unreleased
|
||||
`
|
||||
|
||||
var docSA9001 = `Defers in 'for range' loops may not run when you expect them to
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA9002 = `Using a non-octal os.FileMode that looks like it was meant to be in octal.
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA9003 = `Empty body in an if or else branch
|
||||
|
||||
Available since
|
||||
2017.1
|
||||
`
|
||||
|
||||
var docSA9004 = `Only the first constant has an explicit type
|
||||
|
||||
In a constant declaration such as the following:
|
||||
|
||||
const (
|
||||
First byte = 1
|
||||
Second = 2
|
||||
)
|
||||
|
||||
the constant Second does not have the same type as the constant First.
|
||||
This construct shouldn't be confused with
|
||||
|
||||
const (
|
||||
First byte = iota
|
||||
Second
|
||||
)
|
||||
|
||||
where First and Second do indeed have the same type. The type is only
|
||||
passed on when no explicit value is assigned to the constant.
|
||||
|
||||
When declaring enumerations with explicit values it is therefore
|
||||
important not to write
|
||||
|
||||
const (
|
||||
EnumFirst EnumType = 1
|
||||
EnumSecond = 2
|
||||
EnumThird = 3
|
||||
)
|
||||
|
||||
This discrepancy in types can cause various confusing behaviors and
|
||||
bugs.
|
||||
|
||||
|
||||
Wrong type in variable declarations
|
||||
|
||||
The most obvious issue with such incorrect enumerations expresses
|
||||
itself as a compile error:
|
||||
|
||||
package pkg
|
||||
|
||||
const (
|
||||
EnumFirst uint8 = 1
|
||||
EnumSecond = 2
|
||||
)
|
||||
|
||||
func fn(useFirst bool) {
|
||||
x := EnumSecond
|
||||
if useFirst {
|
||||
x = EnumFirst
|
||||
}
|
||||
}
|
||||
|
||||
fails to compile with
|
||||
|
||||
./const.go:11:5: cannot use EnumFirst (type uint8) as type int in assignment
|
||||
|
||||
|
||||
Losing method sets
|
||||
|
||||
A more subtle issue occurs with types that have methods and optional
|
||||
interfaces. Consider the following:
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Enum int
|
||||
|
||||
func (e Enum) String() string {
|
||||
return "an enum"
|
||||
}
|
||||
|
||||
const (
|
||||
EnumFirst Enum = 1
|
||||
EnumSecond = 2
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println(EnumFirst)
|
||||
fmt.Println(EnumSecond)
|
||||
}
|
||||
|
||||
This code will output
|
||||
|
||||
an enum
|
||||
2
|
||||
|
||||
as EnumSecond has no explicit type, and thus defaults to int.
|
||||
|
||||
Available since
|
||||
2019.1
|
||||
`
|
||||
|
||||
var docSA9005 = `Trying to marshal a struct with no public fields nor custom marshaling
|
||||
|
||||
The encoding/json and encoding/xml packages only operate on exported
|
||||
fields in structs, not unexported ones. It is usually an error to try
|
||||
to (un)marshal structs that only consist of unexported fields.
|
||||
|
||||
This check will not flag calls involving types that define custom
|
||||
marshaling behavior, e.g. via MarshalJSON methods. It will also not
|
||||
flag empty structs.
|
||||
|
||||
Available since
|
||||
Unreleased
|
||||
`
|
1286
vendor/honnef.co/go/tools/staticcheck/lint.go
vendored
1286
vendor/honnef.co/go/tools/staticcheck/lint.go
vendored
File diff suppressed because it is too large
Load diff
58
vendor/honnef.co/go/tools/staticcheck/structtag.go
vendored
Normal file
58
vendor/honnef.co/go/tools/staticcheck/structtag.go
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Copyright 2019 Dominik Honnef. All rights reserved.
|
||||
|
||||
package staticcheck
|
||||
|
||||
import "strconv"
|
||||
|
||||
func parseStructTag(tag string) (map[string][]string, error) {
|
||||
// FIXME(dh): detect missing closing quote
|
||||
out := map[string][]string{}
|
||||
|
||||
for tag != "" {
|
||||
// Skip leading space.
|
||||
i := 0
|
||||
for i < len(tag) && tag[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
tag = tag[i:]
|
||||
if tag == "" {
|
||||
break
|
||||
}
|
||||
|
||||
// Scan to colon. A space, a quote or a control character is a syntax error.
|
||||
// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
|
||||
// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
|
||||
// as it is simpler to inspect the tag's bytes than the tag's runes.
|
||||
i = 0
|
||||
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
|
||||
i++
|
||||
}
|
||||
if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
|
||||
break
|
||||
}
|
||||
name := string(tag[:i])
|
||||
tag = tag[i+1:]
|
||||
|
||||
// Scan quoted string to find value.
|
||||
i = 1
|
||||
for i < len(tag) && tag[i] != '"' {
|
||||
if tag[i] == '\\' {
|
||||
i++
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i >= len(tag) {
|
||||
break
|
||||
}
|
||||
qvalue := string(tag[:i+1])
|
||||
tag = tag[i+1:]
|
||||
|
||||
value, err := strconv.Unquote(qvalue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[name] = append(out[name], value)
|
||||
}
|
||||
return out, nil
|
||||
}
|
|
@ -12,6 +12,7 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"honnef.co/go/tools/lint"
|
||||
"honnef.co/go/tools/ssa"
|
||||
)
|
||||
|
||||
|
@ -291,7 +292,7 @@ func BuildGraph(f *ssa.Function) *Graph {
|
|||
case *ssa.Call:
|
||||
if static := ins.Common().StaticCallee(); static != nil {
|
||||
if fn, ok := static.Object().(*types.Func); ok {
|
||||
switch fn.FullName() {
|
||||
switch lint.FuncName(fn) {
|
||||
case "bytes.Index", "bytes.IndexAny", "bytes.IndexByte",
|
||||
"bytes.IndexFunc", "bytes.IndexRune", "bytes.LastIndex",
|
||||
"bytes.LastIndexAny", "bytes.LastIndexByte", "bytes.LastIndexFunc",
|
||||
|
|
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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
25
vendor/honnef.co/go/tools/unused/implements.go
vendored
25
vendor/honnef.co/go/tools/unused/implements.go
vendored
|
@ -37,43 +37,46 @@ func sameId(obj types.Object, pkg *types.Package, name string) bool {
|
|||
return pkg.Path() == obj.Pkg().Path()
|
||||
}
|
||||
|
||||
func (c *Checker) implements(V types.Type, T *types.Interface) bool {
|
||||
func (g *Graph) implements(V types.Type, T *types.Interface, msV *types.MethodSet) ([]*types.Selection, bool) {
|
||||
// fast path for common case
|
||||
if T.Empty() {
|
||||
return true
|
||||
return nil, true
|
||||
}
|
||||
|
||||
if ityp, _ := V.Underlying().(*types.Interface); ityp != nil {
|
||||
// TODO(dh): is this code reachable?
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
m := T.Method(i)
|
||||
_, obj := lookupMethod(ityp, m.Pkg(), m.Name())
|
||||
switch {
|
||||
case obj == nil:
|
||||
return false
|
||||
return nil, false
|
||||
case !types.Identical(obj.Type(), m.Type()):
|
||||
return false
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return true
|
||||
return nil, true
|
||||
}
|
||||
|
||||
// A concrete type implements T if it implements all methods of T.
|
||||
ms := c.msCache.MethodSet(V)
|
||||
var sels []*types.Selection
|
||||
for i := 0; i < T.NumMethods(); i++ {
|
||||
m := T.Method(i)
|
||||
sel := ms.Lookup(m.Pkg(), m.Name())
|
||||
sel := msV.Lookup(m.Pkg(), m.Name())
|
||||
if sel == nil {
|
||||
return false
|
||||
return nil, false
|
||||
}
|
||||
|
||||
f, _ := sel.Obj().(*types.Func)
|
||||
if f == nil {
|
||||
return false
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if !types.Identical(f.Type(), m.Type()) {
|
||||
return false
|
||||
return nil, false
|
||||
}
|
||||
|
||||
sels = append(sels, sel)
|
||||
}
|
||||
return true
|
||||
return sels, true
|
||||
}
|
||||
|
|
2623
vendor/honnef.co/go/tools/unused/unused.go
vendored
2623
vendor/honnef.co/go/tools/unused/unused.go
vendored
File diff suppressed because it is too large
Load diff
5
vendor/modules.txt
vendored
5
vendor/modules.txt
vendored
|
@ -180,6 +180,7 @@ golang.org/x/text/unicode/bidi
|
|||
golang.org/x/tools/go/loader
|
||||
golang.org/x/tools/go/ast/astutil
|
||||
golang.org/x/tools/go/gcexportdata
|
||||
golang.org/x/tools/go/ast/inspector
|
||||
golang.org/x/tools/go/packages
|
||||
golang.org/x/tools/go/types/typeutil
|
||||
golang.org/x/tools/go/buildutil
|
||||
|
@ -200,7 +201,7 @@ gopkg.in/gomail.v2
|
|||
gopkg.in/testfixtures.v2
|
||||
# gopkg.in/yaml.v2 v2.2.2
|
||||
gopkg.in/yaml.v2
|
||||
# honnef.co/go/tools v0.0.0-20190215041234-466a0476246c
|
||||
# honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a
|
||||
honnef.co/go/tools/cmd/staticcheck
|
||||
honnef.co/go/tools/lint
|
||||
honnef.co/go/tools/lint/lintutil
|
||||
|
@ -218,8 +219,10 @@ honnef.co/go/tools/internal/sharedcheck
|
|||
honnef.co/go/tools/lint/lintdsl
|
||||
honnef.co/go/tools/deprecated
|
||||
honnef.co/go/tools/functions
|
||||
honnef.co/go/tools/printf
|
||||
honnef.co/go/tools/ssautil
|
||||
honnef.co/go/tools/staticcheck/vrp
|
||||
honnef.co/go/tools/go/types/typeutil
|
||||
honnef.co/go/tools/callgraph
|
||||
honnef.co/go/tools/callgraph/static
|
||||
# src.techknowlogick.com/xormigrate v0.0.0-20190321151057-24497c23c09c
|
||||
|
|
Loading…
Reference in a new issue