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