1
0
mirror of https://github.com/golang/go synced 2024-09-30 06:14:31 -06:00

cmd: vendor in new version of x/tools

Fixes #35264

Change-Id: Id540a48f593d8ac1b414551255c5eff24666aa0b
Reviewed-on: https://go-review.googlesource.com/c/go/+/205240
Run-TryBot: Keith Randall <khr@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Keith Randall 2019-11-04 14:50:35 -08:00 committed by Keith Randall
parent ea0b4e7c7d
commit 4af639a568
22 changed files with 162 additions and 88 deletions

View File

@ -9,5 +9,5 @@ require (
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550
golang.org/x/mod v0.1.1-0.20191029194233-18c3998b6452 golang.org/x/mod v0.1.1-0.20191029194233-18c3998b6452
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 // indirect golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 // indirect
golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5 golang.org/x/tools v0.0.0-20191104222624-6b7b8b79ae80
) )

View File

@ -18,8 +18,8 @@ golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dz
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5 h1:TFUhCYbgGMOGnRxJv+j0iAcxCjk8oGjXXWNejQBhUUs= golang.org/x/tools v0.0.0-20191104222624-6b7b8b79ae80 h1:6CcDC1SXj4DrJP955osT9RJmKsH3LQBZJ59D5v4Rw0s=
golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191104222624-6b7b8b79ae80/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -163,13 +163,19 @@ func (pass *Pass) Reportf(pos token.Pos, format string, args ...interface{}) {
pass.Report(Diagnostic{Pos: pos, Message: msg}) pass.Report(Diagnostic{Pos: pos, Message: msg})
} }
// reportNodef is a helper function that reports a Diagnostic using the // The Range interface provides a range. It's equivalent to and satisfied by
// range denoted by the AST node. // ast.Node.
// type Range interface {
// WARNING: This is an experimental API and may change in the future. Pos() token.Pos // position of first character belonging to the node
func (pass *Pass) reportNodef(node ast.Node, format string, args ...interface{}) { End() token.Pos // position of first character immediately after the node
}
// ReportRangef is a helper function that reports a Diagnostic using the
// range provided. ast.Node values can be passed in as the range because
// they satisfy the Range interface.
func (pass *Pass) ReportRangef(rng Range, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...) msg := fmt.Sprintf(format, args...)
pass.Report(Diagnostic{Pos: node.Pos(), End: node.End(), Message: msg}) pass.Report(Diagnostic{Pos: rng.Pos(), End: rng.End(), Message: msg})
} }
func (pass *Pass) String() string { func (pass *Pass) String() string {

View File

@ -22,6 +22,19 @@ type Diagnostic struct {
// Diagnostics should not contain SuggestedFixes that overlap. // Diagnostics should not contain SuggestedFixes that overlap.
// Experimental: This API is experimental and may change in the future. // Experimental: This API is experimental and may change in the future.
SuggestedFixes []SuggestedFix // optional SuggestedFixes []SuggestedFix // optional
// Experimental: This API is experimental and may change in the future.
Related []RelatedInformation // optional
}
// RelatedInformation contains information related to a diagnostic.
// For example, a diagnostic that flags duplicated declarations of a
// variable may include one RelatedInformation per existing
// declaration.
type RelatedInformation struct {
Pos token.Pos
End token.Pos
Message string
} }
// A SuggestedFix is a code change associated with a Diagnostic that a user can choose // A SuggestedFix is a code change associated with a Diagnostic that a user can choose

View File

@ -101,11 +101,13 @@ func (s *Set) ExportObjectFact(obj types.Object, fact analysis.Fact) {
func (s *Set) AllObjectFacts(filter map[reflect.Type]bool) []analysis.ObjectFact { func (s *Set) AllObjectFacts(filter map[reflect.Type]bool) []analysis.ObjectFact {
var facts []analysis.ObjectFact var facts []analysis.ObjectFact
s.mu.Lock()
for k, v := range s.m { for k, v := range s.m {
if k.obj != nil && filter[k.t] { if k.obj != nil && filter[k.t] {
facts = append(facts, analysis.ObjectFact{Object: k.obj, Fact: v}) facts = append(facts, analysis.ObjectFact{Object: k.obj, Fact: v})
} }
} }
s.mu.Unlock()
return facts return facts
} }
@ -134,11 +136,13 @@ func (s *Set) ExportPackageFact(fact analysis.Fact) {
func (s *Set) AllPackageFacts(filter map[reflect.Type]bool) []analysis.PackageFact { func (s *Set) AllPackageFacts(filter map[reflect.Type]bool) []analysis.PackageFact {
var facts []analysis.PackageFact var facts []analysis.PackageFact
s.mu.Lock()
for k, v := range s.m { for k, v := range s.m {
if k.obj == nil && filter[k.t] { if k.obj == nil && filter[k.t] {
facts = append(facts, analysis.PackageFact{Package: k.pkg, Fact: v}) facts = append(facts, analysis.PackageFact{Package: k.pkg, Fact: v})
} }
} }
s.mu.Unlock()
return facts return facts
} }
@ -227,7 +231,6 @@ func Decode(pkg *types.Package, read func(packagePath string) ([]byte, error)) (
// It may fail if one of the Facts could not be gob-encoded, but this is // It may fail if one of the Facts could not be gob-encoded, but this is
// a sign of a bug in an Analyzer. // a sign of a bug in an Analyzer.
func (s *Set) Encode() []byte { func (s *Set) Encode() []byte {
// TODO(adonovan): opt: use a more efficient encoding // TODO(adonovan): opt: use a more efficient encoding
// that avoids repeating PkgPath for each fact. // that avoids repeating PkgPath for each fact.

View File

@ -661,6 +661,10 @@ func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr stri
src = 4 src = 4
break break
} }
if op == "MOVO" || op == "MOVOU" {
src = 16
break
}
if strings.HasPrefix(op, "SET") { if strings.HasPrefix(op, "SET") {
// SETEQ, etc // SETEQ, etc
src = 1 src = 1
@ -736,6 +740,11 @@ func asmCheckVar(badf func(string, ...interface{}), fn *asmFunc, line, expr stri
vk = v.inner[0].kind vk = v.inner[0].kind
vs = v.inner[0].size vs = v.inner[0].size
vt = v.inner[0].typ vt = v.inner[0].typ
case asmComplex:
// Allow a single instruction to load both parts of a complex.
if int(kind) == vs {
kind = asmComplex
}
} }
if addr { if addr {
vk = asmKind(archDef.ptrSize) vk = asmKind(archDef.ptrSize)

View File

@ -91,6 +91,6 @@ func checkAtomicAddAssignment(pass *analysis.Pass, left ast.Expr, call *ast.Call
} }
if broken { if broken {
pass.Reportf(left.Pos(), "direct assignment to atomic value") pass.ReportRangef(left, "direct assignment to atomic value")
} }
} }

View File

@ -100,7 +100,7 @@ func (op boolOp) checkRedundant(pass *analysis.Pass, exprs []ast.Expr) {
for _, e := range exprs { for _, e := range exprs {
efmt := analysisutil.Format(pass.Fset, e) efmt := analysisutil.Format(pass.Fset, e)
if seen[efmt] { if seen[efmt] {
pass.Reportf(e.Pos(), "redundant %s: %s %s %s", op.name, efmt, op.tok, efmt) pass.ReportRangef(e, "redundant %s: %s %s %s", op.name, efmt, op.tok, efmt)
} else { } else {
seen[efmt] = true seen[efmt] = true
} }
@ -147,7 +147,7 @@ func (op boolOp) checkSuspect(pass *analysis.Pass, exprs []ast.Expr) {
if prev, found := seen[xfmt]; found { if prev, found := seen[xfmt]; found {
// checkRedundant handles the case in which efmt == prev. // checkRedundant handles the case in which efmt == prev.
if efmt != prev { if efmt != prev {
pass.Reportf(e.Pos(), "suspect %s: %s %s %s", op.name, efmt, op.tok, prev) pass.ReportRangef(e, "suspect %s: %s %s %s", op.name, efmt, op.tok, prev)
} }
} else { } else {
seen[xfmt] = efmt seen[xfmt] = efmt

View File

@ -97,7 +97,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
return return
} }
pass.Reportf(cl.Pos(), "%s composite literal uses unkeyed fields", typeName) pass.ReportRangef(cl, "%s composite literal uses unkeyed fields", typeName)
}) })
return nil, nil return nil, nil
} }

View File

@ -74,7 +74,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
func checkCopyLocksAssign(pass *analysis.Pass, as *ast.AssignStmt) { func checkCopyLocksAssign(pass *analysis.Pass, as *ast.AssignStmt) {
for i, x := range as.Rhs { for i, x := range as.Rhs {
if path := lockPathRhs(pass, x); path != nil { if path := lockPathRhs(pass, x); path != nil {
pass.Reportf(x.Pos(), "assignment copies lock value to %v: %v", analysisutil.Format(pass.Fset, as.Lhs[i]), path) pass.ReportRangef(x, "assignment copies lock value to %v: %v", analysisutil.Format(pass.Fset, as.Lhs[i]), path)
} }
} }
} }
@ -89,7 +89,7 @@ func checkCopyLocksGenDecl(pass *analysis.Pass, gd *ast.GenDecl) {
valueSpec := spec.(*ast.ValueSpec) valueSpec := spec.(*ast.ValueSpec)
for i, x := range valueSpec.Values { for i, x := range valueSpec.Values {
if path := lockPathRhs(pass, x); path != nil { if path := lockPathRhs(pass, x); path != nil {
pass.Reportf(x.Pos(), "variable declaration copies lock value to %v: %v", valueSpec.Names[i].Name, path) pass.ReportRangef(x, "variable declaration copies lock value to %v: %v", valueSpec.Names[i].Name, path)
} }
} }
} }
@ -102,7 +102,7 @@ func checkCopyLocksCompositeLit(pass *analysis.Pass, cl *ast.CompositeLit) {
x = node.Value x = node.Value
} }
if path := lockPathRhs(pass, x); path != nil { if path := lockPathRhs(pass, x); path != nil {
pass.Reportf(x.Pos(), "literal copies lock value from %v: %v", analysisutil.Format(pass.Fset, x), path) pass.ReportRangef(x, "literal copies lock value from %v: %v", analysisutil.Format(pass.Fset, x), path)
} }
} }
} }
@ -111,7 +111,7 @@ func checkCopyLocksCompositeLit(pass *analysis.Pass, cl *ast.CompositeLit) {
func checkCopyLocksReturnStmt(pass *analysis.Pass, rs *ast.ReturnStmt) { func checkCopyLocksReturnStmt(pass *analysis.Pass, rs *ast.ReturnStmt) {
for _, x := range rs.Results { for _, x := range rs.Results {
if path := lockPathRhs(pass, x); path != nil { if path := lockPathRhs(pass, x); path != nil {
pass.Reportf(x.Pos(), "return copies lock value: %v", path) pass.ReportRangef(x, "return copies lock value: %v", path)
} }
} }
} }
@ -133,7 +133,7 @@ func checkCopyLocksCallExpr(pass *analysis.Pass, ce *ast.CallExpr) {
} }
for _, x := range ce.Args { for _, x := range ce.Args {
if path := lockPathRhs(pass, x); path != nil { if path := lockPathRhs(pass, x); path != nil {
pass.Reportf(x.Pos(), "call of %s copies lock value: %v", analysisutil.Format(pass.Fset, ce.Fun), path) pass.ReportRangef(x, "call of %s copies lock value: %v", analysisutil.Format(pass.Fset, ce.Fun), path)
} }
} }
} }
@ -146,7 +146,7 @@ func checkCopyLocksFunc(pass *analysis.Pass, name string, recv *ast.FieldList, t
if recv != nil && len(recv.List) > 0 { if recv != nil && len(recv.List) > 0 {
expr := recv.List[0].Type 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); path != nil {
pass.Reportf(expr.Pos(), "%s passes lock by value: %v", name, path) pass.ReportRangef(expr, "%s passes lock by value: %v", name, path)
} }
} }
@ -154,7 +154,7 @@ func checkCopyLocksFunc(pass *analysis.Pass, name string, recv *ast.FieldList, t
for _, field := range typ.Params.List { for _, field := range typ.Params.List {
expr := field.Type 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); path != nil {
pass.Reportf(expr.Pos(), "%s passes lock by value: %v", name, path) pass.ReportRangef(expr, "%s passes lock by value: %v", name, path)
} }
} }
} }

View File

@ -51,7 +51,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
return // not enough arguments, e.g. called with return values of another function return // not enough arguments, e.g. called with return values of another function
} }
if fn.FullName() == "errors.As" && !pointerToInterfaceOrError(pass, call.Args[1]) { if fn.FullName() == "errors.As" && !pointerToInterfaceOrError(pass, call.Args[1]) {
pass.Reportf(call.Pos(), "second argument to errors.As must be a pointer to an interface or a type implementing error") pass.ReportRangef(call, "second argument to errors.As must be a pointer to an interface or a type implementing error")
} }
}) })
return nil, nil return nil, nil

View File

@ -85,7 +85,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
} }
if resp.Obj == root.Obj { if resp.Obj == root.Obj {
pass.Reportf(root.Pos(), "using %s before checking for errors", resp.Name) pass.ReportRangef(root, "using %s before checking for errors", resp.Name)
} }
return true return true
}) })

View File

@ -16,7 +16,7 @@
// //
// var Analyzer = &analysis.Analyzer{ // var Analyzer = &analysis.Analyzer{
// ... // ...
// Requires: reflect.TypeOf(new(inspect.Analyzer)), // Requires: []*analysis.Analyzer{inspect.Analyzer},
// } // }
// //
// func run(pass *analysis.Pass) (interface{}, error) { // func run(pass *analysis.Pass) (interface{}, error) {

View File

@ -119,7 +119,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
} }
for _, v := range vars { for _, v := range vars {
if v.Obj == id.Obj { if v.Obj == id.Obj {
pass.Reportf(id.Pos(), "loop variable %s captured by func literal", pass.ReportRangef(id, "loop variable %s captured by func literal",
id.Name) id.Name)
} }
} }

View File

@ -121,7 +121,7 @@ func runFunc(pass *analysis.Pass, node ast.Node) {
} }
if id != nil { if id != nil {
if id.Name == "_" { if id.Name == "_" {
pass.Reportf(id.Pos(), pass.ReportRangef(id,
"the cancel function returned by context.%s should be called, not discarded, to avoid a context leak", "the cancel function returned by context.%s should be called, not discarded, to avoid a context leak",
n.(*ast.SelectorExpr).Sel.Name) n.(*ast.SelectorExpr).Sel.Name)
} else if v, ok := pass.TypesInfo.Uses[id].(*types.Var); ok { } else if v, ok := pass.TypesInfo.Uses[id].(*types.Var); ok {
@ -174,8 +174,8 @@ func runFunc(pass *analysis.Pass, node ast.Node) {
for v, stmt := range cancelvars { for v, stmt := range cancelvars {
if ret := lostCancelPath(pass, g, v, stmt, sig); ret != nil { if ret := lostCancelPath(pass, g, v, stmt, sig); ret != nil {
lineno := pass.Fset.Position(stmt.Pos()).Line lineno := pass.Fset.Position(stmt.Pos()).Line
pass.Reportf(stmt.Pos(), "the %s function is not used on all paths (possible context leak)", v.Name()) pass.ReportRangef(stmt, "the %s function is not used on all paths (possible context leak)", v.Name())
pass.Reportf(ret.Pos(), "this return statement may be reached without using the %s var defined on line %d", v.Name(), lineno) pass.ReportRangef(ret, "this return statement may be reached without using the %s var defined on line %d", v.Name(), lineno)
} }
} }
} }

View File

@ -68,7 +68,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
return return
} }
pass.Reportf(e.Pos(), "comparison of function %v %v nil is always %v", obj.Name(), e.Op, e.Op == token.NEQ) pass.ReportRangef(e, "comparison of function %v %v nil is always %v", obj.Name(), e.Op, e.Op == token.NEQ)
}) })
return nil, nil return nil, nil
} }

View File

@ -13,6 +13,7 @@ import (
"go/constant" "go/constant"
"go/token" "go/token"
"go/types" "go/types"
"reflect"
"regexp" "regexp"
"sort" "sort"
"strconv" "strconv"
@ -31,11 +32,12 @@ func init() {
} }
var Analyzer = &analysis.Analyzer{ var Analyzer = &analysis.Analyzer{
Name: "printf", Name: "printf",
Doc: doc, Doc: doc,
Requires: []*analysis.Analyzer{inspect.Analyzer}, Requires: []*analysis.Analyzer{inspect.Analyzer},
Run: run, Run: run,
FactTypes: []analysis.Fact{new(isWrapper)}, ResultType: reflect.TypeOf((*Result)(nil)),
FactTypes: []analysis.Fact{new(isWrapper)},
} }
const doc = `check consistency of Printf format strings and arguments const doc = `check consistency of Printf format strings and arguments
@ -66,18 +68,64 @@ argument list. Otherwise it is assumed to be Print-like, taking a list
of arguments with no format string. of arguments with no format string.
` `
// Kind is a kind of fmt function behavior.
type Kind int
const (
KindNone Kind = iota // not a fmt wrapper function
KindPrint // function behaves like fmt.Print
KindPrintf // function behaves like fmt.Printf
KindErrorf // function behaves like fmt.Errorf
)
func (kind Kind) String() string {
switch kind {
case KindPrint:
return "print"
case KindPrintf:
return "printf"
case KindErrorf:
return "errorf"
}
return ""
}
// Result is the printf analyzer's result type. Clients may query the result
// to learn whether a function behaves like fmt.Print or fmt.Printf.
type Result struct {
funcs map[*types.Func]Kind
}
// Kind reports whether fn behaves like fmt.Print or fmt.Printf.
func (r *Result) Kind(fn *types.Func) Kind {
_, ok := isPrint[fn.FullName()]
if !ok {
// Next look up just "printf", for use with -printf.funcs.
_, ok = isPrint[strings.ToLower(fn.Name())]
}
if ok {
if strings.HasSuffix(fn.Name(), "f") {
return KindPrintf
} else {
return KindPrint
}
}
return r.funcs[fn]
}
// isWrapper is a fact indicating that a function is a print or printf wrapper. // isWrapper is a fact indicating that a function is a print or printf wrapper.
type isWrapper struct{ Kind funcKind } type isWrapper struct{ Kind Kind }
func (f *isWrapper) AFact() {} func (f *isWrapper) AFact() {}
func (f *isWrapper) String() string { func (f *isWrapper) String() string {
switch f.Kind { switch f.Kind {
case kindPrintf: case KindPrintf:
return "printfWrapper" return "printfWrapper"
case kindPrint: case KindPrint:
return "printWrapper" return "printWrapper"
case kindErrorf: case KindErrorf:
return "errorfWrapper" return "errorfWrapper"
default: default:
return "unknownWrapper" return "unknownWrapper"
@ -85,9 +133,12 @@ func (f *isWrapper) String() string {
} }
func run(pass *analysis.Pass) (interface{}, error) { func run(pass *analysis.Pass) (interface{}, error) {
findPrintfLike(pass) res := &Result{
funcs: make(map[*types.Func]Kind),
}
findPrintfLike(pass, res)
checkCall(pass) checkCall(pass)
return nil, nil return res, nil
} }
type printfWrapper struct { type printfWrapper struct {
@ -154,7 +205,7 @@ func maybePrintfWrapper(info *types.Info, decl ast.Decl) *printfWrapper {
} }
// findPrintfLike scans the entire package to find printf-like functions. // findPrintfLike scans the entire package to find printf-like functions.
func findPrintfLike(pass *analysis.Pass) (interface{}, error) { func findPrintfLike(pass *analysis.Pass, res *Result) (interface{}, error) {
// Gather potential wrappers and call graph between them. // Gather potential wrappers and call graph between them.
byObj := make(map[*types.Func]*printfWrapper) byObj := make(map[*types.Func]*printfWrapper)
var wrappers []*printfWrapper var wrappers []*printfWrapper
@ -209,7 +260,7 @@ func findPrintfLike(pass *analysis.Pass) (interface{}, error) {
fn, kind := printfNameAndKind(pass, call) fn, kind := printfNameAndKind(pass, call)
if kind != 0 { if kind != 0 {
checkPrintfFwd(pass, w, call, kind) checkPrintfFwd(pass, w, call, kind, res)
return true return true
} }
@ -232,20 +283,11 @@ func match(info *types.Info, arg ast.Expr, param *types.Var) bool {
return ok && info.ObjectOf(id) == param return ok && info.ObjectOf(id) == param
} }
type funcKind int
const (
kindUnknown funcKind = iota
kindPrintf = iota
kindPrint
kindErrorf
)
// checkPrintfFwd checks that a printf-forwarding wrapper is forwarding correctly. // checkPrintfFwd checks that a printf-forwarding wrapper is forwarding correctly.
// It diagnoses writing fmt.Printf(format, args) instead of fmt.Printf(format, args...). // It diagnoses writing fmt.Printf(format, args) instead of fmt.Printf(format, args...).
func checkPrintfFwd(pass *analysis.Pass, w *printfWrapper, call *ast.CallExpr, kind funcKind) { func checkPrintfFwd(pass *analysis.Pass, w *printfWrapper, call *ast.CallExpr, kind Kind, res *Result) {
matched := kind == kindPrint || matched := kind == KindPrint ||
kind != kindUnknown && len(call.Args) >= 2 && match(pass.TypesInfo, call.Args[len(call.Args)-2], w.format) kind != KindNone && len(call.Args) >= 2 && match(pass.TypesInfo, call.Args[len(call.Args)-2], w.format)
if !matched { if !matched {
return return
} }
@ -266,10 +308,10 @@ func checkPrintfFwd(pass *analysis.Pass, w *printfWrapper, call *ast.CallExpr, k
return return
} }
desc := "printf" desc := "printf"
if kind == kindPrint { if kind == KindPrint {
desc = "print" desc = "print"
} }
pass.Reportf(call.Pos(), "missing ... in args forwarded to %s-like function", desc) pass.ReportRangef(call, "missing ... in args forwarded to %s-like function", desc)
return return
} }
fn := w.obj fn := w.obj
@ -277,8 +319,9 @@ func checkPrintfFwd(pass *analysis.Pass, w *printfWrapper, call *ast.CallExpr, k
if !pass.ImportObjectFact(fn, &fact) { if !pass.ImportObjectFact(fn, &fact) {
fact.Kind = kind fact.Kind = kind
pass.ExportObjectFact(fn, &fact) pass.ExportObjectFact(fn, &fact)
res.funcs[fn] = kind
for _, caller := range w.callers { for _, caller := range w.callers {
checkPrintfFwd(pass, caller.w, caller.call, kind) checkPrintfFwd(pass, caller.w, caller.call, kind, res)
} }
} }
} }
@ -427,15 +470,15 @@ func checkCall(pass *analysis.Pass) {
call := n.(*ast.CallExpr) call := n.(*ast.CallExpr)
fn, kind := printfNameAndKind(pass, call) fn, kind := printfNameAndKind(pass, call)
switch kind { switch kind {
case kindPrintf, kindErrorf: case KindPrintf, KindErrorf:
checkPrintf(pass, kind, call, fn) checkPrintf(pass, kind, call, fn)
case kindPrint: case KindPrint:
checkPrint(pass, call, fn) checkPrint(pass, call, fn)
} }
}) })
} }
func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func, kind funcKind) { func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func, kind Kind) {
fn, _ = typeutil.Callee(pass.TypesInfo, call).(*types.Func) fn, _ = typeutil.Callee(pass.TypesInfo, call).(*types.Func)
if fn == nil { if fn == nil {
return nil, 0 return nil, 0
@ -448,11 +491,11 @@ func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func,
} }
if ok { if ok {
if fn.Name() == "Errorf" { if fn.Name() == "Errorf" {
kind = kindErrorf kind = KindErrorf
} else if strings.HasSuffix(fn.Name(), "f") { } else if strings.HasSuffix(fn.Name(), "f") {
kind = kindPrintf kind = KindPrintf
} else { } else {
kind = kindPrint kind = KindPrint
} }
return fn, kind return fn, kind
} }
@ -462,7 +505,7 @@ func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func,
return fn, fact.Kind return fn, fact.Kind
} }
return fn, kindUnknown return fn, KindNone
} }
// isFormatter reports whether t satisfies fmt.Formatter. // isFormatter reports whether t satisfies fmt.Formatter.
@ -504,7 +547,7 @@ type formatState struct {
} }
// checkPrintf checks a call to a formatted print routine such as Printf. // checkPrintf checks a call to a formatted print routine such as Printf.
func checkPrintf(pass *analysis.Pass, kind funcKind, call *ast.CallExpr, fn *types.Func) { func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.Func) {
format, idx := formatString(pass, call) format, idx := formatString(pass, call)
if idx < 0 { if idx < 0 {
if false { if false {
@ -542,7 +585,7 @@ func checkPrintf(pass *analysis.Pass, kind funcKind, call *ast.CallExpr, fn *typ
anyIndex = true anyIndex = true
} }
if state.verb == 'w' { if state.verb == 'w' {
if kind != kindErrorf { if kind != KindErrorf {
pass.Reportf(call.Pos(), "%s call has error-wrapping directive %%w", state.name) pass.Reportf(call.Pos(), "%s call has error-wrapping directive %%w", state.name)
return return
} }
@ -574,7 +617,7 @@ func checkPrintf(pass *analysis.Pass, kind funcKind, call *ast.CallExpr, fn *typ
if maxArgNum != len(call.Args) { if maxArgNum != len(call.Args) {
expect := maxArgNum - firstArg expect := maxArgNum - firstArg
numArgs := len(call.Args) - firstArg numArgs := len(call.Args) - firstArg
pass.Reportf(call.Pos(), "%s call needs %v but has %v", fn.Name(), count(expect, "arg"), count(numArgs, "arg")) pass.ReportRangef(call, "%s call needs %v but has %v", fn.Name(), count(expect, "arg"), count(numArgs, "arg"))
} }
} }
@ -615,13 +658,13 @@ func (s *formatState) parseIndex() bool {
ok = false ok = false
s.nbytes = strings.Index(s.format, "]") s.nbytes = strings.Index(s.format, "]")
if s.nbytes < 0 { if s.nbytes < 0 {
s.pass.Reportf(s.call.Pos(), "%s format %s is missing closing ]", s.name, s.format) s.pass.ReportRangef(s.call, "%s format %s is missing closing ]", s.name, s.format)
return false return false
} }
} }
arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32) arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32)
if err != nil || !ok || arg32 <= 0 || arg32 > int64(len(s.call.Args)-s.firstArg) { if err != nil || !ok || arg32 <= 0 || arg32 > int64(len(s.call.Args)-s.firstArg) {
s.pass.Reportf(s.call.Pos(), "%s format has invalid argument index [%s]", s.name, s.format[start:s.nbytes]) s.pass.ReportRangef(s.call, "%s format has invalid argument index [%s]", s.name, s.format[start:s.nbytes])
return false return false
} }
s.nbytes++ // skip ']' s.nbytes++ // skip ']'
@ -698,7 +741,7 @@ func parsePrintfVerb(pass *analysis.Pass, call *ast.CallExpr, name, format strin
return nil return nil
} }
if state.nbytes == len(state.format) { if state.nbytes == len(state.format) {
pass.Reportf(call.Pos(), "%s format %s is missing verb at end of string", name, state.format) pass.ReportRangef(call.Fun, "%s format %s is missing verb at end of string", name, state.format)
return nil return nil
} }
verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:]) verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:])
@ -748,7 +791,7 @@ var printVerbs = []printVerb{
// '#' is alternate format for several verbs. // '#' is alternate format for several verbs.
// ' ' is spacer for numbers // ' ' is spacer for numbers
{'%', noFlag, 0}, {'%', noFlag, 0},
{'b', numFlag, argInt | argFloat | argComplex | argPointer}, {'b', sharpNumFlag, argInt | argFloat | argComplex | argPointer},
{'c', "-", argRune | argInt}, {'c', "-", argRune | argInt},
{'d', numFlag, argInt | argPointer}, {'d', numFlag, argInt | argPointer},
{'e', sharpNumFlag, argFloat | argComplex}, {'e', sharpNumFlag, argFloat | argComplex},
@ -794,7 +837,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
if !formatter { if !formatter {
if !found { if !found {
pass.Reportf(call.Pos(), "%s format %s has unknown verb %c", state.name, state.format, state.verb) pass.ReportRangef(call, "%s format %s has unknown verb %c", state.name, state.format, state.verb)
return false return false
} }
for _, flag := range state.flags { for _, flag := range state.flags {
@ -804,7 +847,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
continue continue
} }
if !strings.ContainsRune(v.flags, rune(flag)) { if !strings.ContainsRune(v.flags, rune(flag)) {
pass.Reportf(call.Pos(), "%s format %s has unrecognized flag %c", state.name, state.format, flag) pass.ReportRangef(call, "%s format %s has unrecognized flag %c", state.name, state.format, flag)
return false return false
} }
} }
@ -823,7 +866,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
} }
arg := call.Args[argNum] arg := call.Args[argNum]
if !matchArgType(pass, argInt, nil, arg) { if !matchArgType(pass, argInt, nil, arg) {
pass.Reportf(call.Pos(), "%s format %s uses non-int %s as argument of *", state.name, state.format, analysisutil.Format(pass.Fset, arg)) pass.ReportRangef(call, "%s format %s uses non-int %s as argument of *", state.name, state.format, analysisutil.Format(pass.Fset, arg))
return false return false
} }
} }
@ -837,7 +880,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
} }
arg := call.Args[argNum] arg := call.Args[argNum]
if isFunctionValue(pass, arg) && state.verb != 'p' && state.verb != 'T' { if isFunctionValue(pass, arg) && state.verb != 'p' && state.verb != 'T' {
pass.Reportf(call.Pos(), "%s format %s arg %s is a func value, not called", state.name, state.format, analysisutil.Format(pass.Fset, arg)) 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 return false
} }
if !matchArgType(pass, v.typ, nil, arg) { if !matchArgType(pass, v.typ, nil, arg) {
@ -845,11 +888,11 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
if typ := pass.TypesInfo.Types[arg].Type; typ != nil { if typ := pass.TypesInfo.Types[arg].Type; typ != nil {
typeString = typ.String() typeString = typ.String()
} }
pass.Reportf(call.Pos(), "%s format %s has arg %s of wrong type %s", state.name, state.format, analysisutil.Format(pass.Fset, arg), typeString) pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s", state.name, state.format, analysisutil.Format(pass.Fset, arg), typeString)
return false return false
} }
if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) && recursiveStringer(pass, arg) { if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) && recursiveStringer(pass, arg) {
pass.Reportf(call.Pos(), "%s format %s with arg %s causes recursive String method call", state.name, state.format, analysisutil.Format(pass.Fset, arg)) pass.ReportRangef(call, "%s format %s with arg %s causes recursive String method call", state.name, state.format, analysisutil.Format(pass.Fset, arg))
return false return false
} }
return true return true
@ -935,7 +978,7 @@ func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, formatArg int, sta
// There are bad indexes in the format or there are fewer arguments than the format needs. // There are bad indexes in the format or there are fewer arguments than the format needs.
// This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi".
arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed. arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed.
pass.Reportf(call.Pos(), "%s format %s reads arg #%d, but call has %v", state.name, state.format, arg, count(len(call.Args)-state.firstArg, "arg")) pass.ReportRangef(call, "%s format %s reads arg #%d, but call has %v", state.name, state.format, arg, count(len(call.Args)-state.firstArg, "arg"))
return false return false
} }
@ -986,7 +1029,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
if sel, ok := call.Args[0].(*ast.SelectorExpr); ok { if sel, ok := call.Args[0].(*ast.SelectorExpr); ok {
if x, ok := sel.X.(*ast.Ident); ok { if x, ok := sel.X.(*ast.Ident); ok {
if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") {
pass.Reportf(call.Pos(), "%s does not take io.Writer but has first arg %s", fn.Name(), analysisutil.Format(pass.Fset, call.Args[0])) pass.ReportRangef(call, "%s does not take io.Writer but has first arg %s", fn.Name(), analysisutil.Format(pass.Fset, call.Args[0]))
} }
} }
} }
@ -1000,7 +1043,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
if strings.Contains(s, "%") { if strings.Contains(s, "%") {
m := printFormatRE.FindStringSubmatch(s) m := printFormatRE.FindStringSubmatch(s)
if m != nil { if m != nil {
pass.Reportf(call.Pos(), "%s call has possible formatting directive %s", fn.Name(), m[0]) pass.ReportRangef(call, "%s call has possible formatting directive %s", fn.Name(), m[0])
} }
} }
} }
@ -1010,16 +1053,16 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING { if lit, ok := arg.(*ast.BasicLit); ok && lit.Kind == token.STRING {
str, _ := strconv.Unquote(lit.Value) str, _ := strconv.Unquote(lit.Value)
if strings.HasSuffix(str, "\n") { if strings.HasSuffix(str, "\n") {
pass.Reportf(call.Pos(), "%s arg list ends with redundant newline", fn.Name()) pass.ReportRangef(call, "%s arg list ends with redundant newline", fn.Name())
} }
} }
} }
for _, arg := range args { for _, arg := range args {
if isFunctionValue(pass, arg) { if isFunctionValue(pass, arg) {
pass.Reportf(call.Pos(), "%s arg %s is a func value, not called", fn.Name(), analysisutil.Format(pass.Fset, arg)) pass.ReportRangef(call, "%s arg %s is a func value, not called", fn.Name(), analysisutil.Format(pass.Fset, arg))
} }
if recursiveStringer(pass, arg) { if recursiveStringer(pass, arg) {
pass.Reportf(call.Pos(), "%s arg %s causes recursive call to String method", fn.Name(), analysisutil.Format(pass.Fset, arg)) pass.ReportRangef(call, "%s arg %s causes recursive call to String method", fn.Name(), analysisutil.Format(pass.Fset, arg))
} }
} }
} }

View File

@ -94,6 +94,6 @@ func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
size := 8 * pass.TypesSizes.Sizeof(t) size := 8 * pass.TypesSizes.Sizeof(t)
if amt >= size { if amt >= size {
ident := analysisutil.Format(pass.Fset, x) ident := analysisutil.Format(pass.Fset, x)
pass.Reportf(node.Pos(), "%s (%d bits) too small for shift of %d", ident, size, amt) pass.ReportRangef(node, "%s (%d bits) too small for shift of %d", ident, size, amt)
} }
} }

View File

@ -141,7 +141,7 @@ func canonicalMethod(pass *analysis.Pass, id *ast.Ident) {
actual = strings.TrimPrefix(actual, "func") actual = strings.TrimPrefix(actual, "func")
actual = id.Name + actual actual = id.Name + actual
pass.Reportf(id.Pos(), "method %s should have signature %s", actual, expectFmt) pass.ReportRangef(id, "method %s should have signature %s", actual, expectFmt)
} }
} }

View File

@ -189,7 +189,7 @@ func (d *deadState) findDead(stmt ast.Stmt) {
case *ast.EmptyStmt: case *ast.EmptyStmt:
// do not warn about unreachable empty statements // do not warn about unreachable empty statements
default: default:
d.pass.Reportf(stmt.Pos(), "unreachable code") d.pass.ReportRangef(stmt, "unreachable code")
d.reachable = true // silence error about next statement d.reachable = true // silence error about next statement
} }
} }

View File

@ -45,7 +45,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
if hasBasicType(pass.TypesInfo, x.Fun, types.UnsafePointer) && if hasBasicType(pass.TypesInfo, x.Fun, types.UnsafePointer) &&
hasBasicType(pass.TypesInfo, x.Args[0], types.Uintptr) && hasBasicType(pass.TypesInfo, x.Args[0], types.Uintptr) &&
!isSafeUintptr(pass.TypesInfo, x.Args[0]) { !isSafeUintptr(pass.TypesInfo, x.Args[0]) {
pass.Reportf(x.Pos(), "possible misuse of unsafe.Pointer") pass.ReportRangef(x, "possible misuse of unsafe.Pointer")
} }
}) })
return nil, nil return nil, nil

View File

@ -43,7 +43,7 @@ golang.org/x/mod/sumdb/tlog
## explicit ## explicit
golang.org/x/sys/unix golang.org/x/sys/unix
golang.org/x/sys/windows golang.org/x/sys/windows
# golang.org/x/tools v0.0.0-20191018203202-04252eccb9d5 # golang.org/x/tools v0.0.0-20191104222624-6b7b8b79ae80
## explicit ## explicit
golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis
golang.org/x/tools/go/analysis/internal/analysisflags golang.org/x/tools/go/analysis/internal/analysisflags