# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
+# Assumes go/types is installed
test testshort:
- go build -tags vet_test
+ go build -tags 'vet_test gotypes'
../../../test/errchk ./vet -compositewhitelist=false -printfuncs='Warn:1,Warnf:1' *.go
"go/parser"
"go/printer"
"go/token"
- "go/types"
"io/ioutil"
"os"
"path/filepath"
}
type Package struct {
- types map[ast.Expr]types.Type
+ types map[ast.Expr]Type
values map[ast.Expr]interface{}
}
astFiles = append(astFiles, parsedFile)
}
pkg := new(Package)
- pkg.types = make(map[ast.Expr]types.Type)
- pkg.values = make(map[ast.Expr]interface{})
- exprFn := func(x ast.Expr, typ types.Type, val interface{}) {
- pkg.types[x] = typ
- if val != nil {
- pkg.values[x] = val
- }
- }
- // By providing the Context with our own error function, it will continue
- // past the first error. There is no need for that function to do anything.
- context := types.Context{
- Expr: exprFn,
- Error: func(error) {},
- }
// Type check the package.
- _, err := context.Check(fs, astFiles)
+ err := pkg.check(fs, astFiles)
if err != nil && *verbose {
warnf("%s", err)
}
"flag"
"go/ast"
"go/token"
- "go/types"
"strconv"
"strings"
"unicode/utf8"
f.Badf(call.Pos(), "unrecognized printf verb %q", verb)
}
-func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
- // TODO: for now, we can only test builtin types and untyped constants.
- typ := f.pkg.types[arg]
- if typ == nil {
- return true
- }
- basic, ok := typ.(*types.Basic)
- if !ok {
- return true
- }
- switch basic.Kind {
- case types.Bool:
- return t&argBool != 0
- case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
- fallthrough
- case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr:
- return t&argInt != 0
- case types.Float32, types.Float64, types.Complex64, types.Complex128:
- return t&argFloat != 0
- case types.String:
- return t&argString != 0
- case types.UnsafePointer:
- return t&(argPointer|argInt) != 0
- case types.UntypedBool:
- return t&argBool != 0
- case types.UntypedComplex:
- return t&argFloat != 0
- case types.UntypedFloat:
- // If it's integral, we can use an int format.
- switch f.pkg.values[arg].(type) {
- case int, int8, int16, int32, int64:
- return t&(argInt|argFloat) != 0
- case uint, uint8, uint16, uint32, uint64:
- return t&(argInt|argFloat) != 0
- }
- return t&argFloat != 0
- case types.UntypedInt:
- return t&argInt != 0
- case types.UntypedRune:
- return t&(argInt|argRune) != 0
- case types.UntypedString:
- return t&argString != 0
- case types.UntypedNil:
- return t&argPointer != 0 // TODO?
- case types.Invalid:
- if *verbose {
- f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", arg)
- }
- return true // Probably a type check problem.
- }
- return false
-}
-
// checkPrint checks a call to an unformatted print routine such as Println.
// call.Args[firstArg] is the first argument to be printed.
func (f *File) checkPrint(call *ast.CallExpr, name string, firstArg int) {
}
}
}
-
-// numArgsInSignature tells how many formal arguments the function type
-// being called has.
-func (f *File) numArgsInSignature(call *ast.CallExpr) int {
- // Check the type of the function or method declaration
- typ := f.pkg.types[call.Fun]
- if typ == nil {
- return 0
- }
- // The type must be a signature, but be sure for safety.
- sig, ok := typ.(*types.Signature)
- if !ok {
- return 0
- }
- return len(sig.Params)
-}
-
-// isErrorMethodCall reports whether the call is of a method with signature
-// func Error() string
-// where "string" is the universe's string type. We know the method is called "Error".
-func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
- // Is it a selector expression? Otherwise it's a function call, not a method call.
- sel, ok := call.Fun.(*ast.SelectorExpr)
- if !ok {
- return false
- }
- // The package is type-checked, so if there are no arguments, we're done.
- if len(call.Args) > 0 {
- return false
- }
- // Check the type of the method declaration
- typ := f.pkg.types[sel]
- if typ == nil {
- return false
- }
- // The type must be a signature, but be sure for safety.
- sig, ok := typ.(*types.Signature)
- if !ok {
- return false
- }
- // There must be a receiver for it to be a method call. Otherwise it is
- // a function, not something that satisfies the error interface.
- if sig.Recv == nil {
- return false
- }
- // There must be no arguments. Already verified by type checking, but be thorough.
- if len(sig.Params) > 0 {
- return false
- }
- // Finally the real questions.
- // There must be one result.
- if len(sig.Results) != 1 {
- return false
- }
- // It must have return type "string" from the universe.
- result := sig.Results[0].Type
- if types.IsIdentical(result, types.Typ[types.String]) {
- return true
- }
- return false
-}
import (
"flag"
"go/ast"
- "go/types"
"strings"
)
return
}
- // Check that the CompositeLit's type is a slice or array (which needs no tag), if possible.
- typ := f.pkg.types[c]
- if typ != nil {
- // If it's a named type, pull out the underlying type.
- if namedType, ok := typ.(*types.NamedType); ok {
- typ = namedType.Underlying
- }
- switch typ.(type) {
- case *types.Slice:
- return
- case *types.Array:
- return
- }
+ isStruct, typeString := f.pkg.isStruct(c)
+ if !isStruct {
+ return
}
// It's a struct, or we can't tell it's not a struct because we don't have types.
return
}
- pre := ""
- if typ != nil {
- pre = typ.String() + " "
- }
- f.Warn(c.Pos(), pre+"composite literal uses untagged fields")
+ f.Warn(c.Pos(), typeString+" composite literal uses untagged fields")
}
// pkgPath returns the import path "image/png" for the package name "png".
"encoding/xml.Comment": true,
"encoding/xml.Directive": true,
"exp/norm.Decomposition": true,
- "exp/types.ObjList": true,
"go/scanner.ErrorList": true,
"image/color.Palette": true,
"net.HardwareAddr": true,
--- /dev/null
+// 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.
+
+// +build gotypes
+
+// This file contains the pieces of the tool that require the go/types package.
+
+package main
+
+import (
+ "go/ast"
+ "go/token"
+ "go/types"
+)
+
+// Type is equivalent to go/types.Type. Repeating it here allows us to avoid
+// depending on the go/types package.
+type Type interface {
+ String() string
+}
+
+func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
+ pkg.types = make(map[ast.Expr]Type)
+ pkg.values = make(map[ast.Expr]interface{})
+ exprFn := func(x ast.Expr, typ types.Type, val interface{}) {
+ pkg.types[x] = typ
+ if val != nil {
+ pkg.values[x] = val
+ }
+ }
+ // By providing the Context with our own error function, it will continue
+ // past the first error. There is no need for that function to do anything.
+ context := types.Context{
+ Expr: exprFn,
+ Error: func(error) {},
+ }
+ _, err := context.Check(fs, astFiles)
+ return err
+}
+
+// isStruct reports whether the composite literal c is a struct.
+// If it is not (probably a struct), it returns a printable form of the type.
+func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
+ // Check that the CompositeLit's type is a slice or array (which needs no tag), if possible.
+ typ := pkg.types[c]
+ if typ == nil {
+ return false, ""
+ }
+ // If it's a named type, pull out the underlying type.
+ if namedType, ok := typ.(*types.NamedType); ok {
+ typ = namedType.Underlying
+ }
+ switch typ.(type) {
+ case *types.Struct:
+ default:
+ return false, ""
+ }
+ typeString := ""
+ if typ != nil {
+ typeString = typ.String() + " "
+ }
+ return true, typeString
+}
+
+func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
+ // TODO: for now, we can only test builtin types and untyped constants.
+ typ := f.pkg.types[arg]
+ if typ == nil {
+ return true
+ }
+ basic, ok := typ.(*types.Basic)
+ if !ok {
+ return true
+ }
+ switch basic.Kind {
+ case types.Bool:
+ return t&argBool != 0
+ case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
+ fallthrough
+ case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr:
+ return t&argInt != 0
+ case types.Float32, types.Float64, types.Complex64, types.Complex128:
+ return t&argFloat != 0
+ case types.String:
+ return t&argString != 0
+ case types.UnsafePointer:
+ return t&(argPointer|argInt) != 0
+ case types.UntypedBool:
+ return t&argBool != 0
+ case types.UntypedComplex:
+ return t&argFloat != 0
+ case types.UntypedFloat:
+ // If it's integral, we can use an int format.
+ switch f.pkg.values[arg].(type) {
+ case int, int8, int16, int32, int64:
+ return t&(argInt|argFloat) != 0
+ case uint, uint8, uint16, uint32, uint64:
+ return t&(argInt|argFloat) != 0
+ }
+ return t&argFloat != 0
+ case types.UntypedInt:
+ return t&argInt != 0
+ case types.UntypedRune:
+ return t&(argInt|argRune) != 0
+ case types.UntypedString:
+ return t&argString != 0
+ case types.UntypedNil:
+ return t&argPointer != 0 // TODO?
+ case types.Invalid:
+ if *verbose {
+ f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", arg)
+ }
+ return true // Probably a type check problem.
+ }
+ return false
+}
+
+// numArgsInSignature tells how many formal arguments the function type
+// being called has.
+func (f *File) numArgsInSignature(call *ast.CallExpr) int {
+ // Check the type of the function or method declaration
+ typ := f.pkg.types[call.Fun]
+ if typ == nil {
+ return 0
+ }
+ // The type must be a signature, but be sure for safety.
+ sig, ok := typ.(*types.Signature)
+ if !ok {
+ return 0
+ }
+ return len(sig.Params)
+}
+
+// isErrorMethodCall reports whether the call is of a method with signature
+// func Error() string
+// where "string" is the universe's string type. We know the method is called "Error".
+func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
+ // Is it a selector expression? Otherwise it's a function call, not a method call.
+ sel, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return false
+ }
+ // The package is type-checked, so if there are no arguments, we're done.
+ if len(call.Args) > 0 {
+ return false
+ }
+ // Check the type of the method declaration
+ typ := f.pkg.types[sel]
+ if typ == nil {
+ return false
+ }
+ // The type must be a signature, but be sure for safety.
+ sig, ok := typ.(*types.Signature)
+ if !ok {
+ return false
+ }
+ // There must be a receiver for it to be a method call. Otherwise it is
+ // a function, not something that satisfies the error interface.
+ if sig.Recv == nil {
+ return false
+ }
+ // There must be no arguments. Already verified by type checking, but be thorough.
+ if len(sig.Params) > 0 {
+ return false
+ }
+ // Finally the real questions.
+ // There must be one result.
+ if len(sig.Results) != 1 {
+ return false
+ }
+ // It must have return type "string" from the universe.
+ result := sig.Results[0].Type
+ if types.IsIdentical(result, types.Typ[types.String]) {
+ return true
+ }
+ return false
+}
--- /dev/null
+// 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.
+
+// +build !gotypes
+
+// This file contains stubs for the pieces of the tool that require the go/types package,
+// to be used if go/types is not available.
+
+package main
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// Type is equivalent to go/types.Type. Repeating it here allows us to avoid
+// depending on the go/types package.
+type Type interface {
+ String() string
+}
+
+func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
+ return nil
+}
+
+func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
+ return true, "struct" // Assume true, so we do the check.
+}
+
+func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool {
+ return true // We can't tell without types.
+}
+
+func (f *File) numArgsInSignature(call *ast.CallExpr) int {
+ return 0 // We don't know.
+}
+
+func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
+ // Is it a selector expression? Otherwise it's a function call, not a method call.
+ if _, ok := call.Fun.(*ast.SelectorExpr); !ok {
+ return false
+ }
+ return true // Best guess we can make without types.
+}