mirror of
https://github.com/DNSCrypt/dnscrypt-proxy.git
synced 2025-04-06 22:57:37 +03:00
Update deps; sync vendor
This commit is contained in:
parent
9373cc7162
commit
c08852feb1
76 changed files with 4193 additions and 1014 deletions
37
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
generated
vendored
37
vendor/golang.org/x/tools/go/analysis/passes/asmdecl/asmdecl.go
generated
vendored
|
@ -51,6 +51,11 @@ type asmArch struct {
|
|||
bigEndian bool
|
||||
stack string
|
||||
lr bool
|
||||
// retRegs is a list of registers for return value in register ABI (ABIInternal).
|
||||
// For now, as we only check whether we write to any result, here we only need to
|
||||
// include the first integer register and first floating-point register. Accessing
|
||||
// any of them counts as writing to result.
|
||||
retRegs []string
|
||||
// calculated during initialization
|
||||
sizes types.Sizes
|
||||
intSize int
|
||||
|
@ -79,8 +84,8 @@ type asmVar struct {
|
|||
var (
|
||||
asmArch386 = asmArch{name: "386", bigEndian: false, stack: "SP", lr: false}
|
||||
asmArchArm = asmArch{name: "arm", bigEndian: false, stack: "R13", lr: true}
|
||||
asmArchArm64 = asmArch{name: "arm64", bigEndian: false, stack: "RSP", lr: true}
|
||||
asmArchAmd64 = asmArch{name: "amd64", bigEndian: false, stack: "SP", lr: false}
|
||||
asmArchArm64 = asmArch{name: "arm64", bigEndian: false, stack: "RSP", lr: true, retRegs: []string{"R0", "F0"}}
|
||||
asmArchAmd64 = asmArch{name: "amd64", bigEndian: false, stack: "SP", lr: false, retRegs: []string{"AX", "X0"}}
|
||||
asmArchMips = asmArch{name: "mips", bigEndian: true, stack: "R29", lr: true}
|
||||
asmArchMipsLE = asmArch{name: "mipsle", bigEndian: false, stack: "R29", lr: true}
|
||||
asmArchMips64 = asmArch{name: "mips64", bigEndian: true, stack: "R29", lr: true}
|
||||
|
@ -137,7 +142,7 @@ var (
|
|||
asmSP = re(`[^+\-0-9](([0-9]+)\(([A-Z0-9]+)\))`)
|
||||
asmOpcode = re(`^\s*(?:[A-Z0-9a-z_]+:)?\s*([A-Z]+)\s*([^,]*)(?:,\s*(.*))?`)
|
||||
ppc64Suff = re(`([BHWD])(ZU|Z|U|BR)?$`)
|
||||
abiSuff = re(`^(.+)<ABI.+>$`)
|
||||
abiSuff = re(`^(.+)<(ABI.+)>$`)
|
||||
)
|
||||
|
||||
func run(pass *analysis.Pass) (interface{}, error) {
|
||||
|
@ -185,6 +190,7 @@ Files:
|
|||
var (
|
||||
fn *asmFunc
|
||||
fnName string
|
||||
abi string
|
||||
localSize, argSize int
|
||||
wroteSP bool
|
||||
noframe bool
|
||||
|
@ -195,18 +201,22 @@ Files:
|
|||
flushRet := func() {
|
||||
if fn != nil && fn.vars["ret"] != nil && !haveRetArg && len(retLine) > 0 {
|
||||
v := fn.vars["ret"]
|
||||
resultStr := fmt.Sprintf("%d-byte ret+%d(FP)", v.size, v.off)
|
||||
if abi == "ABIInternal" {
|
||||
resultStr = "result register"
|
||||
}
|
||||
for _, line := range retLine {
|
||||
pass.Reportf(analysisutil.LineStart(tf, line), "[%s] %s: RET without writing to %d-byte ret+%d(FP)", arch, fnName, v.size, v.off)
|
||||
pass.Reportf(analysisutil.LineStart(tf, line), "[%s] %s: RET without writing to %s", arch, fnName, resultStr)
|
||||
}
|
||||
}
|
||||
retLine = nil
|
||||
}
|
||||
trimABI := func(fnName string) string {
|
||||
trimABI := func(fnName string) (string, string) {
|
||||
m := abiSuff.FindStringSubmatch(fnName)
|
||||
if m != nil {
|
||||
return m[1]
|
||||
return m[1], m[2]
|
||||
}
|
||||
return fnName
|
||||
return fnName, ""
|
||||
}
|
||||
for lineno, line := range lines {
|
||||
lineno++
|
||||
|
@ -273,11 +283,12 @@ Files:
|
|||
// log.Printf("%s:%d: [%s] cannot check cross-package assembly function: %s is in package %s", fname, lineno, arch, fnName, pkgPath)
|
||||
fn = nil
|
||||
fnName = ""
|
||||
abi = ""
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Trim off optional ABI selector.
|
||||
fnName := trimABI(fnName)
|
||||
fnName, abi = trimABI(fnName)
|
||||
flag := m[3]
|
||||
fn = knownFunc[fnName][arch]
|
||||
if fn != nil {
|
||||
|
@ -305,6 +316,7 @@ Files:
|
|||
flushRet()
|
||||
fn = nil
|
||||
fnName = ""
|
||||
abi = ""
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -335,6 +347,15 @@ Files:
|
|||
haveRetArg = true
|
||||
}
|
||||
|
||||
if abi == "ABIInternal" && !haveRetArg {
|
||||
for _, reg := range archDef.retRegs {
|
||||
if strings.Contains(line, reg) {
|
||||
haveRetArg = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, m := range asmSP.FindAllStringSubmatch(line, -1) {
|
||||
if m[3] != archDef.stack || wroteSP || noframe {
|
||||
continue
|
||||
|
|
75
vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go
generated
vendored
75
vendor/golang.org/x/tools/go/analysis/passes/composite/composite.go
generated
vendored
|
@ -14,6 +14,7 @@ import (
|
|||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
const Doc = `check for unkeyed composite literals
|
||||
|
@ -67,41 +68,61 @@ func run(pass *analysis.Pass) (interface{}, error) {
|
|||
// skip whitelisted types
|
||||
return
|
||||
}
|
||||
under := typ.Underlying()
|
||||
for {
|
||||
ptr, ok := under.(*types.Pointer)
|
||||
if !ok {
|
||||
break
|
||||
var structuralTypes []types.Type
|
||||
switch typ := typ.(type) {
|
||||
case *typeparams.TypeParam:
|
||||
terms, err := typeparams.StructuralTerms(typ)
|
||||
if err != nil {
|
||||
return // invalid type
|
||||
}
|
||||
under = ptr.Elem().Underlying()
|
||||
}
|
||||
if _, ok := under.(*types.Struct); !ok {
|
||||
// skip non-struct composite literals
|
||||
return
|
||||
}
|
||||
if isLocalType(pass, typ) {
|
||||
// allow unkeyed locally defined composite literal
|
||||
return
|
||||
}
|
||||
|
||||
// check if the CompositeLit contains an unkeyed field
|
||||
allKeyValue := true
|
||||
for _, e := range cl.Elts {
|
||||
if _, ok := e.(*ast.KeyValueExpr); !ok {
|
||||
allKeyValue = false
|
||||
break
|
||||
for _, term := range terms {
|
||||
structuralTypes = append(structuralTypes, term.Type())
|
||||
}
|
||||
default:
|
||||
structuralTypes = append(structuralTypes, typ)
|
||||
}
|
||||
if allKeyValue {
|
||||
// all the composite literal fields are keyed
|
||||
for _, typ := range structuralTypes {
|
||||
under := deref(typ.Underlying())
|
||||
if _, ok := under.(*types.Struct); !ok {
|
||||
// skip non-struct composite literals
|
||||
continue
|
||||
}
|
||||
if isLocalType(pass, typ) {
|
||||
// allow unkeyed locally defined composite literal
|
||||
continue
|
||||
}
|
||||
|
||||
// check if the CompositeLit contains an unkeyed field
|
||||
allKeyValue := true
|
||||
for _, e := range cl.Elts {
|
||||
if _, ok := e.(*ast.KeyValueExpr); !ok {
|
||||
allKeyValue = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if allKeyValue {
|
||||
// all the composite literal fields are keyed
|
||||
continue
|
||||
}
|
||||
|
||||
pass.ReportRangef(cl, "%s composite literal uses unkeyed fields", typeName)
|
||||
return
|
||||
}
|
||||
|
||||
pass.ReportRangef(cl, "%s composite literal uses unkeyed fields", typeName)
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func deref(typ types.Type) types.Type {
|
||||
for {
|
||||
ptr, ok := typ.(*types.Pointer)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
typ = ptr.Elem().Underlying()
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
func isLocalType(pass *analysis.Pass, typ types.Type) bool {
|
||||
switch x := typ.(type) {
|
||||
case *types.Struct:
|
||||
|
@ -112,6 +133,8 @@ func isLocalType(pass *analysis.Pass, typ types.Type) bool {
|
|||
case *types.Named:
|
||||
// names in package foo are local to foo_test too
|
||||
return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
|
||||
case *typeparams.TypeParam:
|
||||
return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
75
vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
generated
vendored
75
vendor/golang.org/x/tools/go/analysis/passes/copylock/copylock.go
generated
vendored
|
@ -17,6 +17,7 @@ import (
|
|||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
const Doc = `check for locks erroneously passed by value
|
||||
|
@ -145,7 +146,7 @@ func checkCopyLocksCallExpr(pass *analysis.Pass, ce *ast.CallExpr) {
|
|||
func checkCopyLocksFunc(pass *analysis.Pass, name string, recv *ast.FieldList, typ *ast.FuncType) {
|
||||
if recv != nil && len(recv.List) > 0 {
|
||||
expr := recv.List[0].Type
|
||||
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
|
||||
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type, nil); path != nil {
|
||||
pass.ReportRangef(expr, "%s passes lock by value: %v", name, path)
|
||||
}
|
||||
}
|
||||
|
@ -153,7 +154,7 @@ func checkCopyLocksFunc(pass *analysis.Pass, name string, recv *ast.FieldList, t
|
|||
if typ.Params != nil {
|
||||
for _, field := range typ.Params.List {
|
||||
expr := field.Type
|
||||
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type); path != nil {
|
||||
if path := lockPath(pass.Pkg, pass.TypesInfo.Types[expr].Type, nil); path != nil {
|
||||
pass.ReportRangef(expr, "%s passes lock by value: %v", name, path)
|
||||
}
|
||||
}
|
||||
|
@ -199,12 +200,12 @@ func checkCopyLocksRangeVar(pass *analysis.Pass, rtok token.Token, e ast.Expr) {
|
|||
if typ == nil {
|
||||
return
|
||||
}
|
||||
if path := lockPath(pass.Pkg, typ); path != nil {
|
||||
if path := lockPath(pass.Pkg, typ, nil); path != nil {
|
||||
pass.Reportf(e.Pos(), "range var %s copies lock: %v", analysisutil.Format(pass.Fset, e), path)
|
||||
}
|
||||
}
|
||||
|
||||
type typePath []types.Type
|
||||
type typePath []string
|
||||
|
||||
// String pretty-prints a typePath.
|
||||
func (path typePath) String() string {
|
||||
|
@ -215,7 +216,7 @@ func (path typePath) String() string {
|
|||
fmt.Fprint(&buf, " contains ")
|
||||
}
|
||||
// The human-readable path is in reverse order, outermost to innermost.
|
||||
fmt.Fprint(&buf, path[n-i-1].String())
|
||||
fmt.Fprint(&buf, path[n-i-1])
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
@ -234,16 +235,57 @@ func lockPathRhs(pass *analysis.Pass, x ast.Expr) typePath {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
return lockPath(pass.Pkg, pass.TypesInfo.Types[x].Type)
|
||||
return lockPath(pass.Pkg, pass.TypesInfo.Types[x].Type, nil)
|
||||
}
|
||||
|
||||
// lockPath returns a typePath describing the location of a lock value
|
||||
// contained in typ. If there is no contained lock, it returns nil.
|
||||
func lockPath(tpkg *types.Package, typ types.Type) typePath {
|
||||
//
|
||||
// The seenTParams map is used to short-circuit infinite recursion via type
|
||||
// parameters.
|
||||
func lockPath(tpkg *types.Package, typ types.Type, seenTParams map[*typeparams.TypeParam]bool) typePath {
|
||||
if typ == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if tpar, ok := typ.(*typeparams.TypeParam); ok {
|
||||
if seenTParams == nil {
|
||||
// Lazily allocate seenTParams, since the common case will not involve
|
||||
// any type parameters.
|
||||
seenTParams = make(map[*typeparams.TypeParam]bool)
|
||||
}
|
||||
if seenTParams[tpar] {
|
||||
return nil
|
||||
}
|
||||
seenTParams[tpar] = true
|
||||
terms, err := typeparams.StructuralTerms(tpar)
|
||||
if err != nil {
|
||||
return nil // invalid type
|
||||
}
|
||||
for _, term := range terms {
|
||||
subpath := lockPath(tpkg, term.Type(), seenTParams)
|
||||
if len(subpath) > 0 {
|
||||
if term.Tilde() {
|
||||
// Prepend a tilde to our lock path entry to clarify the resulting
|
||||
// diagnostic message. Consider the following example:
|
||||
//
|
||||
// func _[Mutex interface{ ~sync.Mutex; M() }](m Mutex) {}
|
||||
//
|
||||
// Here the naive error message will be something like "passes lock
|
||||
// by value: Mutex contains sync.Mutex". This is misleading because
|
||||
// the local type parameter doesn't actually contain sync.Mutex,
|
||||
// which lacks the M method.
|
||||
//
|
||||
// With tilde, it is clearer that the containment is via an
|
||||
// approximation element.
|
||||
subpath[len(subpath)-1] = "~" + subpath[len(subpath)-1]
|
||||
}
|
||||
return append(subpath, typ.String())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for {
|
||||
atyp, ok := typ.Underlying().(*types.Array)
|
||||
if !ok {
|
||||
|
@ -252,6 +294,17 @@ func lockPath(tpkg *types.Package, typ types.Type) typePath {
|
|||
typ = atyp.Elem()
|
||||
}
|
||||
|
||||
ttyp, ok := typ.Underlying().(*types.Tuple)
|
||||
if ok {
|
||||
for i := 0; i < ttyp.Len(); i++ {
|
||||
subpath := lockPath(tpkg, ttyp.At(i).Type(), seenTParams)
|
||||
if subpath != nil {
|
||||
return append(subpath, typ.String())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// We're only interested in the case in which the underlying
|
||||
// type is a struct. (Interfaces and pointers are safe to copy.)
|
||||
styp, ok := typ.Underlying().(*types.Struct)
|
||||
|
@ -263,7 +316,7 @@ func lockPath(tpkg *types.Package, typ types.Type) typePath {
|
|||
// is a sync.Locker, but a value is not. This differentiates
|
||||
// embedded interfaces from embedded values.
|
||||
if types.Implements(types.NewPointer(typ), lockerType) && !types.Implements(typ, lockerType) {
|
||||
return []types.Type{typ}
|
||||
return []string{typ.String()}
|
||||
}
|
||||
|
||||
// In go1.10, sync.noCopy did not implement Locker.
|
||||
|
@ -272,15 +325,15 @@ func lockPath(tpkg *types.Package, typ types.Type) typePath {
|
|||
if named, ok := typ.(*types.Named); ok &&
|
||||
named.Obj().Name() == "noCopy" &&
|
||||
named.Obj().Pkg().Path() == "sync" {
|
||||
return []types.Type{typ}
|
||||
return []string{typ.String()}
|
||||
}
|
||||
|
||||
nfields := styp.NumFields()
|
||||
for i := 0; i < nfields; i++ {
|
||||
ftyp := styp.Field(i).Type()
|
||||
subpath := lockPath(tpkg, ftyp)
|
||||
subpath := lockPath(tpkg, ftyp, seenTParams)
|
||||
if subpath != nil {
|
||||
return append(subpath, typ)
|
||||
return append(subpath, typ.String())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
6
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go
generated
vendored
6
vendor/golang.org/x/tools/go/analysis/passes/ctrlflow/ctrlflow.go
generated
vendored
|
@ -187,7 +187,11 @@ func (c *CFGs) callMayReturn(call *ast.CallExpr) (r bool) {
|
|||
return false // panic never returns
|
||||
}
|
||||
|
||||
// Is this a static call?
|
||||
// Is this a static call? Also includes static functions
|
||||
// parameterized by a type. Such functions may or may not
|
||||
// return depending on the parameter type, but in some
|
||||
// cases the answer is definite. We let ctrlflow figure
|
||||
// that out.
|
||||
fn := typeutil.StaticCallee(c.pass.TypesInfo, call)
|
||||
if fn == nil {
|
||||
return true // callee not statically known; be conservative
|
||||
|
|
6
vendor/golang.org/x/tools/go/analysis/passes/ifaceassert/ifaceassert.go
generated
vendored
6
vendor/golang.org/x/tools/go/analysis/passes/ifaceassert/ifaceassert.go
generated
vendored
|
@ -51,6 +51,12 @@ func assertableTo(v, t types.Type) *types.Func {
|
|||
if V == nil || T == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Mitigations for interface comparisons and generics.
|
||||
// TODO(https://github.com/golang/go/issues/50658): Support more precise conclusion.
|
||||
if isParameterized(V) || isParameterized(T) {
|
||||
return nil
|
||||
}
|
||||
if f, wrongType := types.MissingMethod(V, T, false); wrongType {
|
||||
return f
|
||||
}
|
||||
|
|
112
vendor/golang.org/x/tools/go/analysis/passes/ifaceassert/parameterized.go
generated
vendored
Normal file
112
vendor/golang.org/x/tools/go/analysis/passes/ifaceassert/parameterized.go
generated
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2022 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 ifaceassert
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
// isParameterized reports whether typ contains any of the type parameters of tparams.
|
||||
//
|
||||
// NOTE: Adapted from go/types/infer.go. If that is exported in a future release remove this copy.
|
||||
func isParameterized(typ types.Type) bool {
|
||||
w := tpWalker{
|
||||
seen: make(map[types.Type]bool),
|
||||
}
|
||||
return w.isParameterized(typ)
|
||||
}
|
||||
|
||||
type tpWalker struct {
|
||||
seen map[types.Type]bool
|
||||
}
|
||||
|
||||
func (w *tpWalker) isParameterized(typ types.Type) (res bool) {
|
||||
// detect cycles
|
||||
if x, ok := w.seen[typ]; ok {
|
||||
return x
|
||||
}
|
||||
w.seen[typ] = false
|
||||
defer func() {
|
||||
w.seen[typ] = res
|
||||
}()
|
||||
|
||||
switch t := typ.(type) {
|
||||
case nil, *types.Basic: // TODO(gri) should nil be handled here?
|
||||
break
|
||||
|
||||
case *types.Array:
|
||||
return w.isParameterized(t.Elem())
|
||||
|
||||
case *types.Slice:
|
||||
return w.isParameterized(t.Elem())
|
||||
|
||||
case *types.Struct:
|
||||
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||
if w.isParameterized(t.Field(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case *types.Pointer:
|
||||
return w.isParameterized(t.Elem())
|
||||
|
||||
case *types.Tuple:
|
||||
n := t.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
if w.isParameterized(t.At(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case *types.Signature:
|
||||
// t.tparams may not be nil if we are looking at a signature
|
||||
// of a generic function type (or an interface method) that is
|
||||
// part of the type we're testing. We don't care about these type
|
||||
// parameters.
|
||||
// Similarly, the receiver of a method may declare (rather then
|
||||
// use) type parameters, we don't care about those either.
|
||||
// Thus, we only need to look at the input and result parameters.
|
||||
return w.isParameterized(t.Params()) || w.isParameterized(t.Results())
|
||||
|
||||
case *types.Interface:
|
||||
for i, n := 0, t.NumMethods(); i < n; i++ {
|
||||
if w.isParameterized(t.Method(i).Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
terms, err := typeparams.InterfaceTermSet(t)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, term := range terms {
|
||||
if w.isParameterized(term.Type()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case *types.Map:
|
||||
return w.isParameterized(t.Key()) || w.isParameterized(t.Elem())
|
||||
|
||||
case *types.Chan:
|
||||
return w.isParameterized(t.Elem())
|
||||
|
||||
case *types.Named:
|
||||
list := typeparams.NamedTypeArgs(t)
|
||||
for i, n := 0, list.Len(); i < n; i++ {
|
||||
if w.isParameterized(list.At(i)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
case *typeparams.TypeParam:
|
||||
return true
|
||||
|
||||
default:
|
||||
panic(t) // unreachable
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
7
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc.go
generated
vendored
7
vendor/golang.org/x/tools/go/analysis/passes/nilfunc/nilfunc.go
generated
vendored
|
@ -14,6 +14,7 @@ import (
|
|||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
const Doc = `check for useless comparisons between functions and nil
|
||||
|
@ -59,6 +60,12 @@ func run(pass *analysis.Pass) (interface{}, error) {
|
|||
obj = pass.TypesInfo.Uses[v]
|
||||
case *ast.SelectorExpr:
|
||||
obj = pass.TypesInfo.Uses[v.Sel]
|
||||
case *ast.IndexExpr, *typeparams.IndexListExpr:
|
||||
// Check generic functions such as "f[T1,T2]".
|
||||
x, _, _, _ := typeparams.UnpackIndexExpr(v)
|
||||
if id, ok := x.(*ast.Ident); ok {
|
||||
obj = pass.TypesInfo.Uses[id]
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
|
29
vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go
generated
vendored
29
vendor/golang.org/x/tools/go/analysis/passes/nilness/nilness.go
generated
vendored
|
@ -135,6 +135,11 @@ func runFunc(pass *analysis.Pass, fn *ssa.Function) {
|
|||
if nilnessOf(stack, instr.X) == isnil {
|
||||
reportf("nilpanic", instr.Pos(), "panic with nil value")
|
||||
}
|
||||
case *ssa.SliceToArrayPointer:
|
||||
nn := nilnessOf(stack, instr.X)
|
||||
if nn == isnil && slice2ArrayPtrLen(instr) > 0 {
|
||||
reportf("conversionpanic", instr.Pos(), "nil slice being cast to an array of len > 0 will always panic")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,6 +264,26 @@ func nilnessOf(stack []fact, v ssa.Value) nilness {
|
|||
if underlying := nilnessOf(stack, v.X); underlying != unknown {
|
||||
return underlying
|
||||
}
|
||||
case *ssa.SliceToArrayPointer:
|
||||
nn := nilnessOf(stack, v.X)
|
||||
if slice2ArrayPtrLen(v) > 0 {
|
||||
if nn == isnil {
|
||||
// We know that *(*[1]byte)(nil) is going to panic because of the
|
||||
// conversion. So return unknown to the caller, prevent useless
|
||||
// nil deference reporting due to * operator.
|
||||
return unknown
|
||||
}
|
||||
// Otherwise, the conversion will yield a non-nil pointer to array.
|
||||
// Note that the instruction can still panic if array length greater
|
||||
// than slice length. If the value is used by another instruction,
|
||||
// that instruction can assume the panic did not happen when that
|
||||
// instruction is reached.
|
||||
return isnonnil
|
||||
}
|
||||
// In case array length is zero, the conversion result depends on nilness of the slice.
|
||||
if nn != unknown {
|
||||
return nn
|
||||
}
|
||||
}
|
||||
|
||||
// Is value intrinsically nil or non-nil?
|
||||
|
@ -292,6 +317,10 @@ func nilnessOf(stack []fact, v ssa.Value) nilness {
|
|||
return unknown
|
||||
}
|
||||
|
||||
func slice2ArrayPtrLen(v *ssa.SliceToArrayPointer) int64 {
|
||||
return v.Type().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
|
||||
}
|
||||
|
||||
// If b ends with an equality comparison, eq returns the operation and
|
||||
// its true (equal) and false (not equal) successors.
|
||||
func eq(b *ssa.BasicBlock) (op *ssa.BinOp, tsucc, fsucc *ssa.BasicBlock) {
|
||||
|
|
56
vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
generated
vendored
56
vendor/golang.org/x/tools/go/analysis/passes/printf/printf.go
generated
vendored
|
@ -25,6 +25,7 @@ import (
|
|||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -452,8 +453,15 @@ func stringConstantArg(pass *analysis.Pass, call *ast.CallExpr, idx int) (string
|
|||
if idx >= len(call.Args) {
|
||||
return "", false
|
||||
}
|
||||
arg := call.Args[idx]
|
||||
lit := pass.TypesInfo.Types[arg].Value
|
||||
return stringConstantExpr(pass, call.Args[idx])
|
||||
}
|
||||
|
||||
// stringConstantExpr returns expression's string constant value.
|
||||
//
|
||||
// ("", false) is returned if expression isn't a string
|
||||
// constant.
|
||||
func stringConstantExpr(pass *analysis.Pass, expr ast.Expr) (string, bool) {
|
||||
lit := pass.TypesInfo.Types[expr].Value
|
||||
if lit != nil && lit.Kind() == constant.String {
|
||||
return constant.StringVal(lit), true
|
||||
}
|
||||
|
@ -490,7 +498,7 @@ func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func,
|
|||
_, ok = isPrint[strings.ToLower(fn.Name())]
|
||||
}
|
||||
if ok {
|
||||
if fn.Name() == "Errorf" {
|
||||
if fn.FullName() == "fmt.Errorf" {
|
||||
kind = KindErrorf
|
||||
} else if strings.HasSuffix(fn.Name(), "f") {
|
||||
kind = KindPrintf
|
||||
|
@ -513,7 +521,12 @@ func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func,
|
|||
func isFormatter(typ types.Type) bool {
|
||||
// If the type is an interface, the value it holds might satisfy fmt.Formatter.
|
||||
if _, ok := typ.Underlying().(*types.Interface); ok {
|
||||
return true
|
||||
// Don't assume type parameters could be formatters. With the greater
|
||||
// expressiveness of constraint interface syntax we expect more type safety
|
||||
// when using type parameters.
|
||||
if !typeparams.IsTypeParam(typ) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Format")
|
||||
fn, ok := obj.(*types.Func)
|
||||
|
@ -590,12 +603,9 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.F
|
|||
}
|
||||
if state.verb == 'w' {
|
||||
switch kind {
|
||||
case KindNone, KindPrint:
|
||||
case KindNone, KindPrint, KindPrintf:
|
||||
pass.Reportf(call.Pos(), "%s does not support error-wrapping directive %%w", state.name)
|
||||
return
|
||||
case KindPrintf:
|
||||
pass.Reportf(call.Pos(), "%s call has error-wrapping directive %%w, which is only supported for functions backed by fmt.Errorf", state.name)
|
||||
return
|
||||
}
|
||||
if anyW {
|
||||
pass.Reportf(call.Pos(), "%s call has more than one error-wrapping directive %%w", state.name)
|
||||
|
@ -837,8 +847,9 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
|
|||
}
|
||||
|
||||
// Could current arg implement fmt.Formatter?
|
||||
// Skip check for the %w verb, which requires an error.
|
||||
formatter := false
|
||||
if state.argNum < len(call.Args) {
|
||||
if v.typ != argError && state.argNum < len(call.Args) {
|
||||
if tv, ok := pass.TypesInfo.Types[call.Args[state.argNum]]; ok {
|
||||
formatter = isFormatter(tv.Type)
|
||||
}
|
||||
|
@ -874,8 +885,12 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
|
|||
return
|
||||
}
|
||||
arg := call.Args[argNum]
|
||||
if !matchArgType(pass, argInt, nil, arg) {
|
||||
pass.ReportRangef(call, "%s format %s uses non-int %s as argument of *", state.name, state.format, analysisutil.Format(pass.Fset, arg))
|
||||
if reason, ok := matchArgType(pass, argInt, arg); !ok {
|
||||
details := ""
|
||||
if reason != "" {
|
||||
details = " (" + reason + ")"
|
||||
}
|
||||
pass.ReportRangef(call, "%s format %s uses non-int %s%s as argument of *", state.name, state.format, analysisutil.Format(pass.Fset, arg), details)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
@ -892,12 +907,16 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
|
|||
pass.ReportRangef(call, "%s format %s arg %s is a func value, not called", state.name, state.format, analysisutil.Format(pass.Fset, arg))
|
||||
return false
|
||||
}
|
||||
if !matchArgType(pass, v.typ, nil, arg) {
|
||||
if reason, ok := matchArgType(pass, v.typ, arg); !ok {
|
||||
typeString := ""
|
||||
if typ := pass.TypesInfo.Types[arg].Type; typ != nil {
|
||||
typeString = typ.String()
|
||||
}
|
||||
pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s", state.name, state.format, analysisutil.Format(pass.Fset, arg), typeString)
|
||||
details := ""
|
||||
if reason != "" {
|
||||
details = " (" + reason + ")"
|
||||
}
|
||||
pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s%s", state.name, state.format, analysisutil.Format(pass.Fset, arg), typeString, details)
|
||||
return false
|
||||
}
|
||||
if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) {
|
||||
|
@ -1055,10 +1074,10 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
|
|||
}
|
||||
|
||||
arg := args[0]
|
||||
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
|
||||
// Ignore trailing % character in lit.Value.
|
||||
if s, ok := stringConstantExpr(pass, arg); ok {
|
||||
// Ignore trailing % character
|
||||
// The % in "abc 0.0%" couldn't be a formatting directive.
|
||||
s := strings.TrimSuffix(lit.Value, `%"`)
|
||||
s = strings.TrimSuffix(s, "%")
|
||||
if strings.Contains(s, "%") {
|
||||
m := printFormatRE.FindStringSubmatch(s)
|
||||
if m != nil {
|
||||
|
@ -1069,9 +1088,8 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
|
|||
if strings.HasSuffix(fn.Name(), "ln") {
|
||||
// The last item, if a string, should not have a newline.
|
||||
arg = args[len(args)-1]
|
||||
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
|
||||
str, _ := strconv.Unquote(lit.Value)
|
||||
if strings.HasSuffix(str, "\n") {
|
||||
if s, ok := stringConstantExpr(pass, arg); ok {
|
||||
if strings.HasSuffix(s, "\n") {
|
||||
pass.ReportRangef(call, "%s arg list ends with redundant newline", fn.FullName())
|
||||
}
|
||||
}
|
||||
|
|
217
vendor/golang.org/x/tools/go/analysis/passes/printf/types.go
generated
vendored
217
vendor/golang.org/x/tools/go/analysis/passes/printf/types.go
generated
vendored
|
@ -5,45 +5,60 @@
|
|||
package printf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
|
||||
|
||||
// matchArgType reports an error if printf verb t is not appropriate
|
||||
// for operand arg.
|
||||
// matchArgType reports an error if printf verb t is not appropriate for
|
||||
// operand arg.
|
||||
//
|
||||
// typ is used only for recursive calls; external callers must supply nil.
|
||||
//
|
||||
// (Recursion arises from the compound types {map,chan,slice} which
|
||||
// may be printed with %d etc. if that is appropriate for their element
|
||||
// types.)
|
||||
func matchArgType(pass *analysis.Pass, t printfArgType, typ types.Type, arg ast.Expr) bool {
|
||||
return matchArgTypeInternal(pass, t, typ, arg, make(map[types.Type]bool))
|
||||
}
|
||||
|
||||
// matchArgTypeInternal is the internal version of matchArgType. It carries a map
|
||||
// remembering what types are in progress so we don't recur when faced with recursive
|
||||
// types or mutually recursive types.
|
||||
func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool {
|
||||
// If arg is a type parameter, the verb t must be appropriate for every type in
|
||||
// the type parameter type set.
|
||||
func matchArgType(pass *analysis.Pass, t printfArgType, arg ast.Expr) (reason string, ok bool) {
|
||||
// %v, %T accept any argument type.
|
||||
if t == anyType {
|
||||
return true
|
||||
}
|
||||
if typ == nil {
|
||||
// external call
|
||||
typ = pass.TypesInfo.Types[arg].Type
|
||||
if typ == nil {
|
||||
return true // probably a type check problem
|
||||
}
|
||||
return "", true
|
||||
}
|
||||
|
||||
typ := pass.TypesInfo.Types[arg].Type
|
||||
if typ == nil {
|
||||
return "", true // probably a type check problem
|
||||
}
|
||||
|
||||
m := &argMatcher{t: t, seen: make(map[types.Type]bool)}
|
||||
ok = m.match(typ, true)
|
||||
return m.reason, ok
|
||||
}
|
||||
|
||||
// argMatcher recursively matches types against the printfArgType t.
|
||||
//
|
||||
// To short-circuit recursion, it keeps track of types that have already been
|
||||
// matched (or are in the process of being matched) via the seen map. Recursion
|
||||
// arises from the compound types {map,chan,slice} which may be printed with %d
|
||||
// etc. if that is appropriate for their element types, as well as from type
|
||||
// parameters, which are expanded to the constituents of their type set.
|
||||
//
|
||||
// The reason field may be set to report the cause of the mismatch.
|
||||
type argMatcher struct {
|
||||
t printfArgType
|
||||
seen map[types.Type]bool
|
||||
reason string
|
||||
}
|
||||
|
||||
// match checks if typ matches m's printf arg type. If topLevel is true, typ is
|
||||
// the actual type of the printf arg, for which special rules apply. As a
|
||||
// special case, top level type parameters pass topLevel=true when checking for
|
||||
// matches among the constituents of their type set, as type arguments will
|
||||
// replace the type parameter at compile time.
|
||||
func (m *argMatcher) match(typ types.Type, topLevel bool) bool {
|
||||
// %w accepts only errors.
|
||||
if t == argError {
|
||||
if m.t == argError {
|
||||
return types.ConvertibleTo(typ, errorType)
|
||||
}
|
||||
|
||||
|
@ -51,65 +66,122 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type,
|
|||
if isFormatter(typ) {
|
||||
return true
|
||||
}
|
||||
|
||||
// If we can use a string, might arg (dynamically) implement the Stringer or Error interface?
|
||||
if t&argString != 0 && isConvertibleToString(pass, typ) {
|
||||
if m.t&argString != 0 && isConvertibleToString(typ) {
|
||||
return true
|
||||
}
|
||||
|
||||
if typ, _ := typ.(*typeparams.TypeParam); typ != nil {
|
||||
// Avoid infinite recursion through type parameters.
|
||||
if m.seen[typ] {
|
||||
return true
|
||||
}
|
||||
m.seen[typ] = true
|
||||
terms, err := typeparams.StructuralTerms(typ)
|
||||
if err != nil {
|
||||
return true // invalid type (possibly an empty type set)
|
||||
}
|
||||
|
||||
if len(terms) == 0 {
|
||||
// No restrictions on the underlying of typ. Type parameters implementing
|
||||
// error, fmt.Formatter, or fmt.Stringer were handled above, and %v and
|
||||
// %T was handled in matchType. We're about to check restrictions the
|
||||
// underlying; if the underlying type is unrestricted there must be an
|
||||
// element of the type set that violates one of the arg type checks
|
||||
// below, so we can safely return false here.
|
||||
|
||||
if m.t == anyType { // anyType must have already been handled.
|
||||
panic("unexpected printfArgType")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Only report a reason if typ is the argument type, otherwise it won't
|
||||
// make sense. Note that it is not sufficient to check if topLevel == here,
|
||||
// as type parameters can have a type set consisting of other type
|
||||
// parameters.
|
||||
reportReason := len(m.seen) == 1
|
||||
|
||||
for _, term := range terms {
|
||||
if !m.match(term.Type(), topLevel) {
|
||||
if reportReason {
|
||||
if term.Tilde() {
|
||||
m.reason = fmt.Sprintf("contains ~%s", term.Type())
|
||||
} else {
|
||||
m.reason = fmt.Sprintf("contains %s", term.Type())
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
typ = typ.Underlying()
|
||||
if inProgress[typ] {
|
||||
// We're already looking at this type. The call that started it will take care of it.
|
||||
if m.seen[typ] {
|
||||
// We've already considered typ, or are in the process of considering it.
|
||||
// In case we've already considered typ, it must have been valid (else we
|
||||
// would have stopped matching). In case we're in the process of
|
||||
// considering it, we must avoid infinite recursion.
|
||||
//
|
||||
// There are some pathological cases where returning true here is
|
||||
// incorrect, for example `type R struct { F []R }`, but these are
|
||||
// acceptable false negatives.
|
||||
return true
|
||||
}
|
||||
inProgress[typ] = true
|
||||
m.seen[typ] = true
|
||||
|
||||
switch typ := typ.(type) {
|
||||
case *types.Signature:
|
||||
return t == argPointer
|
||||
return m.t == argPointer
|
||||
|
||||
case *types.Map:
|
||||
return t == argPointer ||
|
||||
// Recur: map[int]int matches %d.
|
||||
(matchArgTypeInternal(pass, t, typ.Key(), arg, inProgress) && matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress))
|
||||
if m.t == argPointer {
|
||||
return true
|
||||
}
|
||||
// Recur: map[int]int matches %d.
|
||||
return m.match(typ.Key(), false) && m.match(typ.Elem(), false)
|
||||
|
||||
case *types.Chan:
|
||||
return t&argPointer != 0
|
||||
return m.t&argPointer != 0
|
||||
|
||||
case *types.Array:
|
||||
// Same as slice.
|
||||
if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
|
||||
if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && m.t&argString != 0 {
|
||||
return true // %s matches []byte
|
||||
}
|
||||
// Recur: []int matches %d.
|
||||
return matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress)
|
||||
return m.match(typ.Elem(), false)
|
||||
|
||||
case *types.Slice:
|
||||
// Same as array.
|
||||
if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 {
|
||||
if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && m.t&argString != 0 {
|
||||
return true // %s matches []byte
|
||||
}
|
||||
if t == argPointer {
|
||||
if m.t == argPointer {
|
||||
return true // %p prints a slice's 0th element
|
||||
}
|
||||
// Recur: []int matches %d. But watch out for
|
||||
// type T []T
|
||||
// If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below.
|
||||
return matchArgTypeInternal(pass, t, typ.Elem(), arg, inProgress)
|
||||
return m.match(typ.Elem(), false)
|
||||
|
||||
case *types.Pointer:
|
||||
// Ugly, but dealing with an edge case: a known pointer to an invalid type,
|
||||
// probably something from a failed import.
|
||||
if typ.Elem().String() == "invalid type" {
|
||||
if false {
|
||||
pass.Reportf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", analysisutil.Format(pass.Fset, arg))
|
||||
}
|
||||
if typ.Elem() == types.Typ[types.Invalid] {
|
||||
return true // special case
|
||||
}
|
||||
// If it's actually a pointer with %p, it prints as one.
|
||||
if t == argPointer {
|
||||
if m.t == argPointer {
|
||||
return true
|
||||
}
|
||||
|
||||
if typeparams.IsTypeParam(typ.Elem()) {
|
||||
return true // We don't know whether the logic below applies. Give up.
|
||||
}
|
||||
|
||||
under := typ.Elem().Underlying()
|
||||
switch under.(type) {
|
||||
case *types.Struct: // see below
|
||||
|
@ -118,19 +190,31 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type,
|
|||
case *types.Map: // see below
|
||||
default:
|
||||
// Check whether the rest can print pointers.
|
||||
return t&argPointer != 0
|
||||
return m.t&argPointer != 0
|
||||
}
|
||||
// If it's a top-level pointer to a struct, array, slice, or
|
||||
// If it's a top-level pointer to a struct, array, slice, type param, or
|
||||
// map, that's equivalent in our analysis to whether we can
|
||||
// print the type being pointed to. Pointers in nested levels
|
||||
// are not supported to minimize fmt running into loops.
|
||||
if len(inProgress) > 1 {
|
||||
if !topLevel {
|
||||
return false
|
||||
}
|
||||
return matchArgTypeInternal(pass, t, under, arg, inProgress)
|
||||
return m.match(under, false)
|
||||
|
||||
case *types.Struct:
|
||||
return matchStructArgType(pass, t, typ, arg, inProgress)
|
||||
// report whether all the elements of the struct match the expected type. For
|
||||
// instance, with "%d" all the elements must be printable with the "%d" format.
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
typf := typ.Field(i)
|
||||
if !m.match(typf.Type(), false) {
|
||||
return false
|
||||
}
|
||||
if m.t&argString != 0 && !typf.Exported() && isConvertibleToString(typf.Type()) {
|
||||
// Issue #17798: unexported Stringer or error cannot be properly formatted.
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case *types.Interface:
|
||||
// There's little we can do.
|
||||
|
@ -142,7 +226,7 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type,
|
|||
switch typ.Kind() {
|
||||
case types.UntypedBool,
|
||||
types.Bool:
|
||||
return t&argBool != 0
|
||||
return m.t&argBool != 0
|
||||
|
||||
case types.UntypedInt,
|
||||
types.Int,
|
||||
|
@ -156,35 +240,32 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type,
|
|||
types.Uint32,
|
||||
types.Uint64,
|
||||
types.Uintptr:
|
||||
return t&argInt != 0
|
||||
return m.t&argInt != 0
|
||||
|
||||
case types.UntypedFloat,
|
||||
types.Float32,
|
||||
types.Float64:
|
||||
return t&argFloat != 0
|
||||
return m.t&argFloat != 0
|
||||
|
||||
case types.UntypedComplex,
|
||||
types.Complex64,
|
||||
types.Complex128:
|
||||
return t&argComplex != 0
|
||||
return m.t&argComplex != 0
|
||||
|
||||
case types.UntypedString,
|
||||
types.String:
|
||||
return t&argString != 0
|
||||
return m.t&argString != 0
|
||||
|
||||
case types.UnsafePointer:
|
||||
return t&(argPointer|argInt) != 0
|
||||
return m.t&(argPointer|argInt) != 0
|
||||
|
||||
case types.UntypedRune:
|
||||
return t&(argInt|argRune) != 0
|
||||
return m.t&(argInt|argRune) != 0
|
||||
|
||||
case types.UntypedNil:
|
||||
return false
|
||||
|
||||
case types.Invalid:
|
||||
if false {
|
||||
pass.Reportf(arg.Pos(), "printf argument %v has invalid or unknown type", analysisutil.Format(pass.Fset, arg))
|
||||
}
|
||||
return true // Probably a type check problem.
|
||||
}
|
||||
panic("unreachable")
|
||||
|
@ -193,7 +274,7 @@ func matchArgTypeInternal(pass *analysis.Pass, t printfArgType, typ types.Type,
|
|||
return false
|
||||
}
|
||||
|
||||
func isConvertibleToString(pass *analysis.Pass, typ types.Type) bool {
|
||||
func isConvertibleToString(typ types.Type) bool {
|
||||
if bt, ok := typ.(*types.Basic); ok && bt.Kind() == types.UntypedNil {
|
||||
// We explicitly don't want untyped nil, which is
|
||||
// convertible to both of the interfaces below, as it
|
||||
|
@ -228,19 +309,3 @@ func hasBasicType(pass *analysis.Pass, x ast.Expr, kind types.BasicKind) bool {
|
|||
b, ok := t.(*types.Basic)
|
||||
return ok && b.Kind() == kind
|
||||
}
|
||||
|
||||
// matchStructArgType reports whether all the elements of the struct match the expected
|
||||
// type. For instance, with "%d" all the elements must be printable with the "%d" format.
|
||||
func matchStructArgType(pass *analysis.Pass, t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool {
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
typf := typ.Field(i)
|
||||
if !matchArgTypeInternal(pass, t, typf.Type(), arg, inProgress) {
|
||||
return false
|
||||
}
|
||||
if t&argString != 0 && !typf.Exported() && isConvertibleToString(pass, typf.Type()) {
|
||||
// Issue #17798: unexported Stringer or error cannot be properly formatted.
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
36
vendor/golang.org/x/tools/go/analysis/passes/shift/shift.go
generated
vendored
36
vendor/golang.org/x/tools/go/analysis/passes/shift/shift.go
generated
vendored
|
@ -14,11 +14,14 @@ import (
|
|||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"math"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
const Doc = "check for shifts that equal or exceed the width of the integer"
|
||||
|
@ -93,9 +96,36 @@ func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
|
|||
if t == nil {
|
||||
return
|
||||
}
|
||||
size := 8 * pass.TypesSizes.Sizeof(t)
|
||||
if amt >= size {
|
||||
var structuralTypes []types.Type
|
||||
switch t := t.(type) {
|
||||
case *typeparams.TypeParam:
|
||||
terms, err := typeparams.StructuralTerms(t)
|
||||
if err != nil {
|
||||
return // invalid type
|
||||
}
|
||||
for _, term := range terms {
|
||||
structuralTypes = append(structuralTypes, term.Type())
|
||||
}
|
||||
default:
|
||||
structuralTypes = append(structuralTypes, t)
|
||||
}
|
||||
sizes := make(map[int64]struct{})
|
||||
for _, t := range structuralTypes {
|
||||
size := 8 * pass.TypesSizes.Sizeof(t)
|
||||
sizes[size] = struct{}{}
|
||||
}
|
||||
minSize := int64(math.MaxInt64)
|
||||
for size := range sizes {
|
||||
if size < minSize {
|
||||
minSize = size
|
||||
}
|
||||
}
|
||||
if amt >= minSize {
|
||||
ident := analysisutil.Format(pass.Fset, x)
|
||||
pass.ReportRangef(node, "%s (%d bits) too small for shift of %d", ident, size, amt)
|
||||
qualifier := ""
|
||||
if len(sizes) > 1 {
|
||||
qualifier = "may be "
|
||||
}
|
||||
pass.ReportRangef(node, "%s (%s%d bits) too small for shift of %d", ident, qualifier, minSize, amt)
|
||||
}
|
||||
}
|
||||
|
|
5
vendor/golang.org/x/tools/go/analysis/passes/sortslice/analyzer.go
generated
vendored
5
vendor/golang.org/x/tools/go/analysis/passes/sortslice/analyzer.go
generated
vendored
|
@ -45,7 +45,8 @@ func run(pass *analysis.Pass) (interface{}, error) {
|
|||
return
|
||||
}
|
||||
|
||||
if fn.FullName() != "sort.Slice" {
|
||||
fnName := fn.FullName()
|
||||
if fnName != "sort.Slice" && fnName != "sort.SliceStable" && fnName != "sort.SliceIsSorted" {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -115,7 +116,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
|
|||
pass.Report(analysis.Diagnostic{
|
||||
Pos: call.Pos(),
|
||||
End: call.End(),
|
||||
Message: fmt.Sprintf("sort.Slice's argument must be a slice; is called with %s", typ.String()),
|
||||
Message: fmt.Sprintf("%s's argument must be a slice; is called with %s", fnName, typ.String()),
|
||||
SuggestedFixes: fixes,
|
||||
})
|
||||
})
|
||||
|
|
6
vendor/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go
generated
vendored
6
vendor/golang.org/x/tools/go/analysis/passes/stdmethods/stdmethods.go
generated
vendored
|
@ -61,7 +61,7 @@ var Analyzer = &analysis.Analyzer{
|
|||
// we let it go. But if it does have a fmt.ScanState, then the
|
||||
// rest has to match.
|
||||
var canonicalMethods = map[string]struct{ args, results []string }{
|
||||
"As": {[]string{"interface{}"}, []string{"bool"}}, // errors.As
|
||||
"As": {[]string{"any"}, []string{"bool"}}, // errors.As
|
||||
// "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict
|
||||
"Format": {[]string{"=fmt.State", "rune"}, []string{}}, // fmt.Formatter
|
||||
"GobDecode": {[]string{"[]byte"}, []string{"error"}}, // gob.GobDecoder
|
||||
|
@ -194,7 +194,9 @@ func matchParams(pass *analysis.Pass, expect []string, actual *types.Tuple, pref
|
|||
func matchParamType(expect string, actual types.Type) bool {
|
||||
expect = strings.TrimPrefix(expect, "=")
|
||||
// Overkill but easy.
|
||||
return typeString(actual) == expect
|
||||
t := typeString(actual)
|
||||
return t == expect ||
|
||||
(t == "any" || t == "interface{}") && (expect == "any" || expect == "interface{}")
|
||||
}
|
||||
|
||||
var errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface)
|
||||
|
|
153
vendor/golang.org/x/tools/go/analysis/passes/stringintconv/string.go
generated
vendored
153
vendor/golang.org/x/tools/go/analysis/passes/stringintconv/string.go
generated
vendored
|
@ -10,10 +10,12 @@ import (
|
|||
"fmt"
|
||||
"go/ast"
|
||||
"go/types"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
const Doc = `check for string(int) conversions
|
||||
|
@ -36,6 +38,35 @@ var Analyzer = &analysis.Analyzer{
|
|||
Run: run,
|
||||
}
|
||||
|
||||
// describe returns a string describing the type typ contained within the type
|
||||
// set of inType. If non-empty, inName is used as the name of inType (this is
|
||||
// necessary so that we can use alias type names that may not be reachable from
|
||||
// inType itself).
|
||||
func describe(typ, inType types.Type, inName string) string {
|
||||
name := inName
|
||||
if typ != inType {
|
||||
name = typeName(typ)
|
||||
}
|
||||
if name == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
var parentheticals []string
|
||||
if underName := typeName(typ.Underlying()); underName != "" && underName != name {
|
||||
parentheticals = append(parentheticals, underName)
|
||||
}
|
||||
|
||||
if typ != inType && inName != "" && inName != name {
|
||||
parentheticals = append(parentheticals, "in "+inName)
|
||||
}
|
||||
|
||||
if len(parentheticals) > 0 {
|
||||
name += " (" + strings.Join(parentheticals, ", ") + ")"
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
func typeName(typ types.Type) string {
|
||||
if v, _ := typ.(interface{ Name() string }); v != nil {
|
||||
return v.Name()
|
||||
|
@ -54,6 +85,11 @@ func run(pass *analysis.Pass) (interface{}, error) {
|
|||
inspect.Preorder(nodeFilter, func(n ast.Node) {
|
||||
call := n.(*ast.CallExpr)
|
||||
|
||||
if len(call.Args) != 1 {
|
||||
return
|
||||
}
|
||||
arg := call.Args[0]
|
||||
|
||||
// Retrieve target type name.
|
||||
var tname *types.TypeName
|
||||
switch fun := call.Fun.(type) {
|
||||
|
@ -65,62 +101,119 @@ func run(pass *analysis.Pass) (interface{}, error) {
|
|||
if tname == nil {
|
||||
return
|
||||
}
|
||||
target := tname.Name()
|
||||
|
||||
// Check that target type T in T(v) has an underlying type of string.
|
||||
T, _ := tname.Type().Underlying().(*types.Basic)
|
||||
if T == nil || T.Kind() != types.String {
|
||||
return
|
||||
}
|
||||
if s := T.Name(); target != s {
|
||||
target += " (" + s + ")"
|
||||
// In the conversion T(v) of a value v of type V to a target type T, we
|
||||
// look for types T0 in the type set of T and V0 in the type set of V, such
|
||||
// that V0->T0 is a problematic conversion. If T and V are not type
|
||||
// parameters, this amounts to just checking if V->T is a problematic
|
||||
// conversion.
|
||||
|
||||
// First, find a type T0 in T that has an underlying type of string.
|
||||
T := tname.Type()
|
||||
ttypes, err := structuralTypes(T)
|
||||
if err != nil {
|
||||
return // invalid type
|
||||
}
|
||||
|
||||
// Check that type V of v has an underlying integral type that is not byte or rune.
|
||||
if len(call.Args) != 1 {
|
||||
return
|
||||
var T0 types.Type // string type in the type set of T
|
||||
|
||||
for _, tt := range ttypes {
|
||||
u, _ := tt.Underlying().(*types.Basic)
|
||||
if u != nil && u.Kind() == types.String {
|
||||
T0 = tt
|
||||
break
|
||||
}
|
||||
}
|
||||
v := call.Args[0]
|
||||
vtyp := pass.TypesInfo.TypeOf(v)
|
||||
V, _ := vtyp.Underlying().(*types.Basic)
|
||||
if V == nil || V.Info()&types.IsInteger == 0 {
|
||||
return
|
||||
}
|
||||
switch V.Kind() {
|
||||
case types.Byte, types.Rune, types.UntypedRune:
|
||||
|
||||
if T0 == nil {
|
||||
// No target types have an underlying type of string.
|
||||
return
|
||||
}
|
||||
|
||||
// Retrieve source type name.
|
||||
source := typeName(vtyp)
|
||||
if source == "" {
|
||||
// Next, find a type V0 in V that has an underlying integral type that is
|
||||
// not byte or rune.
|
||||
V := pass.TypesInfo.TypeOf(arg)
|
||||
vtypes, err := structuralTypes(V)
|
||||
if err != nil {
|
||||
return // invalid type
|
||||
}
|
||||
|
||||
var V0 types.Type // integral type in the type set of V
|
||||
|
||||
for _, vt := range vtypes {
|
||||
u, _ := vt.Underlying().(*types.Basic)
|
||||
if u != nil && u.Info()&types.IsInteger != 0 {
|
||||
switch u.Kind() {
|
||||
case types.Byte, types.Rune, types.UntypedRune:
|
||||
continue
|
||||
}
|
||||
V0 = vt
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if V0 == nil {
|
||||
// No source types are non-byte or rune integer types.
|
||||
return
|
||||
}
|
||||
if s := V.Name(); source != s {
|
||||
source += " (" + s + ")"
|
||||
|
||||
convertibleToRune := true // if true, we can suggest a fix
|
||||
for _, t := range vtypes {
|
||||
if !types.ConvertibleTo(t, types.Typ[types.Rune]) {
|
||||
convertibleToRune = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
target := describe(T0, T, tname.Name())
|
||||
source := describe(V0, V, typeName(V))
|
||||
|
||||
if target == "" || source == "" {
|
||||
return // something went wrong
|
||||
}
|
||||
|
||||
diag := analysis.Diagnostic{
|
||||
Pos: n.Pos(),
|
||||
Message: fmt.Sprintf("conversion from %s to %s yields a string of one rune, not a string of digits (did you mean fmt.Sprint(x)?)", source, target),
|
||||
SuggestedFixes: []analysis.SuggestedFix{
|
||||
}
|
||||
|
||||
if convertibleToRune {
|
||||
diag.SuggestedFixes = []analysis.SuggestedFix{
|
||||
{
|
||||
Message: "Did you mean to convert a rune to a string?",
|
||||
TextEdits: []analysis.TextEdit{
|
||||
{
|
||||
Pos: v.Pos(),
|
||||
End: v.Pos(),
|
||||
Pos: arg.Pos(),
|
||||
End: arg.Pos(),
|
||||
NewText: []byte("rune("),
|
||||
},
|
||||
{
|
||||
Pos: v.End(),
|
||||
End: v.End(),
|
||||
Pos: arg.End(),
|
||||
End: arg.End(),
|
||||
NewText: []byte(")"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
pass.Report(diag)
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func structuralTypes(t types.Type) ([]types.Type, error) {
|
||||
var structuralTypes []types.Type
|
||||
switch t := t.(type) {
|
||||
case *typeparams.TypeParam:
|
||||
terms, err := typeparams.StructuralTerms(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, term := range terms {
|
||||
structuralTypes = append(structuralTypes, term.Type())
|
||||
}
|
||||
default:
|
||||
structuralTypes = append(structuralTypes, t)
|
||||
}
|
||||
return structuralTypes, nil
|
||||
}
|
||||
|
|
42
vendor/golang.org/x/tools/go/analysis/passes/testinggoroutine/testinggoroutine.go
generated
vendored
42
vendor/golang.org/x/tools/go/analysis/passes/testinggoroutine/testinggoroutine.go
generated
vendored
|
@ -11,6 +11,7 @@ import (
|
|||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
const Doc = `report calls to (*testing.T).Fatal from goroutines started by a test.
|
||||
|
@ -119,11 +120,44 @@ func typeIsTestingDotTOrB(expr ast.Expr) (string, bool) {
|
|||
return varTypeName, ok
|
||||
}
|
||||
|
||||
// goStmtFunc returns the ast.Node of a call expression
|
||||
// that was invoked as a go statement. Currently, only
|
||||
// function literals declared in the same function, and
|
||||
// static calls within the same package are supported.
|
||||
func goStmtFun(goStmt *ast.GoStmt) ast.Node {
|
||||
switch fun := goStmt.Call.Fun.(type) {
|
||||
case *ast.IndexExpr, *typeparams.IndexListExpr:
|
||||
x, _, _, _ := typeparams.UnpackIndexExpr(fun)
|
||||
id, _ := x.(*ast.Ident)
|
||||
if id == nil {
|
||||
break
|
||||
}
|
||||
if id.Obj == nil {
|
||||
break
|
||||
}
|
||||
if funDecl, ok := id.Obj.Decl.(ast.Node); ok {
|
||||
return funDecl
|
||||
}
|
||||
case *ast.Ident:
|
||||
// TODO(cuonglm): improve this once golang/go#48141 resolved.
|
||||
if fun.Obj == nil {
|
||||
break
|
||||
}
|
||||
if funDecl, ok := fun.Obj.Decl.(ast.Node); ok {
|
||||
return funDecl
|
||||
}
|
||||
case *ast.FuncLit:
|
||||
return goStmt.Call.Fun
|
||||
}
|
||||
return goStmt.Call
|
||||
}
|
||||
|
||||
// checkGoStmt traverses the goroutine and checks for the
|
||||
// use of the forbidden *testing.(B, T) methods.
|
||||
func checkGoStmt(pass *analysis.Pass, goStmt *ast.GoStmt) {
|
||||
fn := goStmtFun(goStmt)
|
||||
// Otherwise examine the goroutine to check for the forbidden methods.
|
||||
ast.Inspect(goStmt, func(n ast.Node) bool {
|
||||
ast.Inspect(fn, func(n ast.Node) bool {
|
||||
selExpr, ok := n.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return true
|
||||
|
@ -147,7 +181,11 @@ func checkGoStmt(pass *analysis.Pass, goStmt *ast.GoStmt) {
|
|||
return true
|
||||
}
|
||||
if typeName, ok := typeIsTestingDotTOrB(field.Type); ok {
|
||||
pass.ReportRangef(selExpr, "call to (*%s).%s from a non-test goroutine", typeName, selExpr.Sel)
|
||||
var fnRange analysis.Range = goStmt
|
||||
if _, ok := fn.(*ast.FuncLit); ok {
|
||||
fnRange = selExpr
|
||||
}
|
||||
pass.ReportRangef(fnRange, "call to (*%s).%s from a non-test goroutine", typeName, selExpr.Sel)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
|
70
vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go
generated
vendored
70
vendor/golang.org/x/tools/go/analysis/passes/tests/tests.go
generated
vendored
|
@ -8,12 +8,15 @@ package tests
|
|||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
const Doc = `check for common mistaken usages of tests and examples
|
||||
|
@ -42,10 +45,10 @@ func run(pass *analysis.Pass) (interface{}, error) {
|
|||
// Ignore non-functions or functions with receivers.
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(fn.Name.Name, "Example"):
|
||||
checkExample(pass, fn)
|
||||
checkExampleName(pass, fn)
|
||||
checkExampleOutput(pass, fn, f.Comments)
|
||||
case strings.HasPrefix(fn.Name.Name, "Test"):
|
||||
checkTest(pass, fn, "Test")
|
||||
case strings.HasPrefix(fn.Name.Name, "Benchmark"):
|
||||
|
@ -108,7 +111,59 @@ func lookup(pkg *types.Package, name string) []types.Object {
|
|||
return ret
|
||||
}
|
||||
|
||||
func checkExample(pass *analysis.Pass, fn *ast.FuncDecl) {
|
||||
// This pattern is taken from /go/src/go/doc/example.go
|
||||
var outputRe = regexp.MustCompile(`(?i)^[[:space:]]*(unordered )?output:`)
|
||||
|
||||
type commentMetadata struct {
|
||||
isOutput bool
|
||||
pos token.Pos
|
||||
}
|
||||
|
||||
func checkExampleOutput(pass *analysis.Pass, fn *ast.FuncDecl, fileComments []*ast.CommentGroup) {
|
||||
commentsInExample := []commentMetadata{}
|
||||
numOutputs := 0
|
||||
|
||||
// Find the comment blocks that are in the example. These comments are
|
||||
// guaranteed to be in order of appearance.
|
||||
for _, cg := range fileComments {
|
||||
if cg.Pos() < fn.Pos() {
|
||||
continue
|
||||
} else if cg.End() > fn.End() {
|
||||
break
|
||||
}
|
||||
|
||||
isOutput := outputRe.MatchString(cg.Text())
|
||||
if isOutput {
|
||||
numOutputs++
|
||||
}
|
||||
|
||||
commentsInExample = append(commentsInExample, commentMetadata{
|
||||
isOutput: isOutput,
|
||||
pos: cg.Pos(),
|
||||
})
|
||||
}
|
||||
|
||||
// Change message based on whether there are multiple output comment blocks.
|
||||
msg := "output comment block must be the last comment block"
|
||||
if numOutputs > 1 {
|
||||
msg = "there can only be one output comment block per example"
|
||||
}
|
||||
|
||||
for i, cg := range commentsInExample {
|
||||
// Check for output comments that are not the last comment in the example.
|
||||
isLast := (i == len(commentsInExample)-1)
|
||||
if cg.isOutput && !isLast {
|
||||
pass.Report(
|
||||
analysis.Diagnostic{
|
||||
Pos: cg.pos,
|
||||
Message: msg,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkExampleName(pass *analysis.Pass, fn *ast.FuncDecl) {
|
||||
fnName := fn.Name.Name
|
||||
if params := fn.Type.Params; len(params.List) != 0 {
|
||||
pass.Reportf(fn.Pos(), "%s should be niladic", fnName)
|
||||
|
@ -116,6 +171,9 @@ func checkExample(pass *analysis.Pass, fn *ast.FuncDecl) {
|
|||
if results := fn.Type.Results; results != nil && len(results.List) != 0 {
|
||||
pass.Reportf(fn.Pos(), "%s should return nothing", fnName)
|
||||
}
|
||||
if tparams := typeparams.ForFuncType(fn.Type); tparams != nil && len(tparams.List) > 0 {
|
||||
pass.Reportf(fn.Pos(), "%s should not have type params", fnName)
|
||||
}
|
||||
|
||||
if fnName == "Example" {
|
||||
// Nothing more to do.
|
||||
|
@ -182,6 +240,12 @@ func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) {
|
|||
return
|
||||
}
|
||||
|
||||
if tparams := typeparams.ForFuncType(fn.Type); tparams != nil && len(tparams.List) > 0 {
|
||||
// Note: cmd/go/internal/load also errors about TestXXX and BenchmarkXXX functions with type parameters.
|
||||
// We have currently decided to also warn before compilation/package loading. This can help users in IDEs.
|
||||
pass.Reportf(fn.Pos(), "%s has type parameters: it will not be run by go test as a %sXXX function", fn.Name.Name, prefix)
|
||||
}
|
||||
|
||||
if !isTestSuffix(fn.Name.Name[len(prefix):]) {
|
||||
pass.Reportf(fn.Pos(), "%s has malformed name: first letter after '%s' must not be lowercase", fn.Name.Name, prefix)
|
||||
}
|
||||
|
|
3
vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go
generated
vendored
3
vendor/golang.org/x/tools/go/analysis/passes/unmarshal/unmarshal.go
generated
vendored
|
@ -14,6 +14,7 @@ import (
|
|||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
const Doc = `report passing non-pointer or non-interface values to unmarshal
|
||||
|
@ -85,7 +86,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
|
|||
|
||||
t := pass.TypesInfo.Types[call.Args[argidx]].Type
|
||||
switch t.Underlying().(type) {
|
||||
case *types.Pointer, *types.Interface:
|
||||
case *types.Pointer, *types.Interface, *typeparams.TypeParam:
|
||||
return
|
||||
}
|
||||
|
||||
|
|
6
vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go
generated
vendored
6
vendor/golang.org/x/tools/go/analysis/passes/unusedresult/unusedresult.go
generated
vendored
|
@ -17,6 +17,7 @@ import (
|
|||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
"golang.org/x/tools/internal/typeparams"
|
||||
)
|
||||
|
||||
// TODO(adonovan): make this analysis modular: export a mustUseResult
|
||||
|
@ -70,6 +71,11 @@ func run(pass *analysis.Pass) (interface{}, error) {
|
|||
return // a conversion, not a call
|
||||
}
|
||||
|
||||
x, _, _, _ := typeparams.UnpackIndexExpr(fun)
|
||||
if x != nil {
|
||||
fun = x // If this is generic function or method call, skip the instantiation arguments
|
||||
}
|
||||
|
||||
selector, ok := fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return // neither a method call nor a qualified ident
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue