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