github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/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 // This file contains the pieces of the tool that use typechecking from the go/types package. 6 7 package main 8 9 import ( 10 "go/ast" 11 "go/importer" 12 "go/token" 13 "go/types" 14 ) 15 16 // stdImporter is the importer we use to import packages. 17 // It is created during initialization so that all packages 18 // are imported by the same importer. 19 var stdImporter = importer.Default() 20 21 var ( 22 errorType *types.Interface 23 stringerType *types.Interface // possibly nil 24 formatterType *types.Interface // possibly nil 25 ) 26 27 func init() { 28 errorType = types.Universe.Lookup("error").Type().Underlying().(*types.Interface) 29 30 if typ := importType("fmt", "Stringer"); typ != nil { 31 stringerType = typ.Underlying().(*types.Interface) 32 } 33 34 if typ := importType("fmt", "Formatter"); typ != nil { 35 formatterType = typ.Underlying().(*types.Interface) 36 } 37 } 38 39 // importType returns the type denoted by the qualified identifier 40 // path.name, and adds the respective package to the imports map 41 // as a side effect. In case of an error, importType returns nil. 42 func importType(path, name string) types.Type { 43 pkg, err := stdImporter.Import(path) 44 if err != nil { 45 // This can happen if the package at path hasn't been compiled yet. 46 warnf("import failed: %v", err) 47 return nil 48 } 49 if obj, ok := pkg.Scope().Lookup(name).(*types.TypeName); ok { 50 return obj.Type() 51 } 52 warnf("invalid type name %q", name) 53 return nil 54 } 55 56 func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error { 57 pkg.defs = make(map[*ast.Ident]types.Object) 58 pkg.uses = make(map[*ast.Ident]types.Object) 59 pkg.selectors = make(map[*ast.SelectorExpr]*types.Selection) 60 pkg.spans = make(map[types.Object]Span) 61 pkg.types = make(map[ast.Expr]types.TypeAndValue) 62 config := types.Config{ 63 // We use the same importer for all imports to ensure that 64 // everybody sees identical packages for the given paths. 65 Importer: stdImporter, 66 // By providing a Config with our own error function, it will continue 67 // past the first error. There is no need for that function to do anything. 68 Error: func(error) {}, 69 } 70 info := &types.Info{ 71 Selections: pkg.selectors, 72 Types: pkg.types, 73 Defs: pkg.defs, 74 Uses: pkg.uses, 75 } 76 typesPkg, err := config.Check(pkg.path, fs, astFiles, info) 77 pkg.typesPkg = typesPkg 78 // update spans 79 for id, obj := range pkg.defs { 80 pkg.growSpan(id, obj) 81 } 82 for id, obj := range pkg.uses { 83 pkg.growSpan(id, obj) 84 } 85 return err 86 } 87 88 // matchArgType reports an error if printf verb t is not appropriate 89 // for operand arg. 90 // 91 // typ is used only for recursive calls; external callers must supply nil. 92 // 93 // (Recursion arises from the compound types {map,chan,slice} which 94 // may be printed with %d etc. if that is appropriate for their element 95 // types.) 96 func (f *File) matchArgType(t printfArgType, typ types.Type, arg ast.Expr) bool { 97 return f.matchArgTypeInternal(t, typ, arg, make(map[types.Type]bool)) 98 } 99 100 // matchArgTypeInternal is the internal version of matchArgType. It carries a map 101 // remembering what types are in progress so we don't recur when faced with recursive 102 // types or mutually recursive types. 103 func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Expr, inProgress map[types.Type]bool) bool { 104 // %v, %T accept any argument type. 105 if t == anyType { 106 return true 107 } 108 if typ == nil { 109 // external call 110 typ = f.pkg.types[arg].Type 111 if typ == nil { 112 return true // probably a type check problem 113 } 114 } 115 // If the type implements fmt.Formatter, we have nothing to check. 116 if f.isFormatter(typ) { 117 return true 118 } 119 // If we can use a string, might arg (dynamically) implement the Stringer or Error interface? 120 if t&argString != 0 { 121 if types.AssertableTo(errorType, typ) || stringerType != nil && types.AssertableTo(stringerType, typ) { 122 return true 123 } 124 } 125 126 typ = typ.Underlying() 127 if inProgress[typ] { 128 // We're already looking at this type. The call that started it will take care of it. 129 return true 130 } 131 inProgress[typ] = true 132 133 switch typ := typ.(type) { 134 case *types.Signature: 135 return t&argPointer != 0 136 137 case *types.Map: 138 // Recur: map[int]int matches %d. 139 return t&argPointer != 0 || 140 (f.matchArgTypeInternal(t, typ.Key(), arg, inProgress) && f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress)) 141 142 case *types.Chan: 143 return t&argPointer != 0 144 145 case *types.Array: 146 // Same as slice. 147 if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 { 148 return true // %s matches []byte 149 } 150 // Recur: []int matches %d. 151 return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem().Underlying(), arg, inProgress) 152 153 case *types.Slice: 154 // Same as array. 155 if types.Identical(typ.Elem().Underlying(), types.Typ[types.Byte]) && t&argString != 0 { 156 return true // %s matches []byte 157 } 158 // Recur: []int matches %d. But watch out for 159 // type T []T 160 // If the element is a pointer type (type T[]*T), it's handled fine by the Pointer case below. 161 return t&argPointer != 0 || f.matchArgTypeInternal(t, typ.Elem(), arg, inProgress) 162 163 case *types.Pointer: 164 // Ugly, but dealing with an edge case: a known pointer to an invalid type, 165 // probably something from a failed import. 166 if typ.Elem().String() == "invalid type" { 167 if *verbose { 168 f.Warnf(arg.Pos(), "printf argument %v is pointer to invalid or unknown type", f.gofmt(arg)) 169 } 170 return true // special case 171 } 172 // If it's actually a pointer with %p, it prints as one. 173 if t == argPointer { 174 return true 175 } 176 // If it's pointer to struct, that's equivalent in our analysis to whether we can print the struct. 177 if str, ok := typ.Elem().Underlying().(*types.Struct); ok { 178 return f.matchStructArgType(t, str, arg, inProgress) 179 } 180 // The rest can print with %p as pointers, or as integers with %x etc. 181 return t&(argInt|argPointer) != 0 182 183 case *types.Struct: 184 return f.matchStructArgType(t, typ, arg, inProgress) 185 186 case *types.Interface: 187 // There's little we can do. 188 // Whether any particular verb is valid depends on the argument. 189 // The user may have reasonable prior knowledge of the contents of the interface. 190 return true 191 192 case *types.Basic: 193 switch typ.Kind() { 194 case types.UntypedBool, 195 types.Bool: 196 return t&argBool != 0 197 198 case types.UntypedInt, 199 types.Int, 200 types.Int8, 201 types.Int16, 202 types.Int32, 203 types.Int64, 204 types.Uint, 205 types.Uint8, 206 types.Uint16, 207 types.Uint32, 208 types.Uint64, 209 types.Uintptr: 210 return t&argInt != 0 211 212 case types.UntypedFloat, 213 types.Float32, 214 types.Float64: 215 return t&argFloat != 0 216 217 case types.UntypedComplex, 218 types.Complex64, 219 types.Complex128: 220 return t&argComplex != 0 221 222 case types.UntypedString, 223 types.String: 224 return t&argString != 0 225 226 case types.UnsafePointer: 227 return t&(argPointer|argInt) != 0 228 229 case types.UntypedRune: 230 return t&(argInt|argRune) != 0 231 232 case types.UntypedNil: 233 return t&argPointer != 0 // TODO? 234 235 case types.Invalid: 236 if *verbose { 237 f.Warnf(arg.Pos(), "printf argument %v has invalid or unknown type", f.gofmt(arg)) 238 } 239 return true // Probably a type check problem. 240 } 241 panic("unreachable") 242 } 243 244 return false 245 } 246 247 // hasBasicType reports whether x's type is a types.Basic with the given kind. 248 func (f *File) hasBasicType(x ast.Expr, kind types.BasicKind) bool { 249 t := f.pkg.types[x].Type 250 if t != nil { 251 t = t.Underlying() 252 } 253 b, ok := t.(*types.Basic) 254 return ok && b.Kind() == kind 255 } 256 257 // matchStructArgType reports whether all the elements of the struct match the expected 258 // type. For instance, with "%d" all the elements must be printable with the "%d" format. 259 func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Expr, inProgress map[types.Type]bool) bool { 260 for i := 0; i < typ.NumFields(); i++ { 261 if !f.matchArgTypeInternal(t, typ.Field(i).Type(), arg, inProgress) { 262 return false 263 } 264 } 265 return true 266 } 267 268 // hasMethod reports whether the type contains a method with the given name. 269 // It is part of the workaround for Formatters and should be deleted when 270 // that workaround is no longer necessary. 271 // TODO: This could be better once issue 6259 is fixed. 272 func (f *File) hasMethod(typ types.Type, name string) bool { 273 // assume we have an addressable variable of type typ 274 obj, _, _ := types.LookupFieldOrMethod(typ, true, f.pkg.typesPkg, name) 275 _, ok := obj.(*types.Func) 276 return ok 277 }