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.
5 // This file contains the printf-checker.
23 var printfuncs
= flag
.String("printfuncs", "", "comma-separated list of print function names to check")
27 "check printf-like invocations",
32 func initPrintFlags() {
33 if *printfuncs
== "" {
36 for _
, name
:= range strings
.Split(*printfuncs
, ",") {
41 // Backwards compatibility: skip optional first argument
42 // index after the colon.
43 if colon
:= strings
.LastIndex(name
, ":"); colon
> 0 {
47 isPrint
[strings
.ToLower(name
)] = true
51 // TODO(rsc): Incorporate user-defined printf wrappers again.
52 // The general plan is to allow vet of one package P to output
53 // additional information to supply to later vets of packages
54 // importing P. Then vet of P can record a list of printf wrappers
55 // and the later vet using P.Printf will find it in the list and check it.
56 // That's not ready for Go 1.10.
57 // When that does happen, uncomment the user-defined printf
58 // wrapper tests in testdata/print.go.
60 // isPrint records the print functions.
61 // If a key ends in 'f' then it is assumed to be a formatted print.
62 var isPrint
= map[string]bool{
76 "log.Logger.Fatal": true,
77 "log.Logger.Fatalf": true,
78 "log.Logger.Fatalln": true,
79 "log.Logger.Panic": true,
80 "log.Logger.Panicf": true,
81 "log.Logger.Panicln": true,
82 "log.Logger.Printf": true,
83 "log.Logger.Println": true,
90 "testing.B.Error": true,
91 "testing.B.Errorf": true,
92 "testing.B.Fatal": true,
93 "testing.B.Fatalf": true,
94 "testing.B.Log": true,
95 "testing.B.Logf": true,
96 "testing.B.Skip": true,
97 "testing.B.Skipf": true,
98 "testing.T.Error": true,
99 "testing.T.Errorf": true,
100 "testing.T.Fatal": true,
101 "testing.T.Fatalf": true,
102 "testing.T.Log": true,
103 "testing.T.Logf": true,
104 "testing.T.Skip": true,
105 "testing.T.Skipf": true,
106 "testing.TB.Error": true,
107 "testing.TB.Errorf": true,
108 "testing.TB.Fatal": true,
109 "testing.TB.Fatalf": true,
110 "testing.TB.Log": true,
111 "testing.TB.Logf": true,
112 "testing.TB.Skip": true,
113 "testing.TB.Skipf": true,
116 // formatString returns the format string argument and its index within
117 // the given printf-like call expression.
119 // The last parameter before variadic arguments is assumed to be
122 // The first string literal or string constant is assumed to be a format string
123 // if the call's signature cannot be determined.
125 // If it cannot find any format string parameter, it returns ("", -1).
126 func formatString(f
*File
, call
*ast
.CallExpr
) (format
string, idx
int) {
127 typ
:= f
.pkg
.types
[call
.Fun
].Type
129 if sig
, ok
:= typ
.(*types
.Signature
); ok
{
131 // Skip checking non-variadic functions.
134 idx
:= sig
.Params().Len() - 2
136 // Skip checking variadic functions without
140 s
, ok
:= stringConstantArg(f
, call
, idx
)
142 // The last argument before variadic args isn't a string.
149 // Cannot determine call's signature. Fall back to scanning for the first
150 // string constant in the call.
151 for idx
:= range call
.Args
{
152 if s
, ok
:= stringConstantArg(f
, call
, idx
); ok
{
155 if f
.pkg
.types
[call
.Args
[idx
]].Type
== types
.Typ
[types
.String
] {
156 // Skip checking a call with a non-constant format
157 // string argument, since its contents are unavailable
165 // stringConstantArg returns call's string constant argument at the index idx.
167 // ("", false) is returned if call's argument at the index idx isn't a string
169 func stringConstantArg(f
*File
, call
*ast
.CallExpr
, idx
int) (string, bool) {
170 if idx
>= len(call
.Args
) {
173 arg
:= call
.Args
[idx
]
174 lit
:= f
.pkg
.types
[arg
].Value
175 if lit
!= nil && lit
.Kind() == constant
.String
{
176 return constant
.StringVal(lit
), true
181 // checkCall triggers the print-specific checks if the call invokes a print function.
182 func checkFmtPrintfCall(f
*File
, node ast
.Node
) {
183 if f
.pkg
.typesPkg
== nil {
184 // This check now requires type information.
188 if d
, ok
:= node
.(*ast
.FuncDecl
); ok
&& isStringer(f
, d
) {
189 // Remember we saw this.
190 if f
.stringers
== nil {
191 f
.stringers
= make(map[*ast
.Object
]bool)
193 if l
:= d
.Recv
.List
; len(l
) == 1 {
194 if n
:= l
[0].Names
; len(n
) == 1 {
195 f
.stringers
[n
[0].Obj
] = true
201 call
, ok
:= node
.(*ast
.CallExpr
)
206 // Construct name like pkg.Printf or pkg.Type.Printf for lookup.
208 switch x
:= call
.Fun
.(type) {
210 if fn
, ok
:= f
.pkg
.uses
[x
].(*types
.Func
); ok
{
212 if fn
.Pkg() == nil || fn
.Pkg() == f
.pkg
.typesPkg
{
213 pkg
= vcfg
.ImportPath
215 pkg
= fn
.Pkg().Path()
217 name
= pkg
+ "." + x
.Name
221 case *ast
.SelectorExpr
:
222 // Check for "fmt.Printf".
223 if id
, ok
:= x
.X
.(*ast
.Ident
); ok
{
224 if pkgName
, ok
:= f
.pkg
.uses
[id
].(*types
.PkgName
); ok
{
225 name
= pkgName
.Imported().Path() + "." + x
.Sel
.Name
230 // Check for t.Logf where t is a *testing.T.
231 if sel
:= f
.pkg
.selectors
[x
]; sel
!= nil {
233 if p
, ok
:= recv
.(*types
.Pointer
); ok
{
236 if named
, ok
:= recv
.(*types
.Named
); ok
{
239 if obj
.Pkg() == nil || obj
.Pkg() == f
.pkg
.typesPkg
{
240 pkg
= vcfg
.ImportPath
242 pkg
= obj
.Pkg().Path()
244 name
= pkg
+ "." + obj
.Name() + "." + x
.Sel
.Name
253 shortName
:= name
[strings
.LastIndex(name
, ".")+1:]
255 _
, ok
= isPrint
[name
]
257 // Next look up just "printf", for use with -printfuncs.
258 _
, ok
= isPrint
[strings
.ToLower(shortName
)]
261 if strings
.HasSuffix(name
, "f") {
262 f
.checkPrintf(call
, shortName
)
264 f
.checkPrint(call
, shortName
)
269 // isStringer returns true if the provided declaration is a "String() string"
270 // method, an implementation of fmt.Stringer.
271 func isStringer(f
*File
, d
*ast
.FuncDecl
) bool {
272 return d
.Recv
!= nil && d
.Name
.Name
== "String" && d
.Type
.Results
!= nil &&
273 len(d
.Type
.Params
.List
) == 0 && len(d
.Type
.Results
.List
) == 1 &&
274 f
.pkg
.types
[d
.Type
.Results
.List
[0].Type
].Type
== types
.Typ
[types
.String
]
277 // isFormatter reports whether t satisfies fmt.Formatter.
278 // Unlike fmt.Stringer, it's impossible to satisfy fmt.Formatter without importing fmt.
279 func (f
*File
) isFormatter(t types
.Type
) bool {
280 return formatterType
!= nil && types
.Implements(t
, formatterType
)
283 // formatState holds the parsed representation of a printf directive such as "%3.*[4]d".
284 // It is constructed by parsePrintfVerb.
285 type formatState
struct {
286 verb rune
// the format verb: 'd' for "%d"
287 format
string // the full format directive from % through verb, "%.3d".
288 name
string // Printf, Sprintf etc.
289 flags
[]byte // the list of # + etc.
290 argNums
[]int // the successive argument numbers that are consumed, adjusted to refer to actual arg in call
291 firstArg
int // Index of first argument after the format in the Printf call.
292 // Used only during parse.
295 argNum
int // Which argument we're expecting to format now.
296 indexPending
bool // Whether we have an indexed argument that has not resolved.
297 nbytes
int // number of bytes of the format string consumed.
300 // checkPrintf checks a call to a formatted print routine such as Printf.
301 func (f
*File
) checkPrintf(call
*ast
.CallExpr
, name
string) {
302 format
, idx
:= formatString(f
, call
)
305 f
.Warn(call
.Pos(), "can't check non-constant format in call to", name
)
310 firstArg
:= idx
+ 1 // Arguments are immediately after format string.
311 if !strings
.Contains(format
, "%") {
312 if len(call
.Args
) > firstArg
{
313 f
.Badf(call
.Pos(), "%s call has arguments but no formatting directives", name
)
317 // Hard part: check formats against args.
319 maxArgNum
:= firstArg
320 for i
, w
:= 0, 0; i
< len(format
); i
+= w
{
322 if format
[i
] != '%' {
325 state
:= f
.parsePrintfVerb(call
, name
, format
[i
:], firstArg
, argNum
)
329 w
= len(state
.format
)
330 if !f
.okPrintfArg(call
, state
) { // One error per format is enough.
333 if len(state
.argNums
) > 0 {
334 // Continue with the next sequential argument.
335 argNum
= state
.argNums
[len(state
.argNums
)-1] + 1
337 for _
, n
:= range state
.argNums
{
343 // Dotdotdot is hard.
344 if call
.Ellipsis
.IsValid() && maxArgNum
>= len(call
.Args
)-1 {
347 // There should be no leftover arguments.
348 if maxArgNum
!= len(call
.Args
) {
349 expect
:= maxArgNum
- firstArg
350 numArgs
:= len(call
.Args
) - firstArg
351 f
.Badf(call
.Pos(), "%s call needs %v but has %v", name
, count(expect
, "arg"), count(numArgs
, "arg"))
355 // parseFlags accepts any printf flags.
356 func (s
*formatState
) parseFlags() {
357 for s
.nbytes
< len(s
.format
) {
358 switch c
:= s
.format
[s
.nbytes
]; c
{
359 case '#', '0', '+', '-', ' ':
360 s
.flags
= append(s
.flags
, c
)
368 // scanNum advances through a decimal number if present.
369 func (s
*formatState
) scanNum() {
370 for ; s
.nbytes
< len(s
.format
); s
.nbytes
++ {
371 c
:= s
.format
[s
.nbytes
]
372 if c
< '0' ||
'9' < c
{
378 // parseIndex scans an index expression. It returns false if there is a syntax error.
379 func (s
*formatState
) parseIndex() bool {
380 if s
.nbytes
== len(s
.format
) || s
.format
[s
.nbytes
] != '[' {
383 // Argument index present.
384 s
.nbytes
++ // skip '['
388 if s
.nbytes
== len(s
.format
) || s
.nbytes
== start || s
.format
[s
.nbytes
] != ']' {
390 s
.nbytes
= strings
.Index(s
.format
, "]")
392 s
.file
.Badf(s
.call
.Pos(), "%s format %s is missing closing ]", s
.name
, s
.format
)
396 arg32
, err
:= strconv
.ParseInt(s
.format
[start
:s
.nbytes
], 10, 32)
397 if err
!= nil ||
!ok || arg32
<= 0 || arg32
> int64(len(s
.call
.Args
)-s
.firstArg
) {
398 s
.file
.Badf(s
.call
.Pos(), "%s format has invalid argument index [%s]", s
.name
, s
.format
[start
:s
.nbytes
])
401 s
.nbytes
++ // skip ']'
403 arg
+= s
.firstArg
- 1 // We want to zero-index the actual arguments.
405 s
.indexPending
= true
409 // parseNum scans a width or precision (or *). It returns false if there's a bad index expression.
410 func (s
*formatState
) parseNum() bool {
411 if s
.nbytes
< len(s
.format
) && s
.format
[s
.nbytes
] == '*' {
412 if s
.indexPending
{ // Absorb it.
413 s
.indexPending
= false
416 s
.argNums
= append(s
.argNums
, s
.argNum
)
424 // parsePrecision scans for a precision. It returns false if there's a bad index expression.
425 func (s
*formatState
) parsePrecision() bool {
426 // If there's a period, there may be a precision.
427 if s
.nbytes
< len(s
.format
) && s
.format
[s
.nbytes
] == '.' {
428 s
.flags
= append(s
.flags
, '.') // Treat precision as a flag.
440 // parsePrintfVerb looks the formatting directive that begins the format string
441 // and returns a formatState that encodes what the directive wants, without looking
442 // at the actual arguments present in the call. The result is nil if there is an error.
443 func (f
*File
) parsePrintfVerb(call
*ast
.CallExpr
, name
, format
string, firstArg
, argNum
int) *formatState
{
444 state
:= &formatState
{
447 flags
: make([]byte, 0, 5),
449 argNums
: make([]int, 0, 1),
450 nbytes
: 1, // There's guaranteed to be a percent sign.
455 // There may be flags.
457 // There may be an index.
458 if !state
.parseIndex() {
461 // There may be a width.
462 if !state
.parseNum() {
465 // There may be a precision.
466 if !state
.parsePrecision() {
469 // Now a verb, possibly prefixed by an index (which we may already have).
470 if !state
.indexPending
&& !state
.parseIndex() {
473 if state
.nbytes
== len(state
.format
) {
474 f
.Badf(call
.Pos(), "%s format %s is missing verb at end of string", name
, state
.format
)
477 verb
, w
:= utf8
.DecodeRuneInString(state
.format
[state
.nbytes
:])
481 state
.argNums
= append(state
.argNums
, state
.argNum
)
483 state
.format
= state
.format
[:state
.nbytes
]
487 // printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask.
488 type printfArgType
int
491 argBool printfArgType
= 1 << iota
498 anyType printfArgType
= ^0
501 type printVerb
struct {
502 verb rune
// User may provide verb through Formatter; could be a rune.
503 flags
string // known flags are all ASCII
507 // Common flag sets for printf verbs.
511 sharpNumFlag
= " -+.0#"
515 // printVerbs identifies which flags are known to printf for each verb.
516 var printVerbs
= []printVerb
{
517 // '-' is a width modifier, always valid.
518 // '.' is a precision for float, max width for strings.
519 // '+' is required sign for numbers, Go format for %v.
520 // '#' is alternate format for several verbs.
521 // ' ' is spacer for numbers
523 {'b', numFlag
, argInt | argFloat | argComplex
},
524 {'c', "-", argRune | argInt
},
525 {'d', numFlag
, argInt
},
526 {'e', sharpNumFlag
, argFloat | argComplex
},
527 {'E', sharpNumFlag
, argFloat | argComplex
},
528 {'f', sharpNumFlag
, argFloat | argComplex
},
529 {'F', sharpNumFlag
, argFloat | argComplex
},
530 {'g', sharpNumFlag
, argFloat | argComplex
},
531 {'G', sharpNumFlag
, argFloat | argComplex
},
532 {'o', sharpNumFlag
, argInt
},
533 {'p', "-#", argPointer
},
534 {'q', " -+.0#", argRune | argInt | argString
},
535 {'s', " -+.0", argString
},
538 {'U', "-#", argRune | argInt
},
539 {'v', allFlags
, anyType
},
540 {'x', sharpNumFlag
, argRune | argInt | argString
},
541 {'X', sharpNumFlag
, argRune | argInt | argString
},
544 // okPrintfArg compares the formatState to the arguments actually present,
545 // reporting any discrepancies it can discern. If the final argument is ellipsissed,
546 // there's little it can do for that.
547 func (f
*File
) okPrintfArg(call
*ast
.CallExpr
, state
*formatState
) (ok
bool) {
550 // Linear scan is fast enough for a small list.
551 for _
, v
= range printVerbs
{
552 if v
.verb
== state
.verb
{
558 // Does current arg implement fmt.Formatter?
560 if state
.argNum
< len(call
.Args
) {
561 if tv
, ok
:= f
.pkg
.types
[call
.Args
[state
.argNum
]]; ok
{
562 formatter
= f
.isFormatter(tv
.Type
)
568 f
.Badf(call
.Pos(), "%s format %s has unknown verb %c", state
.name
, state
.format
, state
.verb
)
571 for _
, flag
:= range state
.flags
{
572 if !strings
.ContainsRune(v
.flags
, rune(flag
)) {
573 f
.Badf(call
.Pos(), "%s format %s has unrecognized flag %c", state
.name
, state
.format
, flag
)
578 // Verb is good. If len(state.argNums)>trueArgs, we have something like %.*s and all
579 // but the final arg must be an integer.
581 if state
.verb
== '%' {
584 nargs
:= len(state
.argNums
)
585 for i
:= 0; i
< nargs
-trueArgs
; i
++ {
586 argNum
:= state
.argNums
[i
]
587 if !f
.argCanBeChecked(call
, i
, state
) {
590 arg
:= call
.Args
[argNum
]
591 if !f
.matchArgType(argInt
, nil, arg
) {
592 f
.Badf(call
.Pos(), "%s format %s uses non-int %s as argument of *", state
.name
, state
.format
, f
.gofmt(arg
))
596 if state
.verb
== '%' || formatter
{
599 argNum
:= state
.argNums
[len(state
.argNums
)-1]
600 if !f
.argCanBeChecked(call
, len(state
.argNums
)-1, state
) {
603 arg
:= call
.Args
[argNum
]
604 if f
.isFunctionValue(arg
) && state
.verb
!= 'p' && state
.verb
!= 'T' {
605 f
.Badf(call
.Pos(), "%s format %s arg %s is a func value, not called", state
.name
, state
.format
, f
.gofmt(arg
))
608 if !f
.matchArgType(v
.typ
, nil, arg
) {
610 if typ
:= f
.pkg
.types
[arg
].Type
; typ
!= nil {
611 typeString
= typ
.String()
613 f
.Badf(call
.Pos(), "%s format %s has arg %s of wrong type %s", state
.name
, state
.format
, f
.gofmt(arg
), typeString
)
616 if v
.typ
&argString
!= 0 && v
.verb
!= 'T' && !bytes
.Contains(state
.flags
, []byte{'#'}) && f
.recursiveStringer(arg
) {
617 f
.Badf(call
.Pos(), "%s format %s with arg %s causes recursive String method call", state
.name
, state
.format
, f
.gofmt(arg
))
623 // recursiveStringer reports whether the provided argument is r or &r for the
624 // fmt.Stringer receiver identifier r.
625 func (f
*File
) recursiveStringer(e ast
.Expr
) bool {
626 if len(f
.stringers
) == 0 {
630 switch e
:= e
.(type) {
634 if id
, ok
:= e
.X
.(*ast
.Ident
); ok
&& e
.Op
== token
.AND
{
639 // It's unlikely to be a recursive stringer if it has a Format method.
640 if typ
:= f
.pkg
.types
[e
].Type
; typ
!= nil {
641 // Not a perfect match; see issue 6259.
642 if f
.hasMethod(typ
, "Format") {
647 // We compare the underlying Object, which checks that the identifier
648 // is the one we declared as the receiver for the String method in
649 // which this printf appears.
650 return f
.stringers
[obj
]
653 // isFunctionValue reports whether the expression is a function as opposed to a function call.
654 // It is almost always a mistake to print a function value.
655 func (f
*File
) isFunctionValue(e ast
.Expr
) bool {
656 if typ
:= f
.pkg
.types
[e
].Type
; typ
!= nil {
657 _
, ok
:= typ
.(*types
.Signature
)
663 // argCanBeChecked reports whether the specified argument is statically present;
664 // it may be beyond the list of arguments or in a terminal slice... argument, which
665 // means we can't see it.
666 func (f
*File
) argCanBeChecked(call
*ast
.CallExpr
, formatArg
int, state
*formatState
) bool {
667 argNum
:= state
.argNums
[formatArg
]
669 // Shouldn't happen, so catch it with prejudice.
670 panic("negative arg num")
672 if argNum
< len(call
.Args
)-1 {
673 return true // Always OK.
675 if call
.Ellipsis
.IsValid() {
676 return false // We just can't tell; there could be many more arguments.
678 if argNum
< len(call
.Args
) {
681 // There are bad indexes in the format or there are fewer arguments than the format needs.
682 // This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi".
683 arg
:= argNum
- state
.firstArg
+ 1 // People think of arguments as 1-indexed.
684 f
.Badf(call
.Pos(), "%s format %s reads arg #%d, but call has only %v", state
.name
, state
.format
, arg
, count(len(call
.Args
)-state
.firstArg
, "arg"))
688 // printFormatRE is the regexp we match and report as a possible format string
689 // in the first argument to unformatted prints like fmt.Print.
690 // We exclude the space flag, so that printing a string like "x % y" is not reported as a format.
691 var printFormatRE
= regexp
.MustCompile(`%` + flagsRE
+ numOptRE
+ `\.?` + numOptRE
+ indexOptRE
+ verbRE
)
695 indexOptRE
= `(\[[0-9]+\])?`
696 numOptRE
= `([0-9]+|` + indexOptRE
+ `\*)?`
697 verbRE
= `[bcdefgopqstvxEFGUX]`
700 // checkPrint checks a call to an unformatted print routine such as Println.
701 func (f
*File
) checkPrint(call
*ast
.CallExpr
, name
string) {
703 typ
:= f
.pkg
.types
[call
.Fun
].Type
705 // Skip checking functions with unknown type.
708 if sig
, ok
:= typ
.(*types
.Signature
); ok
{
710 // Skip checking non-variadic functions.
713 params
:= sig
.Params()
714 firstArg
= params
.Len() - 1
716 typ
:= params
.At(firstArg
).Type()
717 typ
= typ
.(*types
.Slice
).Elem()
718 it
, ok
:= typ
.(*types
.Interface
)
719 if !ok ||
!it
.Empty() {
720 // Skip variadic functions accepting non-interface{} args.
725 if len(args
) <= firstArg
{
726 // Skip calls without variadic args.
729 args
= args
[firstArg
:]
732 if sel
, ok
:= call
.Args
[0].(*ast
.SelectorExpr
); ok
{
733 if x
, ok
:= sel
.X
.(*ast
.Ident
); ok
{
734 if x
.Name
== "os" && strings
.HasPrefix(sel
.Sel
.Name
, "Std") {
735 f
.Badf(call
.Pos(), "%s does not take io.Writer but has first arg %s", name
, f
.gofmt(call
.Args
[0]))
742 if lit
, ok
:= arg
.(*ast
.BasicLit
); ok
&& lit
.Kind
== token
.STRING
{
743 // Ignore trailing % character in lit.Value.
744 // The % in "abc 0.0%" couldn't be a formatting directive.
745 s
:= strings
.TrimSuffix(lit
.Value
, `%"`)
746 if strings
.Contains(s
, "%") {
747 m
:= printFormatRE
.FindStringSubmatch(s
)
749 f
.Badf(call
.Pos(), "%s call has possible formatting directive %s", name
, m
[0])
753 if strings
.HasSuffix(name
, "ln") {
754 // The last item, if a string, should not have a newline.
755 arg
= args
[len(args
)-1]
756 if lit
, ok
:= arg
.(*ast
.BasicLit
); ok
&& lit
.Kind
== token
.STRING
{
757 str
, _
:= strconv
.Unquote(lit
.Value
)
758 if strings
.HasSuffix(str
, "\n") {
759 f
.Badf(call
.Pos(), "%s arg list ends with redundant newline", name
)
763 for _
, arg
:= range args
{
764 if f
.isFunctionValue(arg
) {
765 f
.Badf(call
.Pos(), "%s arg %s is a func value, not called", name
, f
.gofmt(arg
))
767 if f
.recursiveStringer(arg
) {
768 f
.Badf(call
.Pos(), "%s arg %s causes recursive call to String method", name
, f
.gofmt(arg
))
773 // count(n, what) returns "1 what" or "N whats"
774 // (assuming the plural of what is whats).
775 func count(n
int, what
string) string {
779 return fmt
.Sprintf("%d %ss", n
, what
)