go/analysis: rename reportNodef to ReportRangef

This adds (or makes exported) a convenience function for reporting diagnostics with a
node directly (which is what folks usually want).

Change-Id: Ieb7ef2703f99d3a24ba7e48a779be62a7761cd0c
Reviewed-on: https://go-review.googlesource.com/c/tools/+/180237
Run-TryBot: Michael Matloob <matloob@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Michael Matloob 2019-06-03 13:08:31 -04:00
parent b394bd8bba
commit e3efbe408c
20 changed files with 60 additions and 54 deletions

View File

@ -163,13 +163,19 @@ func (pass *Pass) Reportf(pos token.Pos, format string, args ...interface{}) {
pass.Report(Diagnostic{Pos: pos, Message: msg})
}
// reportNodef is a helper function that reports a Diagnostic using the
// range denoted by the AST node.
//
// WARNING: This is an experimental API and may change in the future.
func (pass *Pass) reportNodef(node ast.Node, format string, args ...interface{}) {
// The Range interface provides a range. It's equivalent to and satisfied by
// ast.Node.
type Range interface {
Pos() token.Pos // position of first character belonging to the node
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...)
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 {

View File

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

View File

@ -110,7 +110,7 @@ func check64BitAlignment(pass *analysis.Pass, funcName string, arg ast.Expr) {
return // 64-bit aligned
}
pass.Reportf(arg.Pos(), "address of non 64-bit aligned field .%s passed to atomic.%s", tvar.Name(), funcName)
pass.ReportRangef(arg, "address of non 64-bit aligned field .%s passed to atomic.%s", tvar.Name(), funcName)
}
// imports reports whether pkg has path among its direct imports.

View File

@ -100,7 +100,7 @@ func (op boolOp) checkRedundant(pass *analysis.Pass, exprs []ast.Expr) {
for _, e := range exprs {
efmt := analysisutil.Format(pass.Fset, e)
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 {
seen[efmt] = true
}
@ -147,7 +147,7 @@ func (op boolOp) checkSuspect(pass *analysis.Pass, exprs []ast.Expr) {
if prev, found := seen[xfmt]; found {
// checkRedundant handles the case in which 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 {
seen[xfmt] = efmt

View File

@ -97,7 +97,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
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
}

View File

@ -74,7 +74,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
func checkCopyLocksAssign(pass *analysis.Pass, as *ast.AssignStmt) {
for i, x := range as.Rhs {
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)
for i, x := range valueSpec.Values {
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
}
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) {
for _, x := range rs.Results {
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 {
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 {
expr := recv.List[0].Type
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 {
expr := field.Type
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

@ -45,7 +45,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
return
}
if fn.FullName() == "reflect.DeepEqual" && hasError(pass, call.Args[0]) && hasError(pass, call.Args[1]) {
pass.Reportf(call.Pos(), "avoid using reflect.DeepEqual with errors")
pass.ReportRangef(call, "avoid using reflect.DeepEqual with errors")
}
})
return nil, nil

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
}
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

View File

@ -85,7 +85,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
}
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
})

View File

@ -119,7 +119,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
}
for _, v := range vars {
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)
}
}

View File

@ -121,7 +121,7 @@ func runFunc(pass *analysis.Pass, node ast.Node) {
}
if id != nil {
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",
n.(*ast.SelectorExpr).Sel.Name)
} 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 {
if ret := lostCancelPath(pass, g, v, stmt, sig); ret != nil {
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.Reportf(ret.Pos(), "this return statement may be reached without using the %s var defined on line %d", v.Name(), lineno)
pass.ReportRangef(stmt, "the %s function is not used on all paths (possible context leak)", v.Name())
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
}
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
}

View File

@ -66,7 +66,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
eq := strings.IndexByte(pair, '=')
result[pair[:eq]] = pair[1+eq:]
}
pass.Reportf(spec.Pos(), "%s", strings.Join(fact, " "))
pass.ReportRangef(spec, "%s", strings.Join(fact, " "))
}
}

View File

@ -311,7 +311,7 @@ func checkPrintfFwd(pass *analysis.Pass, w *printfWrapper, call *ast.CallExpr, k
if kind == KindPrint {
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
}
fn := w.obj
@ -617,7 +617,7 @@ func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.F
if maxArgNum != len(call.Args) {
expect := maxArgNum - 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"))
}
}
@ -658,13 +658,13 @@ func (s *formatState) parseIndex() bool {
ok = false
s.nbytes = strings.Index(s.format, "]")
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
}
}
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) {
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
}
s.nbytes++ // skip ']'
@ -741,7 +741,7 @@ func parsePrintfVerb(pass *analysis.Pass, call *ast.CallExpr, name, format strin
return nil
}
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
}
verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:])
@ -837,7 +837,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
if !formatter {
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
}
for _, flag := range state.flags {
@ -847,7 +847,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
continue
}
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
}
}
@ -866,7 +866,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
}
arg := call.Args[argNum]
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
}
}
@ -880,7 +880,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
}
arg := call.Args[argNum]
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
}
if !matchArgType(pass, v.typ, nil, arg) {
@ -888,11 +888,11 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (o
if typ := pass.TypesInfo.Types[arg].Type; typ != nil {
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
}
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 true
@ -978,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.
// 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.
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
}
@ -1029,7 +1029,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
if sel, ok := call.Args[0].(*ast.SelectorExpr); ok {
if x, ok := sel.X.(*ast.Ident); ok {
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]))
}
}
}
@ -1043,7 +1043,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
if strings.Contains(s, "%") {
m := printFormatRE.FindStringSubmatch(s)
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])
}
}
}
@ -1053,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 {
str, _ := strconv.Unquote(lit.Value)
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 {
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) {
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

@ -160,7 +160,7 @@ func checkShadowAssignment(pass *analysis.Pass, spans map[types.Object]span, a *
for _, expr := range a.Lhs {
ident, ok := expr.(*ast.Ident)
if !ok {
pass.Reportf(expr.Pos(), "invalid AST: short variable declaration of non-identifier")
pass.ReportRangef(expr, "invalid AST: short variable declaration of non-identifier")
return
}
checkShadowing(pass, spans, ident)
@ -182,7 +182,7 @@ func idiomaticShortRedecl(pass *analysis.Pass, a *ast.AssignStmt) bool {
for i, expr := range a.Lhs {
lhs, ok := expr.(*ast.Ident)
if !ok {
pass.Reportf(expr.Pos(), "invalid AST: short variable declaration of non-identifier")
pass.ReportRangef(expr, "invalid AST: short variable declaration of non-identifier")
return true // Don't do any more processing.
}
switch rhs := a.Rhs[i].(type) {
@ -230,7 +230,7 @@ func checkShadowDecl(pass *analysis.Pass, spans map[types.Object]span, d *ast.Ge
for _, spec := range d.Specs {
valueSpec, ok := spec.(*ast.ValueSpec)
if !ok {
pass.Reportf(spec.Pos(), "invalid AST: var GenDecl not ValueSpec")
pass.ReportRangef(spec, "invalid AST: var GenDecl not ValueSpec")
return
}
// Don't complain about deliberate redeclarations of the form
@ -274,7 +274,7 @@ func checkShadowing(pass *analysis.Pass, spans map[types.Object]span, ident *ast
// the shadowing identifier.
span, ok := spans[shadowed]
if !ok {
pass.Reportf(ident.Pos(), "internal error: no range for %q", ident.Name)
pass.ReportRangef(ident, "internal error: no range for %q", ident.Name)
return
}
if !span.contains(ident.Pos()) {
@ -284,6 +284,6 @@ func checkShadowing(pass *analysis.Pass, spans map[types.Object]span, ident *ast
// Don't complain if the types differ: that implies the programmer really wants two different things.
if types.Identical(obj.Type(), shadowed.Type()) {
line := pass.Fset.Position(shadowed.Pos()).Line
pass.Reportf(ident.Pos(), "declaration of %q shadows declaration at line %d", obj.Name(), line)
pass.ReportRangef(ident, "declaration of %q shadows declaration at line %d", obj.Name(), line)
}
}

View File

@ -94,6 +94,6 @@ func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) {
size := 8 * pass.TypesSizes.Sizeof(t)
if amt >= size {
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 = 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:
// do not warn about unreachable empty statements
default:
d.pass.Reportf(stmt.Pos(), "unreachable code")
d.pass.ReportRangef(stmt, "unreachable code")
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) &&
hasBasicType(pass.TypesInfo, x.Args[0], types.Uintptr) &&
!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

View File

@ -10,7 +10,7 @@ func Testbad(t *testing.T) { //@diag("", "tests", "Testbad has malformed name: f
var x sync.Mutex
_ = x //@diag("x", "copylocks", "assignment copies lock value to _: sync.Mutex")
printfWrapper("%s") //@diag("printfWrapper", "printf", "printfWrapper format %s reads arg #1, but call has 0 args")
printfWrapper("%s") //@diag(re`printfWrapper\(.*\)`, "printf", "printfWrapper format %s reads arg #1, but call has 0 args")
}
func printfWrapper(format string, args ...interface{}) {