2010-12-09 10:37:18 -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.
// Govet is a simple checker for static errors in Go source code.
// See doc.go for more information.
package main
import (
2011-10-19 14:06:16 -06:00
"bytes"
2010-12-09 10:37:18 -07:00
"flag"
"fmt"
"io"
"go/ast"
"go/parser"
2011-10-19 14:06:16 -06:00
"go/printer"
2010-12-09 10:37:18 -07:00
"go/token"
"os"
2011-03-06 15:33:23 -07:00
"path/filepath"
2011-06-29 07:52:34 -06:00
"reflect"
2010-12-09 10:37:18 -07:00
"strconv"
"strings"
2011-03-08 15:36:56 -07:00
"utf8"
2010-12-09 10:37:18 -07:00
)
var verbose = flag . Bool ( "v" , false , "verbose" )
var printfuncs = flag . String ( "printfuncs" , "" , "comma-separated list of print function names to check" )
var exitCode = 0
2010-12-14 15:12:22 -07:00
// setExit sets the value for os.Exit when it is called, later. It
// remembers the highest value.
func setExit ( err int ) {
if err > exitCode {
exitCode = err
}
}
2010-12-09 10:37:18 -07:00
// Usage is a replacement usage function for the flags package.
func Usage ( ) {
fmt . Fprintf ( os . Stderr , "Usage of %s:\n" , os . Args [ 0 ] )
flag . PrintDefaults ( )
os . Exit ( 2 )
}
// File is a wrapper for the state of a file used in the parser.
// The parse tree walkers are all methods of this type.
type File struct {
2011-10-19 14:06:16 -06:00
fset * token . FileSet
file * ast . File
b bytes . Buffer // for use by methods
2010-12-09 10:37:18 -07:00
}
func main ( ) {
flag . Usage = Usage
flag . Parse ( )
if * printfuncs != "" {
2011-06-27 17:43:14 -06:00
for _ , name := range strings . Split ( * printfuncs , "," ) {
2010-12-09 10:37:18 -07:00
if len ( name ) == 0 {
flag . Usage ( )
}
skip := 0
if colon := strings . LastIndex ( name , ":" ) ; colon > 0 {
2011-11-01 20:06:05 -06:00
var err error
2010-12-09 10:37:18 -07:00
skip , err = strconv . Atoi ( name [ colon + 1 : ] )
if err != nil {
2011-06-29 07:52:34 -06:00
errorf ( ` illegal format for "Func:N" argument %q; %s ` , name , err )
2010-12-09 10:37:18 -07:00
}
name = name [ : colon ]
}
2011-04-13 17:57:44 -06:00
name = strings . ToLower ( name )
2010-12-09 10:37:18 -07:00
if name [ len ( name ) - 1 ] == 'f' {
printfList [ name ] = skip
} else {
printList [ name ] = skip
}
}
}
if flag . NArg ( ) == 0 {
doFile ( "stdin" , os . Stdin )
} else {
2010-12-14 18:09:24 -07:00
for _ , name := range flag . Args ( ) {
// Is it a directory?
if fi , err := os . Stat ( name ) ; err == nil && fi . IsDirectory ( ) {
walkDir ( name )
} else {
doFile ( name , nil )
}
2010-12-09 10:37:18 -07:00
}
}
os . Exit ( exitCode )
}
// doFile analyzes one file. If the reader is nil, the source code is read from the
// named file.
func doFile ( name string , reader io . Reader ) {
fs := token . NewFileSet ( )
parsedFile , err := parser . ParseFile ( fs , name , reader , 0 )
if err != nil {
2011-06-29 07:52:34 -06:00
errorf ( "%s: %s" , name , err )
2010-12-14 15:12:22 -07:00
return
2010-12-09 10:37:18 -07:00
}
2011-10-19 14:06:16 -06:00
file := & File { fset : fs , file : parsedFile }
2010-12-09 10:37:18 -07:00
file . checkFile ( name , parsedFile )
}
2011-11-01 20:06:05 -06:00
func visit ( path string , f * os . FileInfo , err error ) error {
2011-09-13 18:47:59 -06:00
if err != nil {
errorf ( "walk error: %s" , err )
return nil
}
if f . IsRegular ( ) && strings . HasSuffix ( path , ".go" ) {
2010-12-14 18:09:24 -07:00
doFile ( path , nil )
}
2011-09-13 18:47:59 -06:00
return nil
2010-12-14 18:09:24 -07:00
}
// walkDir recursively walks the tree looking for .go files.
func walkDir ( root string ) {
2011-09-13 18:47:59 -06:00
filepath . Walk ( root , visit )
2010-12-14 18:09:24 -07:00
}
2010-12-14 15:12:22 -07:00
// error formats the error to standard error, adding program
// identification and a newline
2011-06-29 07:52:34 -06:00
func errorf ( format string , args ... interface { } ) {
2010-12-09 10:37:18 -07:00
fmt . Fprintf ( os . Stderr , "govet: " + format + "\n" , args ... )
2010-12-14 15:12:22 -07:00
setExit ( 2 )
2010-12-09 10:37:18 -07:00
}
// Println is fmt.Println guarded by -v.
func Println ( args ... interface { } ) {
if ! * verbose {
return
}
fmt . Println ( args ... )
}
// Printf is fmt.Printf guarded by -v.
func Printf ( format string , args ... interface { } ) {
if ! * verbose {
return
}
fmt . Printf ( format + "\n" , args ... )
}
// Bad reports an error and sets the exit code..
func ( f * File ) Bad ( pos token . Pos , args ... interface { } ) {
f . Warn ( pos , args ... )
2010-12-14 15:12:22 -07:00
setExit ( 1 )
2010-12-09 10:37:18 -07:00
}
// Badf reports a formatted error and sets the exit code.
func ( f * File ) Badf ( pos token . Pos , format string , args ... interface { } ) {
f . Warnf ( pos , format , args ... )
2010-12-14 15:12:22 -07:00
setExit ( 1 )
2010-12-09 10:37:18 -07:00
}
// Warn reports an error but does not set the exit code.
func ( f * File ) Warn ( pos token . Pos , args ... interface { } ) {
2011-10-19 14:06:16 -06:00
loc := f . fset . Position ( pos ) . String ( ) + ": "
2010-12-09 10:37:18 -07:00
fmt . Fprint ( os . Stderr , loc + fmt . Sprintln ( args ... ) )
}
// Warnf reports a formatted error but does not set the exit code.
func ( f * File ) Warnf ( pos token . Pos , format string , args ... interface { } ) {
2011-10-19 14:06:16 -06:00
loc := f . fset . Position ( pos ) . String ( ) + ": "
2010-12-09 10:37:18 -07:00
fmt . Fprintf ( os . Stderr , loc + format + "\n" , args ... )
}
// checkFile checks all the top-level declarations in a file.
func ( f * File ) checkFile ( name string , file * ast . File ) {
2010-12-14 18:09:24 -07:00
Println ( "Checking file" , name )
2010-12-09 10:37:18 -07:00
ast . Walk ( f , file )
}
// Visit implements the ast.Visitor interface.
2010-12-09 11:22:01 -07:00
func ( f * File ) Visit ( node ast . Node ) ast . Visitor {
2010-12-09 10:37:18 -07:00
switch n := node . ( type ) {
case * ast . CallExpr :
f . checkCallExpr ( n )
2011-06-29 07:52:34 -06:00
case * ast . Field :
f . checkFieldTag ( n )
2011-10-19 14:06:16 -06:00
case * ast . FuncDecl :
f . checkMethodDecl ( n )
case * ast . InterfaceType :
f . checkInterfaceType ( n )
2010-12-09 10:37:18 -07:00
}
return f
}
2011-10-19 14:06:16 -06:00
// checkMethodDecl checks for canonical method signatures
// in method declarations.
func ( f * File ) checkMethodDecl ( d * ast . FuncDecl ) {
if d . Recv == nil {
// not a method
return
}
f . checkMethod ( d . Name , d . Type )
}
// checkInterfaceType checks for canonical method signatures
// in interface definitions.
func ( f * File ) checkInterfaceType ( t * ast . InterfaceType ) {
for _ , field := range t . Methods . List {
for _ , id := range field . Names {
f . checkMethod ( id , field . Type . ( * ast . FuncType ) )
}
}
}
type MethodSig struct {
args [ ] string
results [ ] string
}
// canonicalMethods lists the input and output types for Go methods
// that are checked using dynamic interface checks. Because the
// checks are dynamic, such methods would not cause a compile error
// if they have the wrong signature: instead the dynamic check would
// fail, sometimes mysteriously. If a method is found with a name listed
// here but not the input/output types listed here, govet complains.
//
// A few of the canonical methods have very common names.
// For example, a type might implement a Scan method that
// has nothing to do with fmt.Scanner, but we still want to check
// the methods that are intended to implement fmt.Scanner.
// To do that, the arguments that have a + prefix are treated as
// signals that the canonical meaning is intended: if a Scan
// method doesn't have a fmt.ScanState as its first argument,
// we let it go. But if it does have a fmt.ScanState, then the
// rest has to match.
var canonicalMethods = map [ string ] MethodSig {
2011-11-01 20:58:09 -06:00
// "Flush": {{}, {"error"}}, // http.Flusher and jpeg.writer conflict
"Format" : { [ ] string { "=fmt.State" , "rune" } , [ ] string { } } , // fmt.Formatter
"GobDecode" : { [ ] string { "[]byte" } , [ ] string { "error" } } , // gob.GobDecoder
"GobEncode" : { [ ] string { } , [ ] string { "[]byte" , "error" } } , // gob.GobEncoder
"MarshalJSON" : { [ ] string { } , [ ] string { "[]byte" , "error" } } , // json.Marshaler
"MarshalXML" : { [ ] string { } , [ ] string { "[]byte" , "error" } } , // xml.Marshaler
"Peek" : { [ ] string { "=int" } , [ ] string { "[]byte" , "error" } } , // image.reader (matching bufio.Reader)
"ReadByte" : { [ ] string { } , [ ] string { "byte" , "error" } } , // io.ByteReader
"ReadFrom" : { [ ] string { "=io.Reader" } , [ ] string { "int64" , "error" } } , // io.ReaderFrom
"ReadRune" : { [ ] string { } , [ ] string { "rune" , "int" , "error" } } , // io.RuneReader
"Scan" : { [ ] string { "=fmt.ScanState" , "rune" } , [ ] string { "error" } } , // fmt.Scanner
"Seek" : { [ ] string { "=int64" , "int" } , [ ] string { "int64" , "error" } } , // io.Seeker
"UnmarshalJSON" : { [ ] string { "[]byte" } , [ ] string { "error" } } , // json.Unmarshaler
"UnreadByte" : { [ ] string { } , [ ] string { "error" } } ,
"UnreadRune" : { [ ] string { } , [ ] string { "error" } } ,
"WriteByte" : { [ ] string { "byte" } , [ ] string { "error" } } , // jpeg.writer (matching bufio.Writer)
"WriteTo" : { [ ] string { "=io.Writer" } , [ ] string { "int64" , "error" } } , // io.WriterTo
2011-10-19 14:06:16 -06:00
}
func ( f * File ) checkMethod ( id * ast . Ident , t * ast . FuncType ) {
// Expected input/output.
expect , ok := canonicalMethods [ id . Name ]
if ! ok {
return
}
// Actual input/output
args := typeFlatten ( t . Params . List )
var results [ ] ast . Expr
if t . Results != nil {
results = typeFlatten ( t . Results . List )
}
// Do the =s (if any) all match?
if ! f . matchParams ( expect . args , args , "=" ) || ! f . matchParams ( expect . results , results , "=" ) {
return
}
// Everything must match.
if ! f . matchParams ( expect . args , args , "" ) || ! f . matchParams ( expect . results , results , "" ) {
expectFmt := id . Name + "(" + argjoin ( expect . args ) + ")"
if len ( expect . results ) == 1 {
expectFmt += " " + argjoin ( expect . results )
} else if len ( expect . results ) > 1 {
expectFmt += " (" + argjoin ( expect . results ) + ")"
}
f . b . Reset ( )
if err := printer . Fprint ( & f . b , f . fset , t ) ; err != nil {
fmt . Fprintf ( & f . b , "<%s>" , err )
}
actual := f . b . String ( )
if strings . HasPrefix ( actual , "func(" ) {
actual = actual [ 4 : ]
}
actual = id . Name + actual
f . Warnf ( id . Pos ( ) , "method %s should have signature %s" , actual , expectFmt )
}
}
func argjoin ( x [ ] string ) string {
y := make ( [ ] string , len ( x ) )
for i , s := range x {
if s [ 0 ] == '=' {
s = s [ 1 : ]
}
y [ i ] = s
}
return strings . Join ( y , ", " )
}
// Turn parameter list into slice of types
// (in the ast, types are Exprs).
// Have to handle f(int, bool) and f(x, y, z int)
// so not a simple 1-to-1 conversion.
func typeFlatten ( l [ ] * ast . Field ) [ ] ast . Expr {
var t [ ] ast . Expr
for _ , f := range l {
if len ( f . Names ) == 0 {
t = append ( t , f . Type )
continue
}
for _ = range f . Names {
t = append ( t , f . Type )
}
}
return t
}
// Does each type in expect with the given prefix match the corresponding type in actual?
func ( f * File ) matchParams ( expect [ ] string , actual [ ] ast . Expr , prefix string ) bool {
for i , x := range expect {
if ! strings . HasPrefix ( x , prefix ) {
continue
}
if i >= len ( actual ) {
return false
}
if ! f . matchParamType ( x , actual [ i ] ) {
return false
}
}
if prefix == "" && len ( actual ) > len ( expect ) {
return false
}
return true
}
// Does this one type match?
func ( f * File ) matchParamType ( expect string , actual ast . Expr ) bool {
if strings . HasPrefix ( expect , "=" ) {
expect = expect [ 1 : ]
}
// Strip package name if we're in that package.
if n := len ( f . file . Name . Name ) ; len ( expect ) > n && expect [ : n ] == f . file . Name . Name && expect [ n ] == '.' {
expect = expect [ n + 1 : ]
}
// Overkill but easy.
f . b . Reset ( )
printer . Fprint ( & f . b , f . fset , actual )
return f . b . String ( ) == expect
}
2011-06-29 07:52:34 -06:00
// checkField checks a struct field tag.
func ( f * File ) checkFieldTag ( field * ast . Field ) {
if field . Tag == nil {
return
}
tag , err := strconv . Unquote ( field . Tag . Value )
if err != nil {
f . Warnf ( field . Pos ( ) , "unable to read struct tag %s" , field . Tag . Value )
return
}
// Check tag for validity by appending
// new key:value to end and checking that
// the tag parsing code can find it.
if reflect . StructTag ( tag + ` _gofix:"_magic" ` ) . Get ( "_gofix" ) != "_magic" {
f . Warnf ( field . Pos ( ) , "struct field tag %s not compatible with reflect.StructTag.Get" , field . Tag . Value )
return
}
}
2010-12-09 10:37:18 -07:00
// checkCallExpr checks a call expression.
func ( f * File ) checkCallExpr ( call * ast . CallExpr ) {
switch x := call . Fun . ( type ) {
case * ast . Ident :
f . checkCall ( call , x . Name )
case * ast . SelectorExpr :
f . checkCall ( call , x . Sel . Name )
}
}
// printfList records the formatted-print functions. The value is the location
2011-04-13 17:57:44 -06:00
// of the format parameter. Names are lower-cased so the lookup is
// case insensitive.
2010-12-09 10:37:18 -07:00
var printfList = map [ string ] int {
2011-04-13 17:57:44 -06:00
"errorf" : 0 ,
"fatalf" : 0 ,
"fprintf" : 1 ,
"panicf" : 0 ,
"printf" : 0 ,
"sprintf" : 0 ,
2010-12-09 10:37:18 -07:00
}
// printList records the unformatted-print functions. The value is the location
2011-04-13 17:57:44 -06:00
// of the first parameter to be printed. Names are lower-cased so the lookup is
// case insensitive.
2010-12-09 10:37:18 -07:00
var printList = map [ string ] int {
2011-04-13 17:57:44 -06:00
"error" : 0 ,
"fatal" : 0 ,
"fprint" : 1 , "fprintln" : 1 ,
"panic" : 0 , "panicln" : 0 ,
"print" : 0 , "println" : 0 ,
"sprint" : 0 , "sprintln" : 0 ,
2010-12-09 10:37:18 -07:00
}
// checkCall triggers the print-specific checks if the call invokes a print function.
2011-04-13 17:57:44 -06:00
func ( f * File ) checkCall ( call * ast . CallExpr , Name string ) {
name := strings . ToLower ( Name )
2010-12-09 10:37:18 -07:00
if skip , ok := printfList [ name ] ; ok {
2011-04-13 17:57:44 -06:00
f . checkPrintf ( call , Name , skip )
2010-12-09 10:37:18 -07:00
return
}
if skip , ok := printList [ name ] ; ok {
2011-04-13 17:57:44 -06:00
f . checkPrint ( call , Name , skip )
2010-12-09 10:37:18 -07:00
return
}
}
// 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
}
// Common case: literal is first argument.
arg := call . Args [ skip ]
lit , ok := arg . ( * ast . BasicLit )
if ! ok {
// Too hard to check.
if * verbose {
f . Warn ( call . Pos ( ) , "can't check args for call to" , name )
}
return
}
if lit . Kind == token . STRING {
2011-03-29 14:18:52 -06:00
if ! strings . Contains ( lit . Value , "%" ) {
2010-12-09 10:37:18 -07:00
if len ( call . Args ) > skip + 1 {
f . Badf ( call . Pos ( ) , "no formatting directive in %s call" , name )
}
return
}
}
// Hard part: check formats against args.
// Trivial but useful test: count.
2011-03-08 15:36:56 -07:00
numArgs := 0
for i , w := 0 , 0 ; i < len ( lit . Value ) ; i += w {
w = 1
2010-12-09 10:37:18 -07:00
if lit . Value [ i ] == '%' {
2011-03-08 15:36:56 -07:00
nbytes , nargs := parsePrintfVerb ( lit . Value [ i : ] )
w = nbytes
numArgs += nargs
2010-12-09 10:37:18 -07:00
}
}
expect := len ( call . Args ) - ( skip + 1 )
2011-03-08 15:36:56 -07:00
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-03-28 17:44:28 -06:00
func parsePrintfVerb ( s string ) ( nbytes , nargs int ) {
2011-03-08 15:36:56 -07:00
// There's guaranteed a percent sign.
nbytes = 1
end := len ( s )
2011-03-28 17:44:28 -06:00
// There may be flags.
2011-03-08 15:36:56 -07:00
FlagLoop :
for nbytes < end {
switch s [ nbytes ] {
case '#' , '0' , '+' , '-' , ' ' :
nbytes ++
default :
break FlagLoop
}
2010-12-09 10:37:18 -07:00
}
2011-03-08 15:36:56 -07:00
getNum := func ( ) {
if nbytes < end && s [ nbytes ] == '*' {
nbytes ++
nargs ++
} else {
for nbytes < end && '0' <= s [ nbytes ] && s [ nbytes ] <= '9' {
nbytes ++
}
}
}
2011-03-28 17:44:28 -06:00
// There may be a width.
2011-03-08 15:36:56 -07:00
getNum ( )
// If there's a period, there may be a precision.
if nbytes < end && s [ nbytes ] == '.' {
nbytes ++
getNum ( )
}
// Now a verb.
2011-03-28 17:44:28 -06:00
c , w := utf8 . DecodeRuneInString ( s [ nbytes : ] )
2011-03-08 15:36:56 -07:00
nbytes += w
if c != '%' {
nargs ++
}
return
2010-12-09 10:37:18 -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" )
args := call . Args
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 {
2011-03-29 10:08:23 -06:00
if strings . Contains ( lit . Value , "%" ) {
2010-12-09 10:37:18 -07:00
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 {
2011-03-28 17:44:28 -06:00
if strings . HasSuffix ( lit . Value , ` \n" ` ) {
2010-12-09 10:37:18 -07:00
f . Badf ( call . Pos ( ) , "%s call ends with newline" , name )
}
}
}
}
// This function never executes, but it serves as a simple test for the program.
2011-06-30 08:26:11 -06:00
// Test with make test.
2010-12-09 10:37:18 -07:00
func BadFunctionUsedInTests ( ) {
2011-06-30 08:26:11 -06:00
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
2010-12-09 10:37:18 -07:00
f := new ( File )
2011-06-30 08:26:11 -06:00
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"
2010-12-09 10:37:18 -07:00
}
2011-04-13 17:57:44 -06:00
2011-06-29 07:52:34 -06:00
type BadTypeUsedInTests struct {
2011-06-30 08:26:11 -06:00
X int "hello" // ERROR "struct field tag"
2011-06-29 07:52:34 -06:00
}
2011-11-01 20:58:09 -06:00
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"
2011-10-19 14:06:16 -06:00
}
type BadInterfaceUsedInTests interface {
2011-11-01 20:58:09 -06:00
ReadByte ( ) byte // ERROR "method ReadByte[(][)] byte should have signature ReadByte[(][)] [(]byte, error[)]"
2011-10-19 14:06:16 -06:00
}
2011-04-13 17:57:44 -06:00
// printf is used by the test.
func printf ( format string , args ... interface { } ) {
panic ( "don't call - testing only" )
}