2011-12-15 14:44:35 -07:00
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This file contains the printf-checker.
package main
import (
"flag"
"fmt"
"go/ast"
"go/token"
"strings"
"unicode/utf8"
)
var printfuncs = flag . String ( "printfuncs" , "" , "comma-separated list of print function names to check" )
// printfList records the formatted-print functions. The value is the location
// of the format parameter. Names are lower-cased so the lookup is
// case insensitive.
var printfList = map [ string ] int {
"errorf" : 0 ,
"fatalf" : 0 ,
"fprintf" : 1 ,
"panicf" : 0 ,
"printf" : 0 ,
"sprintf" : 0 ,
}
// printList records the unformatted-print functions. The value is the location
// of the first parameter to be printed. Names are lower-cased so the lookup is
// case insensitive.
var printList = map [ string ] int {
"error" : 0 ,
"fatal" : 0 ,
"fprint" : 1 , "fprintln" : 1 ,
"panic" : 0 , "panicln" : 0 ,
"print" : 0 , "println" : 0 ,
"sprint" : 0 , "sprintln" : 0 ,
}
// checkCall triggers the print-specific checks if the call invokes a print function.
func ( f * File ) checkFmtPrintfCall ( call * ast . CallExpr , Name string ) {
name := strings . ToLower ( Name )
if skip , ok := printfList [ name ] ; ok {
f . checkPrintf ( call , Name , skip )
return
}
if skip , ok := printList [ name ] ; ok {
f . checkPrint ( call , Name , skip )
return
}
}
2012-04-24 20:14:38 -06:00
// literal returns the literal value represented by the expression, or nil if it is not a literal.
func ( f * File ) literal ( value ast . Expr ) * ast . BasicLit {
switch v := value . ( type ) {
case * ast . BasicLit :
return v
case * ast . Ident :
// See if it's a constant or initial value (we can't tell the difference).
if v . Obj == nil || v . Obj . Decl == nil {
return nil
}
valueSpec , ok := v . Obj . Decl . ( * ast . ValueSpec )
if ok && len ( valueSpec . Names ) == len ( valueSpec . Values ) {
// Find the index in the list of names
var i int
for i = 0 ; i < len ( valueSpec . Names ) ; i ++ {
if valueSpec . Names [ i ] . Name == v . Name {
if lit , ok := valueSpec . Values [ i ] . ( * ast . BasicLit ) ; ok {
return lit
}
return nil
}
}
}
}
return nil
}
2011-12-15 14:44:35 -07:00
// checkPrintf checks a call to a formatted print routine such as Printf.
// The skip argument records how many arguments to ignore; that is,
// call.Args[skip] is (well, should be) the format argument.
func ( f * File ) checkPrintf ( call * ast . CallExpr , name string , skip int ) {
if len ( call . Args ) <= skip {
return
}
2012-04-24 20:14:38 -06:00
lit := f . literal ( call . Args [ skip ] )
if lit == nil {
2011-12-15 14:44:35 -07:00
if * verbose {
2011-12-15 16:17:52 -07:00
f . Warn ( call . Pos ( ) , "can't check non-literal format in call to" , name )
2011-12-15 14:44:35 -07:00
}
return
}
2012-04-24 20:14:38 -06:00
if lit . Kind != token . STRING {
f . Badf ( call . Pos ( ) , "literal %v not a string in call to" , lit . Value , name )
}
format := lit . Value
if ! strings . Contains ( format , "%" ) {
if len ( call . Args ) > skip + 1 {
f . Badf ( call . Pos ( ) , "no formatting directive in %s call" , name )
2011-12-15 14:44:35 -07:00
}
2012-04-24 20:14:38 -06:00
return
2011-12-15 14:44:35 -07:00
}
// Hard part: check formats against args.
// Trivial but useful test: count.
numArgs := 0
2012-04-24 20:14:38 -06:00
for i , w := 0 , 0 ; i < len ( format ) ; i += w {
2011-12-15 14:44:35 -07:00
w = 1
2012-04-24 20:14:38 -06:00
if format [ i ] == '%' {
nbytes , nargs := f . parsePrintfVerb ( call , format [ i : ] )
2011-12-15 14:44:35 -07:00
w = nbytes
numArgs += nargs
}
}
expect := len ( call . Args ) - ( skip + 1 )
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.
2011-12-15 16:17:52 -07:00
func ( f * File ) parsePrintfVerb ( call * ast . CallExpr , s string ) ( nbytes , nargs int ) {
2011-12-15 14:44:35 -07:00
// There's guaranteed a percent sign.
2011-12-15 16:17:52 -07:00
flags := make ( [ ] byte , 0 , 5 )
2011-12-15 14:44:35 -07:00
nbytes = 1
end := len ( s )
// There may be flags.
FlagLoop :
for nbytes < end {
switch s [ nbytes ] {
case '#' , '0' , '+' , '-' , ' ' :
2011-12-15 16:17:52 -07:00
flags = append ( flags , s [ nbytes ] )
2011-12-15 14:44:35 -07:00
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 ] == '.' {
2011-12-15 16:17:52 -07:00
flags = append ( flags , '.' ) // Treat precision as a flag.
2011-12-15 14:44:35 -07:00
nbytes ++
getNum ( )
}
// Now a verb.
c , w := utf8 . DecodeRuneInString ( s [ nbytes : ] )
nbytes += w
if c != '%' {
nargs ++
2011-12-15 16:17:52 -07:00
f . checkPrintfVerb ( call , c , flags )
2011-12-15 14:44:35 -07:00
}
return
}
2011-12-15 16:17:52 -07:00
type printVerb struct {
verb rune
flags string // known flags are all ASCII
}
// Common flag sets for printf verbs.
const (
numFlag = " -+.0"
sharpNumFlag = " -+.0#"
allFlags = " -+.0#"
)
// printVerbs identifies which flags are known to printf for each verb.
2012-01-29 12:07:25 -07:00
// TODO: A type that implements Formatter may do what it wants, and vet
2011-12-15 16:17:52 -07:00
// will complain incorrectly.
var printVerbs = [ ] printVerb {
// '-' is a width modifier, always valid.
// '.' is a precision for float, max width for strings.
// '+' is required sign for numbers, Go format for %v.
// '#' is alternate format for several verbs.
// ' ' is spacer for numbers
{ 'b' , numFlag } ,
{ 'c' , "-" } ,
{ 'd' , numFlag } ,
2012-02-24 10:16:17 -07:00
{ 'e' , numFlag } ,
2011-12-15 16:17:52 -07:00
{ 'E' , numFlag } ,
{ 'f' , numFlag } ,
{ 'F' , numFlag } ,
{ 'g' , numFlag } ,
{ 'G' , numFlag } ,
{ 'o' , sharpNumFlag } ,
{ 'p' , "-#" } ,
{ 'q' , "-+#." } ,
{ 's' , "-." } ,
{ 't' , "-" } ,
{ 'T' , "-" } ,
{ 'U' , "-#" } ,
{ 'v' , allFlags } ,
{ 'x' , sharpNumFlag } ,
{ 'X' , sharpNumFlag } ,
}
const printfVerbs = "bcdeEfFgGopqstTvxUX"
func ( f * File ) checkPrintfVerb ( call * ast . CallExpr , verb rune , flags [ ] byte ) {
// Linear scan is fast enough for a small list.
for _ , v := range printVerbs {
if v . verb == verb {
for _ , flag := range flags {
if ! strings . ContainsRune ( v . flags , rune ( flag ) ) {
f . Badf ( call . Pos ( ) , "unrecognized printf flag for verb %q: %q" , verb , flag )
}
}
return
}
}
f . Badf ( call . Pos ( ) , "unrecognized printf verb %q" , verb )
}
2011-12-15 14:44:35 -07:00
// checkPrint checks a call to an unformatted print routine such as Println.
// The skip argument records how many arguments to ignore; that is,
// call.Args[skip] is the first argument to be printed.
func ( f * File ) checkPrint ( call * ast . CallExpr , name string , skip int ) {
isLn := strings . HasSuffix ( name , "ln" )
2012-02-14 09:24:41 -07:00
isF := strings . HasPrefix ( name , "F" )
2011-12-15 14:44:35 -07:00
args := call . Args
2012-02-14 09:24:41 -07:00
// check for Println(os.Stderr, ...)
if skip == 0 && ! isF && len ( args ) > 0 {
if sel , ok := args [ 0 ] . ( * ast . SelectorExpr ) ; ok {
if x , ok := sel . X . ( * ast . Ident ) ; ok {
if x . Name == "os" && strings . HasPrefix ( sel . Sel . Name , "Std" ) {
f . Warnf ( call . Pos ( ) , "first argument to %s is %s.%s" , name , x . Name , sel . Sel . Name )
}
}
}
}
2011-12-15 14:44:35 -07:00
if len ( args ) <= skip {
if * verbose && ! isLn {
f . Badf ( call . Pos ( ) , "no args in %s call" , name )
}
return
}
arg := args [ skip ]
if lit , ok := arg . ( * ast . BasicLit ) ; ok && lit . Kind == token . STRING {
if strings . Contains ( lit . Value , "%" ) {
f . Badf ( call . Pos ( ) , "possible formatting directive in %s call" , name )
}
}
if isLn {
// The last item, if a string, should not have a newline.
arg = args [ len ( call . Args ) - 1 ]
if lit , ok := arg . ( * ast . BasicLit ) ; ok && lit . Kind == token . STRING {
if strings . HasSuffix ( lit . Value , ` \n" ` ) {
f . Badf ( call . Pos ( ) , "%s call ends with newline" , name )
}
}
}
}
// This function never executes, but it serves as a simple test for the program.
// Test with make test.
func BadFunctionUsedInTests ( ) {
fmt . Println ( ) // not an error
fmt . Println ( "%s" , "hi" ) // ERROR "possible formatting directive in Println call"
fmt . Printf ( "%s" , "hi" , 3 ) // ERROR "wrong number of args in Printf call"
fmt . Printf ( "%s%%%d" , "hi" , 3 ) // correct
fmt . Printf ( "%.*d" , 3 , 3 ) // correct
fmt . Printf ( "%.*d" , 3 , 3 , 3 ) // ERROR "wrong number of args in Printf call"
printf ( "now is the time" , "buddy" ) // ERROR "no formatting directive"
Printf ( "now is the time" , "buddy" ) // ERROR "no formatting directive"
Printf ( "hi" ) // ok
2012-04-24 20:14:38 -06:00
const format = "%s %s\n"
Printf ( format , "hi" , "there" )
Printf ( format , "hi" ) // ERROR "wrong number of args in Printf call"
2011-12-15 14:44:35 -07:00
f := new ( File )
f . Warn ( 0 , "%s" , "hello" , 3 ) // ERROR "possible formatting directive in Warn call"
f . Warnf ( 0 , "%s" , "hello" , 3 ) // ERROR "wrong number of args in Warnf call"
2011-12-15 16:17:52 -07:00
f . Warnf ( 0 , "%r" , "hello" ) // ERROR "unrecognized printf verb"
f . Warnf ( 0 , "%#s" , "hello" ) // ERROR "unrecognized printf flag"
2011-12-15 14:44:35 -07:00
}
type BadTypeUsedInTests struct {
X int "hello" // ERROR "struct field tag"
}
func ( t * BadTypeUsedInTests ) Scan ( x fmt . ScanState , c byte ) { // ERROR "method Scan[(]x fmt.ScanState, c byte[)] should have signature Scan[(]fmt.ScanState, rune[)] error"
}
type BadInterfaceUsedInTests interface {
ReadByte ( ) byte // ERROR "method ReadByte[(][)] byte should have signature ReadByte[(][)] [(]byte, error[)]"
}
// printf is used by the test.
func printf ( format string , args ... interface { } ) {
panic ( "don't call - testing only" )
}