|
| 1 | +// Copyright 2012 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 | +// Package types declares the data types and implements |
| 6 | +// the algorithms for type-checking of Go packages. Use |
| 7 | +// Config.Check to invoke the type checker for a package. |
| 8 | +// Alternatively, create a new type checker with NewChecker |
| 9 | +// and invoke it incrementally by calling Checker.Files. |
| 10 | +// |
| 11 | +// Type-checking consists of several interdependent phases: |
| 12 | +// |
| 13 | +// Name resolution maps each identifier (ast.Ident) in the program to the |
| 14 | +// language object (Object) it denotes. |
| 15 | +// Use Info.{Defs,Uses,Implicits} for the results of name resolution. |
| 16 | +// |
| 17 | +// Constant folding computes the exact constant value (constant.Value) |
| 18 | +// for every expression (ast.Expr) that is a compile-time constant. |
| 19 | +// Use Info.Types[expr].Value for the results of constant folding. |
| 20 | +// |
| 21 | +// Type inference computes the type (Type) of every expression (ast.Expr) |
| 22 | +// and checks for compliance with the language specification. |
| 23 | +// Use Info.Types[expr].Type for the results of type inference. |
| 24 | +// |
| 25 | +// For a tutorial, see https://golang.org/s/types-tutorial. |
| 26 | +// |
| 27 | +package types |
| 28 | + |
| 29 | +import ( |
| 30 | + "bytes" |
| 31 | + "fmt" |
| 32 | + "go/ast" |
| 33 | + "go/constant" |
| 34 | + "go/token" |
| 35 | +) |
| 36 | + |
| 37 | +// An Error describes a type-checking error; it implements the error interface. |
| 38 | +// A "soft" error is an error that still permits a valid interpretation of a |
| 39 | +// package (such as "unused variable"); "hard" errors may lead to unpredictable |
| 40 | +// behavior if ignored. |
| 41 | +type Error struct { |
| 42 | + Fset *token.FileSet // file set for interpretation of Pos |
| 43 | + Pos token.Pos // error position |
| 44 | + Msg string // error message |
| 45 | + Soft bool // if set, error is "soft" |
| 46 | +} |
| 47 | + |
| 48 | +// Error returns an error string formatted as follows: |
| 49 | +// filename:line:column: message |
| 50 | +func (err Error) Error() string { |
| 51 | + return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg) |
| 52 | +} |
| 53 | + |
| 54 | +// An Importer resolves import paths to Packages. |
| 55 | +// |
| 56 | +// CAUTION: This interface does not support the import of locally |
| 57 | +// vendored packages. See https://golang.org/s/go15vendor. |
| 58 | +// If possible, external implementations should implement ImporterFrom. |
| 59 | +type Importer interface { |
| 60 | + // Import returns the imported package for the given import path. |
| 61 | + // The semantics is like for ImporterFrom.ImportFrom except that |
| 62 | + // dir and mode are ignored (since they are not present). |
| 63 | + Import(path string) (*Package, error) |
| 64 | +} |
| 65 | + |
| 66 | +// ImportMode is reserved for future use. |
| 67 | +type ImportMode int |
| 68 | + |
| 69 | +// An ImporterFrom resolves import paths to packages; it |
| 70 | +// supports vendoring per https://golang.org/s/go15vendor. |
| 71 | +// Use go/importer to obtain an ImporterFrom implementation. |
| 72 | +type ImporterFrom interface { |
| 73 | + // Importer is present for backward-compatibility. Calling |
| 74 | + // Import(path) is the same as calling ImportFrom(path, "", 0); |
| 75 | + // i.e., locally vendored packages may not be found. |
| 76 | + // The types package does not call Import if an ImporterFrom |
| 77 | + // is present. |
| 78 | + Importer |
| 79 | + |
| 80 | + // ImportFrom returns the imported package for the given import |
| 81 | + // path when imported by a package file located in dir. |
| 82 | + // If the import failed, besides returning an error, ImportFrom |
| 83 | + // is encouraged to cache and return a package anyway, if one |
| 84 | + // was created. This will reduce package inconsistencies and |
| 85 | + // follow-on type checker errors due to the missing package. |
| 86 | + // The mode value must be 0; it is reserved for future use. |
| 87 | + // Two calls to ImportFrom with the same path and dir must |
| 88 | + // return the same package. |
| 89 | + ImportFrom(path, dir string, mode ImportMode) (*Package, error) |
| 90 | +} |
| 91 | + |
| 92 | +// A Config specifies the configuration for type checking. |
| 93 | +// The zero value for Config is a ready-to-use default configuration. |
| 94 | +type Config struct { |
| 95 | + // If IgnoreFuncBodies is set, function bodies are not |
| 96 | + // type-checked. |
| 97 | + IgnoreFuncBodies bool |
| 98 | + |
| 99 | + // If FakeImportC is set, `import "C"` (for packages requiring Cgo) |
| 100 | + // declares an empty "C" package and errors are omitted for qualified |
| 101 | + // identifiers referring to package C (which won't find an object). |
| 102 | + // This feature is intended for the standard library cmd/api tool. |
| 103 | + // |
| 104 | + // Caution: Effects may be unpredictable due to follow-on errors. |
| 105 | + // Do not use casually! |
| 106 | + FakeImportC bool |
| 107 | + |
| 108 | + // If Error != nil, it is called with each error found |
| 109 | + // during type checking; err has dynamic type Error. |
| 110 | + // Secondary errors (for instance, to enumerate all types |
| 111 | + // involved in an invalid recursive type declaration) have |
| 112 | + // error strings that start with a '\t' character. |
| 113 | + // If Error == nil, type-checking stops with the first |
| 114 | + // error found. |
| 115 | + Error func(err error) |
| 116 | + |
| 117 | + // An importer is used to import packages referred to from |
| 118 | + // import declarations. |
| 119 | + // If the installed importer implements ImporterFrom, the type |
| 120 | + // checker calls ImportFrom instead of Import. |
| 121 | + // The type checker reports an error if an importer is needed |
| 122 | + // but none was installed. |
| 123 | + Importer Importer |
| 124 | + |
| 125 | + // If Sizes != nil, it provides the sizing functions for package unsafe. |
| 126 | + // Otherwise SizesFor("gc", "amd64") is used instead. |
| 127 | + Sizes Sizes |
| 128 | + |
| 129 | + // If DisableUnusedImportCheck is set, packages are not checked |
| 130 | + // for unused imports. |
| 131 | + DisableUnusedImportCheck bool |
| 132 | +} |
| 133 | + |
| 134 | +// Info holds result type information for a type-checked package. |
| 135 | +// Only the information for which a map is provided is collected. |
| 136 | +// If the package has type errors, the collected information may |
| 137 | +// be incomplete. |
| 138 | +type Info struct { |
| 139 | + // Types maps expressions to their types, and for constant |
| 140 | + // expressions, also their values. Invalid expressions are |
| 141 | + // omitted. |
| 142 | + // |
| 143 | + // For (possibly parenthesized) identifiers denoting built-in |
| 144 | + // functions, the recorded signatures are call-site specific: |
| 145 | + // if the call result is not a constant, the recorded type is |
| 146 | + // an argument-specific signature. Otherwise, the recorded type |
| 147 | + // is invalid. |
| 148 | + // |
| 149 | + // The Types map does not record the type of every identifier, |
| 150 | + // only those that appear where an arbitrary expression is |
| 151 | + // permitted. For instance, the identifier f in a selector |
| 152 | + // expression x.f is found only in the Selections map, the |
| 153 | + // identifier z in a variable declaration 'var z int' is found |
| 154 | + // only in the Defs map, and identifiers denoting packages in |
| 155 | + // qualified identifiers are collected in the Uses map. |
| 156 | + Types map[ast.Expr]TypeAndValue |
| 157 | + |
| 158 | + // Defs maps identifiers to the objects they define (including |
| 159 | + // package names, dots "." of dot-imports, and blank "_" identifiers). |
| 160 | + // For identifiers that do not denote objects (e.g., the package name |
| 161 | + // in package clauses, or symbolic variables t in t := x.(type) of |
| 162 | + // type switch headers), the corresponding objects are nil. |
| 163 | + // |
| 164 | + // For an anonymous field, Defs returns the field *Var it defines. |
| 165 | + // |
| 166 | + // Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos() |
| 167 | + Defs map[*ast.Ident]Object |
| 168 | + |
| 169 | + // Uses maps identifiers to the objects they denote. |
| 170 | + // |
| 171 | + // For an anonymous field, Uses returns the *TypeName it denotes. |
| 172 | + // |
| 173 | + // Invariant: Uses[id].Pos() != id.Pos() |
| 174 | + Uses map[*ast.Ident]Object |
| 175 | + |
| 176 | + // Implicits maps nodes to their implicitly declared objects, if any. |
| 177 | + // The following node and object types may appear: |
| 178 | + // |
| 179 | + // node declared object |
| 180 | + // |
| 181 | + // *ast.ImportSpec *PkgName for imports without renames |
| 182 | + // *ast.CaseClause type-specific *Var for each type switch case clause (incl. default) |
| 183 | + // *ast.Field anonymous parameter *Var |
| 184 | + // |
| 185 | + Implicits map[ast.Node]Object |
| 186 | + |
| 187 | + // Selections maps selector expressions (excluding qualified identifiers) |
| 188 | + // to their corresponding selections. |
| 189 | + Selections map[*ast.SelectorExpr]*Selection |
| 190 | + |
| 191 | + // Scopes maps ast.Nodes to the scopes they define. Package scopes are not |
| 192 | + // associated with a specific node but with all files belonging to a package. |
| 193 | + // Thus, the package scope can be found in the type-checked Package object. |
| 194 | + // Scopes nest, with the Universe scope being the outermost scope, enclosing |
| 195 | + // the package scope, which contains (one or more) files scopes, which enclose |
| 196 | + // function scopes which in turn enclose statement and function literal scopes. |
| 197 | + // Note that even though package-level functions are declared in the package |
| 198 | + // scope, the function scopes are embedded in the file scope of the file |
| 199 | + // containing the function declaration. |
| 200 | + // |
| 201 | + // The following node types may appear in Scopes: |
| 202 | + // |
| 203 | + // *ast.File |
| 204 | + // *ast.FuncType |
| 205 | + // *ast.BlockStmt |
| 206 | + // *ast.IfStmt |
| 207 | + // *ast.SwitchStmt |
| 208 | + // *ast.TypeSwitchStmt |
| 209 | + // *ast.CaseClause |
| 210 | + // *ast.CommClause |
| 211 | + // *ast.ForStmt |
| 212 | + // *ast.RangeStmt |
| 213 | + // |
| 214 | + Scopes map[ast.Node]*Scope |
| 215 | + |
| 216 | + // InitOrder is the list of package-level initializers in the order in which |
| 217 | + // they must be executed. Initializers referring to variables related by an |
| 218 | + // initialization dependency appear in topological order, the others appear |
| 219 | + // in source order. Variables without an initialization expression do not |
| 220 | + // appear in this list. |
| 221 | + InitOrder []*Initializer |
| 222 | +} |
| 223 | + |
| 224 | +// TypeOf returns the type of expression e, or nil if not found. |
| 225 | +// Precondition: the Types, Uses and Defs maps are populated. |
| 226 | +// |
| 227 | +func (info *Info) TypeOf(e ast.Expr) Type { |
| 228 | + if t, ok := info.Types[e]; ok { |
| 229 | + return t.Type |
| 230 | + } |
| 231 | + if id, _ := e.(*ast.Ident); id != nil { |
| 232 | + if obj := info.ObjectOf(id); obj != nil { |
| 233 | + return obj.Type() |
| 234 | + } |
| 235 | + } |
| 236 | + return nil |
| 237 | +} |
| 238 | + |
| 239 | +// ObjectOf returns the object denoted by the specified id, |
| 240 | +// or nil if not found. |
| 241 | +// |
| 242 | +// If id is an anonymous struct field, ObjectOf returns the field (*Var) |
| 243 | +// it uses, not the type (*TypeName) it defines. |
| 244 | +// |
| 245 | +// Precondition: the Uses and Defs maps are populated. |
| 246 | +// |
| 247 | +func (info *Info) ObjectOf(id *ast.Ident) Object { |
| 248 | + if obj := info.Defs[id]; obj != nil { |
| 249 | + return obj |
| 250 | + } |
| 251 | + return info.Uses[id] |
| 252 | +} |
| 253 | + |
| 254 | +// TypeAndValue reports the type and value (for constants) |
| 255 | +// of the corresponding expression. |
| 256 | +type TypeAndValue struct { |
| 257 | + mode operandMode |
| 258 | + Type Type |
| 259 | + Value constant.Value |
| 260 | +} |
| 261 | + |
| 262 | +// TODO(gri) Consider eliminating the IsVoid predicate. Instead, report |
| 263 | +// "void" values as regular values but with the empty tuple type. |
| 264 | + |
| 265 | +// IsVoid reports whether the corresponding expression |
| 266 | +// is a function call without results. |
| 267 | +func (tv TypeAndValue) IsVoid() bool { |
| 268 | + return tv.mode == novalue |
| 269 | +} |
| 270 | + |
| 271 | +// IsType reports whether the corresponding expression specifies a type. |
| 272 | +func (tv TypeAndValue) IsType() bool { |
| 273 | + return tv.mode == typexpr |
| 274 | +} |
| 275 | + |
| 276 | +// IsBuiltin reports whether the corresponding expression denotes |
| 277 | +// a (possibly parenthesized) built-in function. |
| 278 | +func (tv TypeAndValue) IsBuiltin() bool { |
| 279 | + return tv.mode == builtin |
| 280 | +} |
| 281 | + |
| 282 | +// IsValue reports whether the corresponding expression is a value. |
| 283 | +// Builtins are not considered values. Constant values have a non- |
| 284 | +// nil Value. |
| 285 | +func (tv TypeAndValue) IsValue() bool { |
| 286 | + switch tv.mode { |
| 287 | + case constant_, variable, mapindex, value, commaok: |
| 288 | + return true |
| 289 | + } |
| 290 | + return false |
| 291 | +} |
| 292 | + |
| 293 | +// IsNil reports whether the corresponding expression denotes the |
| 294 | +// predeclared value nil. |
| 295 | +func (tv TypeAndValue) IsNil() bool { |
| 296 | + return tv.mode == value && tv.Type == Typ[UntypedNil] |
| 297 | +} |
| 298 | + |
| 299 | +// Addressable reports whether the corresponding expression |
| 300 | +// is addressable (https://golang.org/ref/spec#Address_operators). |
| 301 | +func (tv TypeAndValue) Addressable() bool { |
| 302 | + return tv.mode == variable |
| 303 | +} |
| 304 | + |
| 305 | +// Assignable reports whether the corresponding expression |
| 306 | +// is assignable to (provided a value of the right type). |
| 307 | +func (tv TypeAndValue) Assignable() bool { |
| 308 | + return tv.mode == variable || tv.mode == mapindex |
| 309 | +} |
| 310 | + |
| 311 | +// HasOk reports whether the corresponding expression may be |
| 312 | +// used on the rhs of a comma-ok assignment. |
| 313 | +func (tv TypeAndValue) HasOk() bool { |
| 314 | + return tv.mode == commaok || tv.mode == mapindex |
| 315 | +} |
| 316 | + |
| 317 | +// An Initializer describes a package-level variable, or a list of variables in case |
| 318 | +// of a multi-valued initialization expression, and the corresponding initialization |
| 319 | +// expression. |
| 320 | +type Initializer struct { |
| 321 | + Lhs []*Var // var Lhs = Rhs |
| 322 | + Rhs ast.Expr |
| 323 | +} |
| 324 | + |
| 325 | +func (init *Initializer) String() string { |
| 326 | + var buf bytes.Buffer |
| 327 | + for i, lhs := range init.Lhs { |
| 328 | + if i > 0 { |
| 329 | + buf.WriteString(", ") |
| 330 | + } |
| 331 | + buf.WriteString(lhs.Name()) |
| 332 | + } |
| 333 | + buf.WriteString(" = ") |
| 334 | + WriteExpr(&buf, init.Rhs) |
| 335 | + return buf.String() |
| 336 | +} |
| 337 | + |
| 338 | +// Check type-checks a package and returns the resulting package object and |
| 339 | +// the first error if any. Additionally, if info != nil, Check populates each |
| 340 | +// of the non-nil maps in the Info struct. |
| 341 | +// |
| 342 | +// The package is marked as complete if no errors occurred, otherwise it is |
| 343 | +// incomplete. See Config.Error for controlling behavior in the presence of |
| 344 | +// errors. |
| 345 | +// |
| 346 | +// The package is specified by a list of *ast.Files and corresponding |
| 347 | +// file set, and the package path the package is identified with. |
| 348 | +// The clean path must not be empty or dot ("."). |
| 349 | +func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) { |
| 350 | + pkg := NewPackage(path, "") |
| 351 | + return pkg, NewChecker(conf, fset, pkg, info).Files(files) |
| 352 | +} |
| 353 | + |
| 354 | +// AssertableTo reports whether a value of type V can be asserted to have type T. |
| 355 | +func AssertableTo(V *Interface, T Type) bool { |
| 356 | + m, _ := assertableTo(V, T) |
| 357 | + return m == nil |
| 358 | +} |
| 359 | + |
| 360 | +// AssignableTo reports whether a value of type V is assignable to a variable of type T. |
| 361 | +func AssignableTo(V, T Type) bool { |
| 362 | + x := operand{mode: value, typ: V} |
| 363 | + return x.assignableTo(nil, T, nil) // config not needed for non-constant x |
| 364 | +} |
| 365 | + |
| 366 | +// ConvertibleTo reports whether a value of type V is convertible to a value of type T. |
| 367 | +func ConvertibleTo(V, T Type) bool { |
| 368 | + x := operand{mode: value, typ: V} |
| 369 | + return x.convertibleTo(nil, T) // config not needed for non-constant x |
| 370 | +} |
| 371 | + |
| 372 | +// Implements reports whether type V implements interface T. |
| 373 | +func Implements(V Type, T *Interface) bool { |
| 374 | + f, _ := MissingMethod(V, T, true) |
| 375 | + return f == nil |
| 376 | +} |
0 commit comments