@@ -67,7 +67,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) {
67
67
if ! ok {
68
68
// Too hard to check.
69
69
if * verbose {
70
- f .Warn (call .Pos (), "can't check args for call to" , name )
70
+ f .Warn (call .Pos (), "can't check non-literal format in call to" , name )
71
71
}
72
72
return
73
73
}
@@ -85,7 +85,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) {
85
85
for i , w := 0 , 0 ; i < len (lit .Value ); i += w {
86
86
w = 1
87
87
if lit .Value [i ] == '%' {
88
- nbytes , nargs := parsePrintfVerb (lit .Value [i :])
88
+ nbytes , nargs := f . parsePrintfVerb (call , lit .Value [i :])
89
89
w = nbytes
90
90
numArgs += nargs
91
91
}
@@ -99,15 +99,17 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string, skip int) {
99
99
// parsePrintfVerb returns the number of bytes and number of arguments
100
100
// consumed by the Printf directive that begins s, including its percent sign
101
101
// and verb.
102
- func parsePrintfVerb (s string ) (nbytes , nargs int ) {
102
+ func ( f * File ) parsePrintfVerb (call * ast. CallExpr , s string ) (nbytes , nargs int ) {
103
103
// There's guaranteed a percent sign.
104
+ flags := make ([]byte , 0 , 5 )
104
105
nbytes = 1
105
106
end := len (s )
106
107
// There may be flags.
107
108
FlagLoop:
108
109
for nbytes < end {
109
110
switch s [nbytes ] {
110
111
case '#' , '0' , '+' , '-' , ' ' :
112
+ flags = append (flags , s [nbytes ])
111
113
nbytes ++
112
114
default :
113
115
break FlagLoop
@@ -127,6 +129,7 @@ FlagLoop:
127
129
getNum ()
128
130
// If there's a period, there may be a precision.
129
131
if nbytes < end && s [nbytes ] == '.' {
132
+ flags = append (flags , '.' ) // Treat precision as a flag.
130
133
nbytes ++
131
134
getNum ()
132
135
}
@@ -135,10 +138,70 @@ FlagLoop:
135
138
nbytes += w
136
139
if c != '%' {
137
140
nargs ++
141
+ f .checkPrintfVerb (call , c , flags )
138
142
}
139
143
return
140
144
}
141
145
146
+ type printVerb struct {
147
+ verb rune
148
+ flags string // known flags are all ASCII
149
+ }
150
+
151
+ // Common flag sets for printf verbs.
152
+ const (
153
+ numFlag = " -+.0"
154
+ sharpNumFlag = " -+.0#"
155
+ allFlags = " -+.0#"
156
+ )
157
+
158
+ // printVerbs identifies which flags are known to printf for each verb.
159
+ // TODO: A type that implements Formatter may do what it wants, and govet
160
+ // will complain incorrectly.
161
+ var printVerbs = []printVerb {
162
+ // '-' is a width modifier, always valid.
163
+ // '.' is a precision for float, max width for strings.
164
+ // '+' is required sign for numbers, Go format for %v.
165
+ // '#' is alternate format for several verbs.
166
+ // ' ' is spacer for numbers
167
+ {'b' , numFlag },
168
+ {'c' , "-" },
169
+ {'d' , numFlag },
170
+ {'e' , "-." },
171
+ {'E' , numFlag },
172
+ {'f' , numFlag },
173
+ {'F' , numFlag },
174
+ {'g' , numFlag },
175
+ {'G' , numFlag },
176
+ {'o' , sharpNumFlag },
177
+ {'p' , "-#" },
178
+ {'q' , "-+#." },
179
+ {'s' , "-." },
180
+ {'t' , "-" },
181
+ {'T' , "-" },
182
+ {'U' , "-#" },
183
+ {'v' , allFlags },
184
+ {'x' , sharpNumFlag },
185
+ {'X' , sharpNumFlag },
186
+ }
187
+
188
+ const printfVerbs = "bcdeEfFgGopqstTvxUX"
189
+
190
+ func (f * File ) checkPrintfVerb (call * ast.CallExpr , verb rune , flags []byte ) {
191
+ // Linear scan is fast enough for a small list.
192
+ for _ , v := range printVerbs {
193
+ if v .verb == verb {
194
+ for _ , flag := range flags {
195
+ if ! strings .ContainsRune (v .flags , rune (flag )) {
196
+ f .Badf (call .Pos (), "unrecognized printf flag for verb %q: %q" , verb , flag )
197
+ }
198
+ }
199
+ return
200
+ }
201
+ }
202
+ f .Badf (call .Pos (), "unrecognized printf verb %q" , verb )
203
+ }
204
+
142
205
// checkPrint checks a call to an unformatted print routine such as Println.
143
206
// The skip argument records how many arguments to ignore; that is,
144
207
// call.Args[skip] is the first argument to be printed.
@@ -183,6 +246,8 @@ func BadFunctionUsedInTests() {
183
246
f := new (File )
184
247
f .Warn (0 , "%s" , "hello" , 3 ) // ERROR "possible formatting directive in Warn call"
185
248
f .Warnf (0 , "%s" , "hello" , 3 ) // ERROR "wrong number of args in Warnf call"
249
+ f .Warnf (0 , "%r" , "hello" ) // ERROR "unrecognized printf verb"
250
+ f .Warnf (0 , "%#s" , "hello" ) // ERROR "unrecognized printf flag"
186
251
}
187
252
188
253
type BadTypeUsedInTests struct {
0 commit comments