Commit 8138654a authored by Rob Pike's avatar Rob Pike

govet: handle '*' in print format strings.

While we're on govet, fix a couple of mistakes in a test.

Fixes #1592.

R=rsc
CC=golang-dev
https://golang.org/cl/4239071
parent f3ed1ad5
......@@ -18,6 +18,7 @@ import (
"path/filepath"
"strconv"
"strings"
"utf8"
)
var verbose = flag.Bool("v", false, "verbose")
......@@ -265,23 +266,65 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) {
}
// Hard part: check formats against args.
// Trivial but useful test: count.
numPercent := 0
for i := 0; i < len(lit.Value); i++ {
numArgs := 0
for i, w := 0, 0; i < len(lit.Value); i += w {
w = 1
if lit.Value[i] == '%' {
if i+1 < len(lit.Value) && lit.Value[i+1] == '%' {
// %% doesn't count.
i++
} else {
numPercent++
}
nbytes, nargs := parsePrintfVerb(lit.Value[i:])
w = nbytes
numArgs += nargs
}
}
expect := len(call.Args) - (skip + 1)
if numPercent != expect {
f.Badf(call.Pos(), "wrong number of formatting directives in %s call: %d percent(s) for %d args", name, numPercent, expect)
if numArgs != expect {
f.Badf(call.Pos(), "wrong number of args in %s call: %d needed but %d args", name, numArgs, expect)
}
}
// parsePrintfVerb returns the number of bytes and number of arguments
// consumed by the Printf directive that begins s, including its percent sign
// and verb.
func parsePrintfVerb(s []byte) (nbytes, nargs int) {
// There's guaranteed a percent sign.
nbytes = 1
end := len(s)
// There may be flags
FlagLoop:
for nbytes < end {
switch s[nbytes] {
case '#', '0', '+', '-', ' ':
nbytes++
default:
break FlagLoop
}
}
getNum := func() {
if nbytes < end && s[nbytes] == '*' {
nbytes++
nargs++
} else {
for nbytes < end && '0' <= s[nbytes] && s[nbytes] <= '9' {
nbytes++
}
}
}
// There may be a width
getNum()
// If there's a period, there may be a precision.
if nbytes < end && s[nbytes] == '.' {
nbytes++
getNum()
}
// Now a verb.
c, w := utf8.DecodeRune(s[nbytes:])
nbytes += w
if c != '%' {
nargs++
}
return
}
var terminalNewline = []byte(`\n"`) // \n at end of interpreted string
// checkPrint checks a call to an unformatted print routine such as Println.
......@@ -320,6 +363,8 @@ func BadFunctionUsedInTests() {
fmt.Println("%s", "hi") // % in call to Println
fmt.Printf("%s", "hi", 3) // wrong # percents
fmt.Printf("%s%%%d", "hi", 3) // right # percents
fmt.Printf("%.*d", 3, 3) // right # percents, with a *
fmt.Printf("%.*d", 3, 3, 3) // wrong # percents, with a *
Printf("now is the time", "buddy") // no %s
f := new(File)
f.Warn(0, "%s", "hello", 3) // % in call to added function
......
......@@ -820,12 +820,12 @@ func testScanInts(t *testing.T, scan func(*RecursiveInt, *bytes.Buffer) os.Error
i := 1
for ; r != nil; r = r.next {
if r.i != i {
t.Fatal("bad scan: expected %d got %d", i, r.i)
t.Fatalf("bad scan: expected %d got %d", i, r.i)
}
i++
}
if i-1 != intCount {
t.Fatal("bad scan count: expected %d got %d", intCount, i-1)
t.Fatalf("bad scan count: expected %d got %d", intCount, i-1)
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment