1 | // Copyright 2010 The Go Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style |
3 | // license that can be found in the LICENSE file. |
4 | |
5 | // Package printf defines an Analyzer that checks consistency |
6 | // of Printf format strings and arguments. |
7 | package printf |
8 | |
9 | import ( |
10 | "bytes" |
11 | "fmt" |
12 | "go/ast" |
13 | "go/constant" |
14 | "go/token" |
15 | "go/types" |
16 | "reflect" |
17 | "regexp" |
18 | "sort" |
19 | "strconv" |
20 | "strings" |
21 | "unicode/utf8" |
22 | |
23 | "golang.org/x/tools/go/analysis" |
24 | "golang.org/x/tools/go/analysis/passes/inspect" |
25 | "golang.org/x/tools/go/analysis/passes/internal/analysisutil" |
26 | "golang.org/x/tools/go/ast/inspector" |
27 | "golang.org/x/tools/go/types/typeutil" |
28 | "golang.org/x/tools/internal/typeparams" |
29 | ) |
30 | |
31 | func init() { |
32 | Analyzer.Flags.Var(isPrint, "funcs", "comma-separated list of print function names to check") |
33 | } |
34 | |
35 | var Analyzer = &analysis.Analyzer{ |
36 | Name: "printf", |
37 | Doc: Doc, |
38 | Requires: []*analysis.Analyzer{inspect.Analyzer}, |
39 | Run: run, |
40 | ResultType: reflect.TypeOf((*Result)(nil)), |
41 | FactTypes: []analysis.Fact{new(isWrapper)}, |
42 | } |
43 | |
44 | const Doc = `check consistency of Printf format strings and arguments |
45 | |
46 | The check applies to known functions (for example, those in package fmt) |
47 | as well as any detected wrappers of known functions. |
48 | |
49 | A function that wants to avail itself of printf checking but is not |
50 | found by this analyzer's heuristics (for example, due to use of |
51 | dynamic calls) can insert a bogus call: |
52 | |
53 | if false { |
54 | _ = fmt.Sprintf(format, args...) // enable printf checking |
55 | } |
56 | |
57 | The -funcs flag specifies a comma-separated list of names of additional |
58 | known formatting functions or methods. If the name contains a period, |
59 | it must denote a specific function using one of the following forms: |
60 | |
61 | dir/pkg.Function |
62 | dir/pkg.Type.Method |
63 | (*dir/pkg.Type).Method |
64 | |
65 | Otherwise the name is interpreted as a case-insensitive unqualified |
66 | identifier such as "errorf". Either way, if a listed name ends in f, the |
67 | function is assumed to be Printf-like, taking a format string before the |
68 | argument list. Otherwise it is assumed to be Print-like, taking a list |
69 | of arguments with no format string. |
70 | ` |
71 | |
72 | // Kind is a kind of fmt function behavior. |
73 | type Kind int |
74 | |
75 | const ( |
76 | KindNone Kind = iota // not a fmt wrapper function |
77 | KindPrint // function behaves like fmt.Print |
78 | KindPrintf // function behaves like fmt.Printf |
79 | KindErrorf // function behaves like fmt.Errorf |
80 | ) |
81 | |
82 | func (kind Kind) String() string { |
83 | switch kind { |
84 | case KindPrint: |
85 | return "print" |
86 | case KindPrintf: |
87 | return "printf" |
88 | case KindErrorf: |
89 | return "errorf" |
90 | } |
91 | return "" |
92 | } |
93 | |
94 | // Result is the printf analyzer's result type. Clients may query the result |
95 | // to learn whether a function behaves like fmt.Print or fmt.Printf. |
96 | type Result struct { |
97 | funcs map[*types.Func]Kind |
98 | } |
99 | |
100 | // Kind reports whether fn behaves like fmt.Print or fmt.Printf. |
101 | func (r *Result) Kind(fn *types.Func) Kind { |
102 | _, ok := isPrint[fn.FullName()] |
103 | if !ok { |
104 | // Next look up just "printf", for use with -printf.funcs. |
105 | _, ok = isPrint[strings.ToLower(fn.Name())] |
106 | } |
107 | if ok { |
108 | if strings.HasSuffix(fn.Name(), "f") { |
109 | return KindPrintf |
110 | } else { |
111 | return KindPrint |
112 | } |
113 | } |
114 | |
115 | return r.funcs[fn] |
116 | } |
117 | |
118 | // isWrapper is a fact indicating that a function is a print or printf wrapper. |
119 | type isWrapper struct{ Kind Kind } |
120 | |
121 | func (f *isWrapper) AFact() {} |
122 | |
123 | func (f *isWrapper) String() string { |
124 | switch f.Kind { |
125 | case KindPrintf: |
126 | return "printfWrapper" |
127 | case KindPrint: |
128 | return "printWrapper" |
129 | case KindErrorf: |
130 | return "errorfWrapper" |
131 | default: |
132 | return "unknownWrapper" |
133 | } |
134 | } |
135 | |
136 | func run(pass *analysis.Pass) (interface{}, error) { |
137 | res := &Result{ |
138 | funcs: make(map[*types.Func]Kind), |
139 | } |
140 | findPrintfLike(pass, res) |
141 | checkCall(pass) |
142 | return res, nil |
143 | } |
144 | |
145 | type printfWrapper struct { |
146 | obj *types.Func |
147 | fdecl *ast.FuncDecl |
148 | format *types.Var |
149 | args *types.Var |
150 | callers []printfCaller |
151 | failed bool // if true, not a printf wrapper |
152 | } |
153 | |
154 | type printfCaller struct { |
155 | w *printfWrapper |
156 | call *ast.CallExpr |
157 | } |
158 | |
159 | // maybePrintfWrapper decides whether decl (a declared function) may be a wrapper |
160 | // around a fmt.Printf or fmt.Print function. If so it returns a printfWrapper |
161 | // function describing the declaration. Later processing will analyze the |
162 | // graph of potential printf wrappers to pick out the ones that are true wrappers. |
163 | // A function may be a Printf or Print wrapper if its last argument is ...interface{}. |
164 | // If the next-to-last argument is a string, then this may be a Printf wrapper. |
165 | // Otherwise it may be a Print wrapper. |
166 | func maybePrintfWrapper(info *types.Info, decl ast.Decl) *printfWrapper { |
167 | // Look for functions with final argument type ...interface{}. |
168 | fdecl, ok := decl.(*ast.FuncDecl) |
169 | if !ok || fdecl.Body == nil { |
170 | return nil |
171 | } |
172 | fn, ok := info.Defs[fdecl.Name].(*types.Func) |
173 | // Type information may be incomplete. |
174 | if !ok { |
175 | return nil |
176 | } |
177 | |
178 | sig := fn.Type().(*types.Signature) |
179 | if !sig.Variadic() { |
180 | return nil // not variadic |
181 | } |
182 | |
183 | params := sig.Params() |
184 | nparams := params.Len() // variadic => nonzero |
185 | |
186 | args := params.At(nparams - 1) |
187 | iface, ok := args.Type().(*types.Slice).Elem().(*types.Interface) |
188 | if !ok || !iface.Empty() { |
189 | return nil // final (args) param is not ...interface{} |
190 | } |
191 | |
192 | // Is second last param 'format string'? |
193 | var format *types.Var |
194 | if nparams >= 2 { |
195 | if p := params.At(nparams - 2); p.Type() == types.Typ[types.String] { |
196 | format = p |
197 | } |
198 | } |
199 | |
200 | return &printfWrapper{ |
201 | obj: fn, |
202 | fdecl: fdecl, |
203 | format: format, |
204 | args: args, |
205 | } |
206 | } |
207 | |
208 | // findPrintfLike scans the entire package to find printf-like functions. |
209 | func findPrintfLike(pass *analysis.Pass, res *Result) (interface{}, error) { |
210 | // Gather potential wrappers and call graph between them. |
211 | byObj := make(map[*types.Func]*printfWrapper) |
212 | var wrappers []*printfWrapper |
213 | for _, file := range pass.Files { |
214 | for _, decl := range file.Decls { |
215 | w := maybePrintfWrapper(pass.TypesInfo, decl) |
216 | if w == nil { |
217 | continue |
218 | } |
219 | byObj[w.obj] = w |
220 | wrappers = append(wrappers, w) |
221 | } |
222 | } |
223 | |
224 | // Walk the graph to figure out which are really printf wrappers. |
225 | for _, w := range wrappers { |
226 | // Scan function for calls that could be to other printf-like functions. |
227 | ast.Inspect(w.fdecl.Body, func(n ast.Node) bool { |
228 | if w.failed { |
229 | return false |
230 | } |
231 | |
232 | // TODO: Relax these checks; issue 26555. |
233 | if assign, ok := n.(*ast.AssignStmt); ok { |
234 | for _, lhs := range assign.Lhs { |
235 | if match(pass.TypesInfo, lhs, w.format) || |
236 | match(pass.TypesInfo, lhs, w.args) { |
237 | // Modifies the format |
238 | // string or args in |
239 | // some way, so not a |
240 | // simple wrapper. |
241 | w.failed = true |
242 | return false |
243 | } |
244 | } |
245 | } |
246 | if un, ok := n.(*ast.UnaryExpr); ok && un.Op == token.AND { |
247 | if match(pass.TypesInfo, un.X, w.format) || |
248 | match(pass.TypesInfo, un.X, w.args) { |
249 | // Taking the address of the |
250 | // format string or args, |
251 | // so not a simple wrapper. |
252 | w.failed = true |
253 | return false |
254 | } |
255 | } |
256 | |
257 | call, ok := n.(*ast.CallExpr) |
258 | if !ok || len(call.Args) == 0 || !match(pass.TypesInfo, call.Args[len(call.Args)-1], w.args) { |
259 | return true |
260 | } |
261 | |
262 | fn, kind := printfNameAndKind(pass, call) |
263 | if kind != 0 { |
264 | checkPrintfFwd(pass, w, call, kind, res) |
265 | return true |
266 | } |
267 | |
268 | // If the call is to another function in this package, |
269 | // maybe we will find out it is printf-like later. |
270 | // Remember this call for later checking. |
271 | if fn != nil && fn.Pkg() == pass.Pkg && byObj[fn] != nil { |
272 | callee := byObj[fn] |
273 | callee.callers = append(callee.callers, printfCaller{w, call}) |
274 | } |
275 | |
276 | return true |
277 | }) |
278 | } |
279 | return nil, nil |
280 | } |
281 | |
282 | func match(info *types.Info, arg ast.Expr, param *types.Var) bool { |
283 | id, ok := arg.(*ast.Ident) |
284 | return ok && info.ObjectOf(id) == param |
285 | } |
286 | |
287 | // checkPrintfFwd checks that a printf-forwarding wrapper is forwarding correctly. |
288 | // It diagnoses writing fmt.Printf(format, args) instead of fmt.Printf(format, args...). |
289 | func checkPrintfFwd(pass *analysis.Pass, w *printfWrapper, call *ast.CallExpr, kind Kind, res *Result) { |
290 | matched := kind == KindPrint || |
291 | kind != KindNone && len(call.Args) >= 2 && match(pass.TypesInfo, call.Args[len(call.Args)-2], w.format) |
292 | if !matched { |
293 | return |
294 | } |
295 | |
296 | if !call.Ellipsis.IsValid() { |
297 | typ, ok := pass.TypesInfo.Types[call.Fun].Type.(*types.Signature) |
298 | if !ok { |
299 | return |
300 | } |
301 | if len(call.Args) > typ.Params().Len() { |
302 | // If we're passing more arguments than what the |
303 | // print/printf function can take, adding an ellipsis |
304 | // would break the program. For example: |
305 | // |
306 | // func foo(arg1 string, arg2 ...interface{} { |
307 | // fmt.Printf("%s %v", arg1, arg2) |
308 | // } |
309 | return |
310 | } |
311 | desc := "printf" |
312 | if kind == KindPrint { |
313 | desc = "print" |
314 | } |
315 | pass.ReportRangef(call, "missing ... in args forwarded to %s-like function", desc) |
316 | return |
317 | } |
318 | fn := w.obj |
319 | var fact isWrapper |
320 | if !pass.ImportObjectFact(fn, &fact) { |
321 | fact.Kind = kind |
322 | pass.ExportObjectFact(fn, &fact) |
323 | res.funcs[fn] = kind |
324 | for _, caller := range w.callers { |
325 | checkPrintfFwd(pass, caller.w, caller.call, kind, res) |
326 | } |
327 | } |
328 | } |
329 | |
330 | // isPrint records the print functions. |
331 | // If a key ends in 'f' then it is assumed to be a formatted print. |
332 | // |
333 | // Keys are either values returned by (*types.Func).FullName, |
334 | // or case-insensitive identifiers such as "errorf". |
335 | // |
336 | // The -funcs flag adds to this set. |
337 | // |
338 | // The set below includes facts for many important standard library |
339 | // functions, even though the analysis is capable of deducing that, for |
340 | // example, fmt.Printf forwards to fmt.Fprintf. We avoid relying on the |
341 | // driver applying analyzers to standard packages because "go vet" does |
342 | // not do so with gccgo, and nor do some other build systems. |
343 | // TODO(adonovan): eliminate the redundant facts once this restriction |
344 | // is lifted. |
345 | var isPrint = stringSet{ |
346 | "fmt.Errorf": true, |
347 | "fmt.Fprint": true, |
348 | "fmt.Fprintf": true, |
349 | "fmt.Fprintln": true, |
350 | "fmt.Print": true, |
351 | "fmt.Printf": true, |
352 | "fmt.Println": true, |
353 | "fmt.Sprint": true, |
354 | "fmt.Sprintf": true, |
355 | "fmt.Sprintln": true, |
356 | |
357 | "runtime/trace.Logf": true, |
358 | |
359 | "log.Print": true, |
360 | "log.Printf": true, |
361 | "log.Println": true, |
362 | "log.Fatal": true, |
363 | "log.Fatalf": true, |
364 | "log.Fatalln": true, |
365 | "log.Panic": true, |
366 | "log.Panicf": true, |
367 | "log.Panicln": true, |
368 | "(*log.Logger).Fatal": true, |
369 | "(*log.Logger).Fatalf": true, |
370 | "(*log.Logger).Fatalln": true, |
371 | "(*log.Logger).Panic": true, |
372 | "(*log.Logger).Panicf": true, |
373 | "(*log.Logger).Panicln": true, |
374 | "(*log.Logger).Print": true, |
375 | "(*log.Logger).Printf": true, |
376 | "(*log.Logger).Println": true, |
377 | |
378 | "(*testing.common).Error": true, |
379 | "(*testing.common).Errorf": true, |
380 | "(*testing.common).Fatal": true, |
381 | "(*testing.common).Fatalf": true, |
382 | "(*testing.common).Log": true, |
383 | "(*testing.common).Logf": true, |
384 | "(*testing.common).Skip": true, |
385 | "(*testing.common).Skipf": true, |
386 | // *testing.T and B are detected by induction, but testing.TB is |
387 | // an interface and the inference can't follow dynamic calls. |
388 | "(testing.TB).Error": true, |
389 | "(testing.TB).Errorf": true, |
390 | "(testing.TB).Fatal": true, |
391 | "(testing.TB).Fatalf": true, |
392 | "(testing.TB).Log": true, |
393 | "(testing.TB).Logf": true, |
394 | "(testing.TB).Skip": true, |
395 | "(testing.TB).Skipf": true, |
396 | } |
397 | |
398 | // formatString returns the format string argument and its index within |
399 | // the given printf-like call expression. |
400 | // |
401 | // The last parameter before variadic arguments is assumed to be |
402 | // a format string. |
403 | // |
404 | // The first string literal or string constant is assumed to be a format string |
405 | // if the call's signature cannot be determined. |
406 | // |
407 | // If it cannot find any format string parameter, it returns ("", -1). |
408 | func formatString(pass *analysis.Pass, call *ast.CallExpr) (format string, idx int) { |
409 | typ := pass.TypesInfo.Types[call.Fun].Type |
410 | if typ != nil { |
411 | if sig, ok := typ.(*types.Signature); ok { |
412 | if !sig.Variadic() { |
413 | // Skip checking non-variadic functions. |
414 | return "", -1 |
415 | } |
416 | idx := sig.Params().Len() - 2 |
417 | if idx < 0 { |
418 | // Skip checking variadic functions without |
419 | // fixed arguments. |
420 | return "", -1 |
421 | } |
422 | s, ok := stringConstantArg(pass, call, idx) |
423 | if !ok { |
424 | // The last argument before variadic args isn't a string. |
425 | return "", -1 |
426 | } |
427 | return s, idx |
428 | } |
429 | } |
430 | |
431 | // Cannot determine call's signature. Fall back to scanning for the first |
432 | // string constant in the call. |
433 | for idx := range call.Args { |
434 | if s, ok := stringConstantArg(pass, call, idx); ok { |
435 | return s, idx |
436 | } |
437 | if pass.TypesInfo.Types[call.Args[idx]].Type == types.Typ[types.String] { |
438 | // Skip checking a call with a non-constant format |
439 | // string argument, since its contents are unavailable |
440 | // for validation. |
441 | return "", -1 |
442 | } |
443 | } |
444 | return "", -1 |
445 | } |
446 | |
447 | // stringConstantArg returns call's string constant argument at the index idx. |
448 | // |
449 | // ("", false) is returned if call's argument at the index idx isn't a string |
450 | // constant. |
451 | func stringConstantArg(pass *analysis.Pass, call *ast.CallExpr, idx int) (string, bool) { |
452 | if idx >= len(call.Args) { |
453 | return "", false |
454 | } |
455 | return stringConstantExpr(pass, call.Args[idx]) |
456 | } |
457 | |
458 | // stringConstantExpr returns expression's string constant value. |
459 | // |
460 | // ("", false) is returned if expression isn't a string |
461 | // constant. |
462 | func stringConstantExpr(pass *analysis.Pass, expr ast.Expr) (string, bool) { |
463 | lit := pass.TypesInfo.Types[expr].Value |
464 | if lit != nil && lit.Kind() == constant.String { |
465 | return constant.StringVal(lit), true |
466 | } |
467 | return "", false |
468 | } |
469 | |
470 | // checkCall triggers the print-specific checks if the call invokes a print function. |
471 | func checkCall(pass *analysis.Pass) { |
472 | inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) |
473 | nodeFilter := []ast.Node{ |
474 | (*ast.CallExpr)(nil), |
475 | } |
476 | inspect.Preorder(nodeFilter, func(n ast.Node) { |
477 | call := n.(*ast.CallExpr) |
478 | fn, kind := printfNameAndKind(pass, call) |
479 | switch kind { |
480 | case KindPrintf, KindErrorf: |
481 | checkPrintf(pass, kind, call, fn) |
482 | case KindPrint: |
483 | checkPrint(pass, call, fn) |
484 | } |
485 | }) |
486 | } |
487 | |
488 | func printfNameAndKind(pass *analysis.Pass, call *ast.CallExpr) (fn *types.Func, kind Kind) { |
489 | fn, _ = typeutil.Callee(pass.TypesInfo, call).(*types.Func) |
490 | if fn == nil { |
491 | return nil, 0 |
492 | } |
493 | |
494 | _, ok := isPrint[fn.FullName()] |
495 | if !ok { |
496 | // Next look up just "printf", for use with -printf.funcs. |
497 | _, ok = isPrint[strings.ToLower(fn.Name())] |
498 | } |
499 | if ok { |
500 | if fn.FullName() == "fmt.Errorf" { |
501 | kind = KindErrorf |
502 | } else if strings.HasSuffix(fn.Name(), "f") { |
503 | kind = KindPrintf |
504 | } else { |
505 | kind = KindPrint |
506 | } |
507 | return fn, kind |
508 | } |
509 | |
510 | var fact isWrapper |
511 | if pass.ImportObjectFact(fn, &fact) { |
512 | return fn, fact.Kind |
513 | } |
514 | |
515 | return fn, KindNone |
516 | } |
517 | |
518 | // isFormatter reports whether t could satisfy fmt.Formatter. |
519 | // The only interface method to look for is "Format(State, rune)". |
520 | func isFormatter(typ types.Type) bool { |
521 | // If the type is an interface, the value it holds might satisfy fmt.Formatter. |
522 | if _, ok := typ.Underlying().(*types.Interface); ok { |
523 | // Don't assume type parameters could be formatters. With the greater |
524 | // expressiveness of constraint interface syntax we expect more type safety |
525 | // when using type parameters. |
526 | if !typeparams.IsTypeParam(typ) { |
527 | return true |
528 | } |
529 | } |
530 | obj, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Format") |
531 | fn, ok := obj.(*types.Func) |
532 | if !ok { |
533 | return false |
534 | } |
535 | sig := fn.Type().(*types.Signature) |
536 | return sig.Params().Len() == 2 && |
537 | sig.Results().Len() == 0 && |
538 | isNamed(sig.Params().At(0).Type(), "fmt", "State") && |
539 | types.Identical(sig.Params().At(1).Type(), types.Typ[types.Rune]) |
540 | } |
541 | |
542 | func isNamed(T types.Type, pkgpath, name string) bool { |
543 | named, ok := T.(*types.Named) |
544 | return ok && named.Obj().Pkg().Path() == pkgpath && named.Obj().Name() == name |
545 | } |
546 | |
547 | // formatState holds the parsed representation of a printf directive such as "%3.*[4]d". |
548 | // It is constructed by parsePrintfVerb. |
549 | type formatState struct { |
550 | verb rune // the format verb: 'd' for "%d" |
551 | format string // the full format directive from % through verb, "%.3d". |
552 | name string // Printf, Sprintf etc. |
553 | flags []byte // the list of # + etc. |
554 | argNums []int // the successive argument numbers that are consumed, adjusted to refer to actual arg in call |
555 | firstArg int // Index of first argument after the format in the Printf call. |
556 | // Used only during parse. |
557 | pass *analysis.Pass |
558 | call *ast.CallExpr |
559 | argNum int // Which argument we're expecting to format now. |
560 | hasIndex bool // Whether the argument is indexed. |
561 | indexPending bool // Whether we have an indexed argument that has not resolved. |
562 | nbytes int // number of bytes of the format string consumed. |
563 | } |
564 | |
565 | // checkPrintf checks a call to a formatted print routine such as Printf. |
566 | func checkPrintf(pass *analysis.Pass, kind Kind, call *ast.CallExpr, fn *types.Func) { |
567 | format, idx := formatString(pass, call) |
568 | if idx < 0 { |
569 | if false { |
570 | pass.Reportf(call.Lparen, "can't check non-constant format in call to %s", fn.FullName()) |
571 | } |
572 | return |
573 | } |
574 | |
575 | firstArg := idx + 1 // Arguments are immediately after format string. |
576 | if !strings.Contains(format, "%") { |
577 | if len(call.Args) > firstArg { |
578 | pass.Reportf(call.Lparen, "%s call has arguments but no formatting directives", fn.FullName()) |
579 | } |
580 | return |
581 | } |
582 | // Hard part: check formats against args. |
583 | argNum := firstArg |
584 | maxArgNum := firstArg |
585 | anyIndex := false |
586 | for i, w := 0, 0; i < len(format); i += w { |
587 | w = 1 |
588 | if format[i] != '%' { |
589 | continue |
590 | } |
591 | state := parsePrintfVerb(pass, call, fn.FullName(), format[i:], firstArg, argNum) |
592 | if state == nil { |
593 | return |
594 | } |
595 | w = len(state.format) |
596 | if !okPrintfArg(pass, call, state) { // One error per format is enough. |
597 | return |
598 | } |
599 | if state.hasIndex { |
600 | anyIndex = true |
601 | } |
602 | if state.verb == 'w' { |
603 | switch kind { |
604 | case KindNone, KindPrint, KindPrintf: |
605 | pass.Reportf(call.Pos(), "%s does not support error-wrapping directive %%w", state.name) |
606 | return |
607 | } |
608 | } |
609 | if len(state.argNums) > 0 { |
610 | // Continue with the next sequential argument. |
611 | argNum = state.argNums[len(state.argNums)-1] + 1 |
612 | } |
613 | for _, n := range state.argNums { |
614 | if n >= maxArgNum { |
615 | maxArgNum = n + 1 |
616 | } |
617 | } |
618 | } |
619 | // Dotdotdot is hard. |
620 | if call.Ellipsis.IsValid() && maxArgNum >= len(call.Args)-1 { |
621 | return |
622 | } |
623 | // If any formats are indexed, extra arguments are ignored. |
624 | if anyIndex { |
625 | return |
626 | } |
627 | // There should be no leftover arguments. |
628 | if maxArgNum != len(call.Args) { |
629 | expect := maxArgNum - firstArg |
630 | numArgs := len(call.Args) - firstArg |
631 | pass.ReportRangef(call, "%s call needs %v but has %v", fn.FullName(), count(expect, "arg"), count(numArgs, "arg")) |
632 | } |
633 | } |
634 | |
635 | // parseFlags accepts any printf flags. |
636 | func (s *formatState) parseFlags() { |
637 | for s.nbytes < len(s.format) { |
638 | switch c := s.format[s.nbytes]; c { |
639 | case '#', '0', '+', '-', ' ': |
640 | s.flags = append(s.flags, c) |
641 | s.nbytes++ |
642 | default: |
643 | return |
644 | } |
645 | } |
646 | } |
647 | |
648 | // scanNum advances through a decimal number if present. |
649 | func (s *formatState) scanNum() { |
650 | for ; s.nbytes < len(s.format); s.nbytes++ { |
651 | c := s.format[s.nbytes] |
652 | if c < '0' || '9' < c { |
653 | return |
654 | } |
655 | } |
656 | } |
657 | |
658 | // parseIndex scans an index expression. It returns false if there is a syntax error. |
659 | func (s *formatState) parseIndex() bool { |
660 | if s.nbytes == len(s.format) || s.format[s.nbytes] != '[' { |
661 | return true |
662 | } |
663 | // Argument index present. |
664 | s.nbytes++ // skip '[' |
665 | start := s.nbytes |
666 | s.scanNum() |
667 | ok := true |
668 | if s.nbytes == len(s.format) || s.nbytes == start || s.format[s.nbytes] != ']' { |
669 | ok = false // syntax error is either missing "]" or invalid index. |
670 | s.nbytes = strings.Index(s.format[start:], "]") |
671 | if s.nbytes < 0 { |
672 | s.pass.ReportRangef(s.call, "%s format %s is missing closing ]", s.name, s.format) |
673 | return false |
674 | } |
675 | s.nbytes = s.nbytes + start |
676 | } |
677 | arg32, err := strconv.ParseInt(s.format[start:s.nbytes], 10, 32) |
678 | if err != nil || !ok || arg32 <= 0 || arg32 > int64(len(s.call.Args)-s.firstArg) { |
679 | s.pass.ReportRangef(s.call, "%s format has invalid argument index [%s]", s.name, s.format[start:s.nbytes]) |
680 | return false |
681 | } |
682 | s.nbytes++ // skip ']' |
683 | arg := int(arg32) |
684 | arg += s.firstArg - 1 // We want to zero-index the actual arguments. |
685 | s.argNum = arg |
686 | s.hasIndex = true |
687 | s.indexPending = true |
688 | return true |
689 | } |
690 | |
691 | // parseNum scans a width or precision (or *). It returns false if there's a bad index expression. |
692 | func (s *formatState) parseNum() bool { |
693 | if s.nbytes < len(s.format) && s.format[s.nbytes] == '*' { |
694 | if s.indexPending { // Absorb it. |
695 | s.indexPending = false |
696 | } |
697 | s.nbytes++ |
698 | s.argNums = append(s.argNums, s.argNum) |
699 | s.argNum++ |
700 | } else { |
701 | s.scanNum() |
702 | } |
703 | return true |
704 | } |
705 | |
706 | // parsePrecision scans for a precision. It returns false if there's a bad index expression. |
707 | func (s *formatState) parsePrecision() bool { |
708 | // If there's a period, there may be a precision. |
709 | if s.nbytes < len(s.format) && s.format[s.nbytes] == '.' { |
710 | s.flags = append(s.flags, '.') // Treat precision as a flag. |
711 | s.nbytes++ |
712 | if !s.parseIndex() { |
713 | return false |
714 | } |
715 | if !s.parseNum() { |
716 | return false |
717 | } |
718 | } |
719 | return true |
720 | } |
721 | |
722 | // parsePrintfVerb looks the formatting directive that begins the format string |
723 | // and returns a formatState that encodes what the directive wants, without looking |
724 | // at the actual arguments present in the call. The result is nil if there is an error. |
725 | func parsePrintfVerb(pass *analysis.Pass, call *ast.CallExpr, name, format string, firstArg, argNum int) *formatState { |
726 | state := &formatState{ |
727 | format: format, |
728 | name: name, |
729 | flags: make([]byte, 0, 5), |
730 | argNum: argNum, |
731 | argNums: make([]int, 0, 1), |
732 | nbytes: 1, // There's guaranteed to be a percent sign. |
733 | firstArg: firstArg, |
734 | pass: pass, |
735 | call: call, |
736 | } |
737 | // There may be flags. |
738 | state.parseFlags() |
739 | // There may be an index. |
740 | if !state.parseIndex() { |
741 | return nil |
742 | } |
743 | // There may be a width. |
744 | if !state.parseNum() { |
745 | return nil |
746 | } |
747 | // There may be a precision. |
748 | if !state.parsePrecision() { |
749 | return nil |
750 | } |
751 | // Now a verb, possibly prefixed by an index (which we may already have). |
752 | if !state.indexPending && !state.parseIndex() { |
753 | return nil |
754 | } |
755 | if state.nbytes == len(state.format) { |
756 | pass.ReportRangef(call.Fun, "%s format %s is missing verb at end of string", name, state.format) |
757 | return nil |
758 | } |
759 | verb, w := utf8.DecodeRuneInString(state.format[state.nbytes:]) |
760 | state.verb = verb |
761 | state.nbytes += w |
762 | if verb != '%' { |
763 | state.argNums = append(state.argNums, state.argNum) |
764 | } |
765 | state.format = state.format[:state.nbytes] |
766 | return state |
767 | } |
768 | |
769 | // printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask. |
770 | type printfArgType int |
771 | |
772 | const ( |
773 | argBool printfArgType = 1 << iota |
774 | argInt |
775 | argRune |
776 | argString |
777 | argFloat |
778 | argComplex |
779 | argPointer |
780 | argError |
781 | anyType printfArgType = ^0 |
782 | ) |
783 | |
784 | type printVerb struct { |
785 | verb rune // User may provide verb through Formatter; could be a rune. |
786 | flags string // known flags are all ASCII |
787 | typ printfArgType |
788 | } |
789 | |
790 | // Common flag sets for printf verbs. |
791 | const ( |
792 | noFlag = "" |
793 | numFlag = " -+.0" |
794 | sharpNumFlag = " -+.0#" |
795 | allFlags = " -+.0#" |
796 | ) |
797 | |
798 | // printVerbs identifies which flags are known to printf for each verb. |
799 | var printVerbs = []printVerb{ |
800 | // '-' is a width modifier, always valid. |
801 | // '.' is a precision for float, max width for strings. |
802 | // '+' is required sign for numbers, Go format for %v. |
803 | // '#' is alternate format for several verbs. |
804 | // ' ' is spacer for numbers |
805 | {'%', noFlag, 0}, |
806 | {'b', sharpNumFlag, argInt | argFloat | argComplex | argPointer}, |
807 | {'c', "-", argRune | argInt}, |
808 | {'d', numFlag, argInt | argPointer}, |
809 | {'e', sharpNumFlag, argFloat | argComplex}, |
810 | {'E', sharpNumFlag, argFloat | argComplex}, |
811 | {'f', sharpNumFlag, argFloat | argComplex}, |
812 | {'F', sharpNumFlag, argFloat | argComplex}, |
813 | {'g', sharpNumFlag, argFloat | argComplex}, |
814 | {'G', sharpNumFlag, argFloat | argComplex}, |
815 | {'o', sharpNumFlag, argInt | argPointer}, |
816 | {'O', sharpNumFlag, argInt | argPointer}, |
817 | {'p', "-#", argPointer}, |
818 | {'q', " -+.0#", argRune | argInt | argString}, |
819 | {'s', " -+.0", argString}, |
820 | {'t', "-", argBool}, |
821 | {'T', "-", anyType}, |
822 | {'U', "-#", argRune | argInt}, |
823 | {'v', allFlags, anyType}, |
824 | {'w', allFlags, argError}, |
825 | {'x', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex}, |
826 | {'X', sharpNumFlag, argRune | argInt | argString | argPointer | argFloat | argComplex}, |
827 | } |
828 | |
829 | // okPrintfArg compares the formatState to the arguments actually present, |
830 | // reporting any discrepancies it can discern. If the final argument is ellipsissed, |
831 | // there's little it can do for that. |
832 | func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, state *formatState) (ok bool) { |
833 | var v printVerb |
834 | found := false |
835 | // Linear scan is fast enough for a small list. |
836 | for _, v = range printVerbs { |
837 | if v.verb == state.verb { |
838 | found = true |
839 | break |
840 | } |
841 | } |
842 | |
843 | // Could current arg implement fmt.Formatter? |
844 | // Skip check for the %w verb, which requires an error. |
845 | formatter := false |
846 | if v.typ != argError && state.argNum < len(call.Args) { |
847 | if tv, ok := pass.TypesInfo.Types[call.Args[state.argNum]]; ok { |
848 | formatter = isFormatter(tv.Type) |
849 | } |
850 | } |
851 | |
852 | if !formatter { |
853 | if !found { |
854 | pass.ReportRangef(call, "%s format %s has unknown verb %c", state.name, state.format, state.verb) |
855 | return false |
856 | } |
857 | for _, flag := range state.flags { |
858 | // TODO: Disable complaint about '0' for Go 1.10. To be fixed properly in 1.11. |
859 | // See issues 23598 and 23605. |
860 | if flag == '0' { |
861 | continue |
862 | } |
863 | if !strings.ContainsRune(v.flags, rune(flag)) { |
864 | pass.ReportRangef(call, "%s format %s has unrecognized flag %c", state.name, state.format, flag) |
865 | return false |
866 | } |
867 | } |
868 | } |
869 | // Verb is good. If len(state.argNums)>trueArgs, we have something like %.*s and all |
870 | // but the final arg must be an integer. |
871 | trueArgs := 1 |
872 | if state.verb == '%' { |
873 | trueArgs = 0 |
874 | } |
875 | nargs := len(state.argNums) |
876 | for i := 0; i < nargs-trueArgs; i++ { |
877 | argNum := state.argNums[i] |
878 | if !argCanBeChecked(pass, call, i, state) { |
879 | return |
880 | } |
881 | arg := call.Args[argNum] |
882 | if reason, ok := matchArgType(pass, argInt, arg); !ok { |
883 | details := "" |
884 | if reason != "" { |
885 | details = " (" + reason + ")" |
886 | } |
887 | pass.ReportRangef(call, "%s format %s uses non-int %s%s as argument of *", state.name, state.format, analysisutil.Format(pass.Fset, arg), details) |
888 | return false |
889 | } |
890 | } |
891 | |
892 | if state.verb == '%' || formatter { |
893 | return true |
894 | } |
895 | argNum := state.argNums[len(state.argNums)-1] |
896 | if !argCanBeChecked(pass, call, len(state.argNums)-1, state) { |
897 | return false |
898 | } |
899 | arg := call.Args[argNum] |
900 | if isFunctionValue(pass, arg) && state.verb != 'p' && state.verb != 'T' { |
901 | pass.ReportRangef(call, "%s format %s arg %s is a func value, not called", state.name, state.format, analysisutil.Format(pass.Fset, arg)) |
902 | return false |
903 | } |
904 | if reason, ok := matchArgType(pass, v.typ, arg); !ok { |
905 | typeString := "" |
906 | if typ := pass.TypesInfo.Types[arg].Type; typ != nil { |
907 | typeString = typ.String() |
908 | } |
909 | details := "" |
910 | if reason != "" { |
911 | details = " (" + reason + ")" |
912 | } |
913 | pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s%s, see also https://pkg.go.dev/fmt#hdr-Printing", state.name, state.format, analysisutil.Format(pass.Fset, arg), typeString, details) |
914 | return false |
915 | } |
916 | if v.typ&argString != 0 && v.verb != 'T' && !bytes.Contains(state.flags, []byte{'#'}) { |
917 | if methodName, ok := recursiveStringer(pass, arg); ok { |
918 | pass.ReportRangef(call, "%s format %s with arg %s causes recursive %s method call", state.name, state.format, analysisutil.Format(pass.Fset, arg), methodName) |
919 | return false |
920 | } |
921 | } |
922 | return true |
923 | } |
924 | |
925 | // recursiveStringer reports whether the argument e is a potential |
926 | // recursive call to stringer or is an error, such as t and &t in these examples: |
927 | // |
928 | // func (t *T) String() string { printf("%s", t) } |
929 | // func (t T) Error() string { printf("%s", t) } |
930 | // func (t T) String() string { printf("%s", &t) } |
931 | func recursiveStringer(pass *analysis.Pass, e ast.Expr) (string, bool) { |
932 | typ := pass.TypesInfo.Types[e].Type |
933 | |
934 | // It's unlikely to be a recursive stringer if it has a Format method. |
935 | if isFormatter(typ) { |
936 | return "", false |
937 | } |
938 | |
939 | // Does e allow e.String() or e.Error()? |
940 | strObj, _, _ := types.LookupFieldOrMethod(typ, false, pass.Pkg, "String") |
941 | strMethod, strOk := strObj.(*types.Func) |
942 | errObj, _, _ := types.LookupFieldOrMethod(typ, false, pass.Pkg, "Error") |
943 | errMethod, errOk := errObj.(*types.Func) |
944 | if !strOk && !errOk { |
945 | return "", false |
946 | } |
947 | |
948 | // inScope returns true if e is in the scope of f. |
949 | inScope := func(e ast.Expr, f *types.Func) bool { |
950 | return f.Scope() != nil && f.Scope().Contains(e.Pos()) |
951 | } |
952 | |
953 | // Is the expression e within the body of that String or Error method? |
954 | var method *types.Func |
955 | if strOk && strMethod.Pkg() == pass.Pkg && inScope(e, strMethod) { |
956 | method = strMethod |
957 | } else if errOk && errMethod.Pkg() == pass.Pkg && inScope(e, errMethod) { |
958 | method = errMethod |
959 | } else { |
960 | return "", false |
961 | } |
962 | |
963 | sig := method.Type().(*types.Signature) |
964 | if !isStringer(sig) { |
965 | return "", false |
966 | } |
967 | |
968 | // Is it the receiver r, or &r? |
969 | if u, ok := e.(*ast.UnaryExpr); ok && u.Op == token.AND { |
970 | e = u.X // strip off & from &r |
971 | } |
972 | if id, ok := e.(*ast.Ident); ok { |
973 | if pass.TypesInfo.Uses[id] == sig.Recv() { |
974 | return method.FullName(), true |
975 | } |
976 | } |
977 | return "", false |
978 | } |
979 | |
980 | // isStringer reports whether the method signature matches the String() definition in fmt.Stringer. |
981 | func isStringer(sig *types.Signature) bool { |
982 | return sig.Params().Len() == 0 && |
983 | sig.Results().Len() == 1 && |
984 | sig.Results().At(0).Type() == types.Typ[types.String] |
985 | } |
986 | |
987 | // isFunctionValue reports whether the expression is a function as opposed to a function call. |
988 | // It is almost always a mistake to print a function value. |
989 | func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool { |
990 | if typ := pass.TypesInfo.Types[e].Type; typ != nil { |
991 | _, ok := typ.(*types.Signature) |
992 | return ok |
993 | } |
994 | return false |
995 | } |
996 | |
997 | // argCanBeChecked reports whether the specified argument is statically present; |
998 | // it may be beyond the list of arguments or in a terminal slice... argument, which |
999 | // means we can't see it. |
1000 | func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, formatArg int, state *formatState) bool { |
1001 | argNum := state.argNums[formatArg] |
1002 | if argNum <= 0 { |
1003 | // Shouldn't happen, so catch it with prejudice. |
1004 | panic("negative arg num") |
1005 | } |
1006 | if argNum < len(call.Args)-1 { |
1007 | return true // Always OK. |
1008 | } |
1009 | if call.Ellipsis.IsValid() { |
1010 | return false // We just can't tell; there could be many more arguments. |
1011 | } |
1012 | if argNum < len(call.Args) { |
1013 | return true |
1014 | } |
1015 | // There are bad indexes in the format or there are fewer arguments than the format needs. |
1016 | // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi". |
1017 | arg := argNum - state.firstArg + 1 // People think of arguments as 1-indexed. |
1018 | 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")) |
1019 | return false |
1020 | } |
1021 | |
1022 | // printFormatRE is the regexp we match and report as a possible format string |
1023 | // in the first argument to unformatted prints like fmt.Print. |
1024 | // We exclude the space flag, so that printing a string like "x % y" is not reported as a format. |
1025 | var printFormatRE = regexp.MustCompile(`%` + flagsRE + numOptRE + `\.?` + numOptRE + indexOptRE + verbRE) |
1026 | |
1027 | const ( |
1028 | flagsRE = `[+\-#]*` |
1029 | indexOptRE = `(\[[0-9]+\])?` |
1030 | numOptRE = `([0-9]+|` + indexOptRE + `\*)?` |
1031 | verbRE = `[bcdefgopqstvxEFGTUX]` |
1032 | ) |
1033 | |
1034 | // checkPrint checks a call to an unformatted print routine such as Println. |
1035 | func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) { |
1036 | firstArg := 0 |
1037 | typ := pass.TypesInfo.Types[call.Fun].Type |
1038 | if typ == nil { |
1039 | // Skip checking functions with unknown type. |
1040 | return |
1041 | } |
1042 | if sig, ok := typ.(*types.Signature); ok { |
1043 | if !sig.Variadic() { |
1044 | // Skip checking non-variadic functions. |
1045 | return |
1046 | } |
1047 | params := sig.Params() |
1048 | firstArg = params.Len() - 1 |
1049 | |
1050 | typ := params.At(firstArg).Type() |
1051 | typ = typ.(*types.Slice).Elem() |
1052 | it, ok := typ.(*types.Interface) |
1053 | if !ok || !it.Empty() { |
1054 | // Skip variadic functions accepting non-interface{} args. |
1055 | return |
1056 | } |
1057 | } |
1058 | args := call.Args |
1059 | if len(args) <= firstArg { |
1060 | // Skip calls without variadic args. |
1061 | return |
1062 | } |
1063 | args = args[firstArg:] |
1064 | |
1065 | if firstArg == 0 { |
1066 | if sel, ok := call.Args[0].(*ast.SelectorExpr); ok { |
1067 | if x, ok := sel.X.(*ast.Ident); ok { |
1068 | if x.Name == "os" && strings.HasPrefix(sel.Sel.Name, "Std") { |
1069 | pass.ReportRangef(call, "%s does not take io.Writer but has first arg %s", fn.FullName(), analysisutil.Format(pass.Fset, call.Args[0])) |
1070 | } |
1071 | } |
1072 | } |
1073 | } |
1074 | |
1075 | arg := args[0] |
1076 | if s, ok := stringConstantExpr(pass, arg); ok { |
1077 | // Ignore trailing % character |
1078 | // The % in "abc 0.0%" couldn't be a formatting directive. |
1079 | s = strings.TrimSuffix(s, "%") |
1080 | if strings.Contains(s, "%") { |
1081 | m := printFormatRE.FindStringSubmatch(s) |
1082 | if m != nil { |
1083 | pass.ReportRangef(call, "%s call has possible formatting directive %s", fn.FullName(), m[0]) |
1084 | } |
1085 | } |
1086 | } |
1087 | if strings.HasSuffix(fn.Name(), "ln") { |
1088 | // The last item, if a string, should not have a newline. |
1089 | arg = args[len(args)-1] |
1090 | if s, ok := stringConstantExpr(pass, arg); ok { |
1091 | if strings.HasSuffix(s, "\n") { |
1092 | pass.ReportRangef(call, "%s arg list ends with redundant newline", fn.FullName()) |
1093 | } |
1094 | } |
1095 | } |
1096 | for _, arg := range args { |
1097 | if isFunctionValue(pass, arg) { |
1098 | pass.ReportRangef(call, "%s arg %s is a func value, not called", fn.FullName(), analysisutil.Format(pass.Fset, arg)) |
1099 | } |
1100 | if methodName, ok := recursiveStringer(pass, arg); ok { |
1101 | pass.ReportRangef(call, "%s arg %s causes recursive call to %s method", fn.FullName(), analysisutil.Format(pass.Fset, arg), methodName) |
1102 | } |
1103 | } |
1104 | } |
1105 | |
1106 | // count(n, what) returns "1 what" or "N whats" |
1107 | // (assuming the plural of what is whats). |
1108 | func count(n int, what string) string { |
1109 | if n == 1 { |
1110 | return "1 " + what |
1111 | } |
1112 | return fmt.Sprintf("%d %ss", n, what) |
1113 | } |
1114 | |
1115 | // stringSet is a set-of-nonempty-strings-valued flag. |
1116 | // Note: elements without a '.' get lower-cased. |
1117 | type stringSet map[string]bool |
1118 | |
1119 | func (ss stringSet) String() string { |
1120 | var list []string |
1121 | for name := range ss { |
1122 | list = append(list, name) |
1123 | } |
1124 | sort.Strings(list) |
1125 | return strings.Join(list, ",") |
1126 | } |
1127 | |
1128 | func (ss stringSet) Set(flag string) error { |
1129 | for _, name := range strings.Split(flag, ",") { |
1130 | if len(name) == 0 { |
1131 | return fmt.Errorf("empty string") |
1132 | } |
1133 | if !strings.Contains(name, ".") { |
1134 | name = strings.ToLower(name) |
1135 | } |
1136 | ss[name] = true |
1137 | } |
1138 | return nil |
1139 | } |
1140 |
Members