github.com/zebozhuang/go@v0.0.0-20200207033046-f8a98f6f5c5d/src/go/types/check.go (about) 1 // Copyright 2011 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 implements the Check function, which drives type-checking. 6 7 package types 8 9 import ( 10 "go/ast" 11 "go/constant" 12 "go/token" 13 ) 14 15 // debugging/development support 16 const ( 17 debug = false // leave on during development 18 trace = false // turn on for detailed type resolution traces 19 ) 20 21 // If Strict is set, the type-checker enforces additional 22 // rules not specified by the Go 1 spec, but which will 23 // catch guaranteed run-time errors if the respective 24 // code is executed. In other words, programs passing in 25 // Strict mode are Go 1 compliant, but not all Go 1 programs 26 // will pass in Strict mode. The additional rules are: 27 // 28 // - A type assertion x.(T) where T is an interface type 29 // is invalid if any (statically known) method that exists 30 // for both x and T have different signatures. 31 // 32 const strict = false 33 34 // exprInfo stores information about an untyped expression. 35 type exprInfo struct { 36 isLhs bool // expression is lhs operand of a shift with delayed type-check 37 mode operandMode 38 typ *Basic 39 val constant.Value // constant value; or nil (if not a constant) 40 } 41 42 // funcInfo stores the information required for type-checking a function. 43 type funcInfo struct { 44 name string // for debugging/tracing only 45 decl *declInfo // for cycle detection 46 sig *Signature 47 body *ast.BlockStmt 48 } 49 50 // A context represents the context within which an object is type-checked. 51 type context struct { 52 decl *declInfo // package-level declaration whose init expression/function body is checked 53 scope *Scope // top-most scope for lookups 54 iota constant.Value // value of iota in a constant declaration; nil otherwise 55 sig *Signature // function signature if inside a function; nil otherwise 56 hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions 57 hasCallOrRecv bool // set if an expression contains a function call or channel receive operation 58 } 59 60 // An importKey identifies an imported package by import path and source directory 61 // (directory containing the file containing the import). In practice, the directory 62 // may always be the same, or may not matter. Given an (import path, directory), an 63 // importer must always return the same package (but given two different import paths, 64 // an importer may still return the same package by mapping them to the same package 65 // paths). 66 type importKey struct { 67 path, dir string 68 } 69 70 // A Checker maintains the state of the type checker. 71 // It must be created with NewChecker. 72 type Checker struct { 73 // package information 74 // (initialized by NewChecker, valid for the life-time of checker) 75 conf *Config 76 fset *token.FileSet 77 pkg *Package 78 *Info 79 objMap map[Object]*declInfo // maps package-level object to declaration info 80 impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package 81 82 // information collected during type-checking of a set of package files 83 // (initialized by Files, valid only for the duration of check.Files; 84 // maps and lists are allocated on demand) 85 files []*ast.File // package files 86 unusedDotImports map[*Scope]map[*Package]token.Pos // positions of unused dot-imported packages for each file scope 87 88 firstErr error // first error encountered 89 methods map[string][]*Func // maps type names to associated methods 90 untyped map[ast.Expr]exprInfo // map of expressions without final type 91 funcs []funcInfo // list of functions to type-check 92 delayed []func() // delayed checks requiring fully setup types 93 94 // context within which the current object is type-checked 95 // (valid only for the duration of type-checking a specific object) 96 context 97 pos token.Pos // if valid, identifiers are looked up as if at position pos (used by Eval) 98 99 // debugging 100 indent int // indentation for tracing 101 } 102 103 // addUnusedImport adds the position of a dot-imported package 104 // pkg to the map of dot imports for the given file scope. 105 func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, pos token.Pos) { 106 mm := check.unusedDotImports 107 if mm == nil { 108 mm = make(map[*Scope]map[*Package]token.Pos) 109 check.unusedDotImports = mm 110 } 111 m := mm[scope] 112 if m == nil { 113 m = make(map[*Package]token.Pos) 114 mm[scope] = m 115 } 116 m[pkg] = pos 117 } 118 119 // addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists 120 func (check *Checker) addDeclDep(to Object) { 121 from := check.decl 122 if from == nil { 123 return // not in a package-level init expression 124 } 125 if _, found := check.objMap[to]; !found { 126 return // to is not a package-level object 127 } 128 from.addDep(to) 129 } 130 131 func (check *Checker) assocMethod(tname string, meth *Func) { 132 m := check.methods 133 if m == nil { 134 m = make(map[string][]*Func) 135 check.methods = m 136 } 137 m[tname] = append(m[tname], meth) 138 } 139 140 func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) { 141 m := check.untyped 142 if m == nil { 143 m = make(map[ast.Expr]exprInfo) 144 check.untyped = m 145 } 146 m[e] = exprInfo{lhs, mode, typ, val} 147 } 148 149 func (check *Checker) later(name string, decl *declInfo, sig *Signature, body *ast.BlockStmt) { 150 check.funcs = append(check.funcs, funcInfo{name, decl, sig, body}) 151 } 152 153 func (check *Checker) delay(f func()) { 154 check.delayed = append(check.delayed, f) 155 } 156 157 // NewChecker returns a new Checker instance for a given package. 158 // Package files may be added incrementally via checker.Files. 159 func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Checker { 160 // make sure we have a configuration 161 if conf == nil { 162 conf = new(Config) 163 } 164 165 // make sure we have an info struct 166 if info == nil { 167 info = new(Info) 168 } 169 170 return &Checker{ 171 conf: conf, 172 fset: fset, 173 pkg: pkg, 174 Info: info, 175 objMap: make(map[Object]*declInfo), 176 impMap: make(map[importKey]*Package), 177 } 178 } 179 180 // initFiles initializes the files-specific portion of checker. 181 // The provided files must all belong to the same package. 182 func (check *Checker) initFiles(files []*ast.File) { 183 // start with a clean slate (check.Files may be called multiple times) 184 check.files = nil 185 check.unusedDotImports = nil 186 187 check.firstErr = nil 188 check.methods = nil 189 check.untyped = nil 190 check.funcs = nil 191 check.delayed = nil 192 193 // determine package name and collect valid files 194 pkg := check.pkg 195 for _, file := range files { 196 switch name := file.Name.Name; pkg.name { 197 case "": 198 if name != "_" { 199 pkg.name = name 200 } else { 201 check.errorf(file.Name.Pos(), "invalid package name _") 202 } 203 fallthrough 204 205 case name: 206 check.files = append(check.files, file) 207 208 default: 209 check.errorf(file.Package, "package %s; expected %s", name, pkg.name) 210 // ignore this file 211 } 212 } 213 } 214 215 // A bailout panic is used for early termination. 216 type bailout struct{} 217 218 func (check *Checker) handleBailout(err *error) { 219 switch p := recover().(type) { 220 case nil, bailout: 221 // normal return or early exit 222 *err = check.firstErr 223 default: 224 // re-panic 225 panic(p) 226 } 227 } 228 229 // Files checks the provided files as part of the checker's package. 230 func (check *Checker) Files(files []*ast.File) error { return check.checkFiles(files) } 231 232 func (check *Checker) checkFiles(files []*ast.File) (err error) { 233 defer check.handleBailout(&err) 234 235 check.initFiles(files) 236 237 check.collectObjects() 238 239 check.packageObjects(check.resolveOrder()) 240 241 check.functionBodies() 242 243 check.initOrder() 244 245 if !check.conf.DisableUnusedImportCheck { 246 check.unusedImports() 247 } 248 249 // perform delayed checks 250 for _, f := range check.delayed { 251 f() 252 } 253 254 check.recordUntyped() 255 256 check.pkg.complete = true 257 return 258 } 259 260 func (check *Checker) recordUntyped() { 261 if !debug && check.Types == nil { 262 return // nothing to do 263 } 264 265 for x, info := range check.untyped { 266 if debug && isTyped(info.typ) { 267 check.dump("%s: %s (type %s) is typed", x.Pos(), x, info.typ) 268 unreachable() 269 } 270 check.recordTypeAndValue(x, info.mode, info.typ, info.val) 271 } 272 } 273 274 func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val constant.Value) { 275 assert(x != nil) 276 assert(typ != nil) 277 if mode == invalid { 278 return // omit 279 } 280 assert(typ != nil) 281 if mode == constant_ { 282 assert(val != nil) 283 assert(typ == Typ[Invalid] || isConstType(typ)) 284 } 285 if m := check.Types; m != nil { 286 m[x] = TypeAndValue{mode, typ, val} 287 } 288 } 289 290 func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) { 291 // f must be a (possibly parenthesized) identifier denoting a built-in 292 // (built-ins in package unsafe always produce a constant result and 293 // we don't record their signatures, so we don't see qualified idents 294 // here): record the signature for f and possible children. 295 for { 296 check.recordTypeAndValue(f, builtin, sig, nil) 297 switch p := f.(type) { 298 case *ast.Ident: 299 return // we're done 300 case *ast.ParenExpr: 301 f = p.X 302 default: 303 unreachable() 304 } 305 } 306 } 307 308 func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) { 309 assert(x != nil) 310 if a[0] == nil || a[1] == nil { 311 return 312 } 313 assert(isTyped(a[0]) && isTyped(a[1]) && isBoolean(a[1])) 314 if m := check.Types; m != nil { 315 for { 316 tv := m[x] 317 assert(tv.Type != nil) // should have been recorded already 318 pos := x.Pos() 319 tv.Type = NewTuple( 320 NewVar(pos, check.pkg, "", a[0]), 321 NewVar(pos, check.pkg, "", a[1]), 322 ) 323 m[x] = tv 324 // if x is a parenthesized expression (p.X), update p.X 325 p, _ := x.(*ast.ParenExpr) 326 if p == nil { 327 break 328 } 329 x = p.X 330 } 331 } 332 } 333 334 func (check *Checker) recordDef(id *ast.Ident, obj Object) { 335 assert(id != nil) 336 if m := check.Defs; m != nil { 337 m[id] = obj 338 } 339 } 340 341 func (check *Checker) recordUse(id *ast.Ident, obj Object) { 342 assert(id != nil) 343 assert(obj != nil) 344 if m := check.Uses; m != nil { 345 m[id] = obj 346 } 347 } 348 349 func (check *Checker) recordImplicit(node ast.Node, obj Object) { 350 assert(node != nil) 351 assert(obj != nil) 352 if m := check.Implicits; m != nil { 353 m[node] = obj 354 } 355 } 356 357 func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) { 358 assert(obj != nil && (recv == nil || len(index) > 0)) 359 check.recordUse(x.Sel, obj) 360 if m := check.Selections; m != nil { 361 m[x] = &Selection{kind, recv, obj, index, indirect} 362 } 363 } 364 365 func (check *Checker) recordScope(node ast.Node, scope *Scope) { 366 assert(node != nil) 367 assert(scope != nil) 368 if m := check.Scopes; m != nil { 369 m[node] = scope 370 } 371 }