Skip to content

Commit c9fbe0f

Browse files
valyalarobpike
authored andcommitted
cmd/vet: properly handle indexed arguments in printf
Fixes #15884 Change-Id: I33d98db861d74e3c37a546efaf83ce6f2f76d335 Reviewed-on: https://go-review.googlesource.com/24391 Run-TryBot: Rob Pike <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Rob Pike <[email protected]>
1 parent 4955147 commit c9fbe0f

File tree

2 files changed

+28
-18
lines changed

2 files changed

+28
-18
lines changed

src/cmd/vet/print.go

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ type formatState struct {
194194
name string // Printf, Sprintf etc.
195195
flags []byte // the list of # + etc.
196196
argNums []int // the successive argument numbers that are consumed, adjusted to refer to actual arg in call
197-
indexed bool // whether an indexing expression appears: %[1]d.
198197
firstArg int // Index of first argument after the format in the Printf call.
199198
// Used only during parse.
200199
file *File
@@ -223,7 +222,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string) {
223222
}
224223
// Hard part: check formats against args.
225224
argNum := firstArg
226-
indexed := false
225+
maxArgNum := firstArg
227226
for i, w := 0, 0; i < len(format); i += w {
228227
w = 1
229228
if format[i] == '%' {
@@ -232,26 +231,27 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string) {
232231
return
233232
}
234233
w = len(state.format)
235-
if state.indexed {
236-
indexed = true
237-
}
238234
if !f.okPrintfArg(call, state) { // One error per format is enough.
239235
return
240236
}
241237
if len(state.argNums) > 0 {
242238
// Continue with the next sequential argument.
243239
argNum = state.argNums[len(state.argNums)-1] + 1
244240
}
241+
for _, n := range state.argNums {
242+
if n >= maxArgNum {
243+
maxArgNum = n + 1
244+
}
245+
}
245246
}
246247
}
247248
// Dotdotdot is hard.
248-
if call.Ellipsis.IsValid() && argNum >= len(call.Args)-1 {
249+
if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 {
249250
return
250251
}
251-
// If the arguments were direct indexed, we assume the programmer knows what's up.
252-
// Otherwise, there should be no leftover arguments.
253-
if !indexed && argNum != len(call.Args) {
254-
expect := argNum - firstArg
252+
// There should be no leftover arguments.
253+
if maxArgNum != len(call.Args) {
254+
expect := maxArgNum - firstArg
255255
numArgs := len(call.Args) - firstArg
256256
f.Badf(call.Pos(), "wrong number of args for format in %s call: %d needed but %d args", name, expect, numArgs)
257257
}
@@ -286,17 +286,20 @@ func (s *formatState) parseIndex() bool {
286286
return true
287287
}
288288
// Argument index present.
289-
s.indexed = true
290289
s.nbytes++ // skip '['
291290
start := s.nbytes
292291
s.scanNum()
293292
if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' {
294-
s.file.Badf(s.call.Pos(), "illegal syntax for printf argument index")
293+
end := strings.Index(s.format, "]")
294+
if end < 0 {
295+
end = len(s.format)
296+
}
297+
s.file.Badf(s.call.Pos(), "bad syntax for printf argument index: [%s]", s.format[start:end])
295298
return false
296299
}
297300
arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32)
298301
if err != nil {
299-
s.file.Badf(s.call.Pos(), "illegal syntax for printf argument index: %s", err)
302+
s.file.Badf(s.call.Pos(), "bad syntax for printf argument index: %s", err)
300303
return false
301304
}
302305
s.nbytes++ // skip ']'
@@ -349,14 +352,12 @@ func (f *File) parsePrintfVerb(call *ast.CallExpr, name, format string, firstArg
349352
argNum: argNum,
350353
argNums: make([]int, 0, 1),
351354
nbytes: 1, // There's guaranteed to be a percent sign.
352-
indexed: false,
353355
firstArg: firstArg,
354356
file: f,
355357
call: call,
356358
}
357359
// There may be flags.
358360
state.parseFlags()
359-
indexPending := false
360361
// There may be an index.
361362
if !state.parseIndex() {
362363
return nil
@@ -370,7 +371,7 @@ func (f *File) parsePrintfVerb(call *ast.CallExpr, name, format string, firstArg
370371
return nil
371372
}
372373
// Now a verb, possibly prefixed by an index (which we may already have).
373-
if !indexPending && !state.parseIndex() {
374+
if !state.indexPending && !state.parseIndex() {
374375
return nil
375376
}
376377
if state.nbytes == len(state.format) {

src/cmd/vet/testdata/print.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@ func PrintfTests() {
174174
Printf("%[2]*.[1]*[3]d", 2, 3, 4)
175175
fmt.Fprintf(os.Stderr, "%[2]*.[1]*[3]d", 2, 3, 4) // Use Fprintf to make sure we count arguments correctly.
176176
// Bad argument reorderings.
177-
Printf("%[xd", 3) // ERROR "illegal syntax for printf argument index"
178-
Printf("%[x]d", 3) // ERROR "illegal syntax for printf argument index"
177+
Printf("%[xd", 3) // ERROR "bad syntax for printf argument index: \[xd\]"
178+
Printf("%[x]d", 3) // ERROR "bad syntax for printf argument index: \[x\]"
179179
Printf("%[3]*s", "hi", 2) // ERROR "missing argument for Printf.* reads arg 3, have only 2"
180180
_ = fmt.Sprintf("%[3]d", 2) // ERROR "missing argument for Sprintf.* reads arg 3, have only 1"
181181
Printf("%[2]*.[1]*[3]d", 2, "hi", 4) // ERROR "arg .hi. for \* in printf format not of type int"
@@ -249,6 +249,15 @@ func PrintfTests() {
249249
ss.log(someFunction) // OK
250250
ss.log(someFunction, "bar", 1.33) // OK
251251
ss.log(someFunction, someFunction) // ERROR "arg someFunction in log call is a function value, not a function call"
252+
253+
// indexed arguments
254+
Printf("%d %[3]d %d %[2]d", 1, 2, 3, 4) // OK
255+
Printf("%d %[0]d %d %[2]d", 1, 2, 3, 4) // ERROR "indexes start at 1"
256+
Printf("%d %[3]d %d %[-2]d", 1, 2, 3, 4) // ERROR "bad syntax for printf argument index: \[-2\]"
257+
Printf("%d %[3]d %d %[2234234234234]d", 1, 2, 3, 4) // ERROR "bad syntax for printf argument index: .+ value out of range"
258+
Printf("%d %[3]d %d %[2]d", 1, 2, 3) // ERROR "format reads arg 4, have only 3 args"
259+
Printf("%d %[3]d %d %[2]d", 1, 2, 3, 4, 5) // ERROR "wrong number of args for format in Printf call: 4 needed but 5 args"
260+
Printf("%[1][3]d", 1, 2) // ERROR "unrecognized printf verb '\['"
252261
}
253262

254263
type someStruct struct{}

0 commit comments

Comments
 (0)