github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/vet/types.go (about) 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 // +build gotypes 6 7 // This file contains the pieces of the tool that require the go/types package. 8 // To compile this file, you must first run 9 // $ go get code.google.com/p/go.exp/go/types 10 11 package main 12 13 import ( 14 "go/ast" 15 "go/token" 16 17 "code.google.com/p/go.exp/go/types" 18 ) 19 20 // Type is equivalent to go/types.Type. Repeating it here allows us to avoid 21 // depending on the go/types package. 22 type Type interface { 23 String() string 24 } 25 26 func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error { 27 pkg.types = make(map[ast.Expr]Type) 28 pkg.values = make(map[ast.Expr]interface{}) 29 exprFn := func(x ast.Expr, typ types.Type, val interface{}) { 30 pkg.types[x] = typ 31 if val != nil { 32 pkg.values[x] = val 33 } 34 } 35 // By providing the Context with our own error function, it will continue 36 // past the first error. There is no need for that function to do anything. 37 context := types.Context{ 38 Expr: exprFn, 39 Error: func(error) {}, 40 } 41 _, err := context.Check(fs, astFiles) 42 return err 43 } 44 45 // isStruct reports whether the composite literal c is a struct. 46 // If it is not (probably a struct), it returns a printable form of the type. 47 func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) { 48 // Check that the CompositeLit's type is a slice or array (which needs no tag), if possible. 49 typ := pkg.types[c] 50 // If it's a named type, pull out the underlying type. 51 actual := typ 52 if namedType, ok := typ.(*types.NamedType); ok { 53 actual = namedType.Underlying 54 } 55 if actual == nil { 56 // No type information available. Assume true, so we do the check. 57 return true, "" 58 } 59 switch actual.(type) { 60 case *types.Struct: 61 return true, typ.String() 62 default: 63 return false, "" 64 } 65 } 66 67 func (f *File) matchArgType(t printfArgType, arg ast.Expr) bool { 68 // TODO: for now, we can only test builtin types and untyped constants. 69 typ := f.pkg.types[arg] 70 if typ == nil { 71 return true 72 } 73 basic, ok := typ.(*types.Basic) 74 if !ok { 75 return true 76 } 77 switch basic.Kind { 78 case types.Bool: 79 return t&argBool != 0 80 case types.Int, types.Int8, types.Int16, types.Int32, types.Int64: 81 fallthrough 82 case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr: 83 return t&argInt != 0 84 case types.Float32, types.Float64, types.Complex64, types.Complex128: 85 return t&argFloat != 0 86 case types.String: 87 return t&argString != 0 88 case types.UnsafePointer: 89 return t&(argPointer|argInt) != 0 90 case types.UntypedBool: 91 return t&argBool != 0 92 case types.UntypedComplex: 93 return t&argFloat != 0 94 case types.UntypedFloat: 95 // If it's integral, we can use an int format. 96 switch f.pkg.values[arg].(type) { 97 case int, int8, int16, int32, int64: 98 return t&(argInt|argFloat) != 0 99 case uint, uint8, uint16, uint32, uint64: 100 return t&(argInt|argFloat) != 0 101 } 102 return t&argFloat != 0 103 case types.UntypedInt: 104 return t&argInt != 0 105 case types.UntypedRune: 106 return t&(argInt|argRune) != 0 107 case types.UntypedString: 108 return t&argString != 0 109 case types.UntypedNil: 110 return t&argPointer != 0 // TODO? 111 case types.Invalid: 112 if *verbose { 113 f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", arg) 114 } 115 return true // Probably a type check problem. 116 } 117 return false 118 } 119 120 // numArgsInSignature tells how many formal arguments the function type 121 // being called has. 122 func (f *File) numArgsInSignature(call *ast.CallExpr) int { 123 // Check the type of the function or method declaration 124 typ := f.pkg.types[call.Fun] 125 if typ == nil { 126 return 0 127 } 128 // The type must be a signature, but be sure for safety. 129 sig, ok := typ.(*types.Signature) 130 if !ok { 131 return 0 132 } 133 return len(sig.Params) 134 } 135 136 // isErrorMethodCall reports whether the call is of a method with signature 137 // func Error() string 138 // where "string" is the universe's string type. We know the method is called "Error". 139 func (f *File) isErrorMethodCall(call *ast.CallExpr) bool { 140 // Is it a selector expression? Otherwise it's a function call, not a method call. 141 sel, ok := call.Fun.(*ast.SelectorExpr) 142 if !ok { 143 return false 144 } 145 // The package is type-checked, so if there are no arguments, we're done. 146 if len(call.Args) > 0 { 147 return false 148 } 149 // Check the type of the method declaration 150 typ := f.pkg.types[sel] 151 if typ == nil { 152 return false 153 } 154 // The type must be a signature, but be sure for safety. 155 sig, ok := typ.(*types.Signature) 156 if !ok { 157 return false 158 } 159 // There must be a receiver for it to be a method call. Otherwise it is 160 // a function, not something that satisfies the error interface. 161 if sig.Recv == nil { 162 return false 163 } 164 // There must be no arguments. Already verified by type checking, but be thorough. 165 if len(sig.Params) > 0 { 166 return false 167 } 168 // Finally the real questions. 169 // There must be one result. 170 if len(sig.Results) != 1 { 171 return false 172 } 173 // It must have return type "string" from the universe. 174 result := sig.Results[0].Type 175 if types.IsIdentical(result, types.Typ[types.String]) { 176 return true 177 } 178 return false 179 }