github.com/jhump/golang-x-tools@v0.0.0-20220218190644-4958d6d39439/go/types/objectpath/objectpath.go (about) 1 // Copyright 2018 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 objectpath defines a naming scheme for types.Objects 6 // (that is, named entities in Go programs) relative to their enclosing 7 // package. 8 // 9 // Type-checker objects are canonical, so they are usually identified by 10 // their address in memory (a pointer), but a pointer has meaning only 11 // within one address space. By contrast, objectpath names allow the 12 // identity of an object to be sent from one program to another, 13 // establishing a correspondence between types.Object variables that are 14 // distinct but logically equivalent. 15 // 16 // A single object may have multiple paths. In this example, 17 // type A struct{ X int } 18 // type B A 19 // the field X has two paths due to its membership of both A and B. 20 // The For(obj) function always returns one of these paths, arbitrarily 21 // but consistently. 22 package objectpath 23 24 import ( 25 "fmt" 26 "go/types" 27 "sort" 28 "strconv" 29 "strings" 30 31 "github.com/jhump/golang-x-tools/internal/typeparams" 32 ) 33 34 // A Path is an opaque name that identifies a types.Object 35 // relative to its package. Conceptually, the name consists of a 36 // sequence of destructuring operations applied to the package scope 37 // to obtain the original object. 38 // The name does not include the package itself. 39 type Path string 40 41 // Encoding 42 // 43 // An object path is a textual and (with training) human-readable encoding 44 // of a sequence of destructuring operators, starting from a types.Package. 45 // The sequences represent a path through the package/object/type graph. 46 // We classify these operators by their type: 47 // 48 // PO package->object Package.Scope.Lookup 49 // OT object->type Object.Type 50 // TT type->type Type.{Elem,Key,Params,Results,Underlying} [EKPRU] 51 // TO type->object Type.{At,Field,Method,Obj} [AFMO] 52 // 53 // All valid paths start with a package and end at an object 54 // and thus may be defined by the regular language: 55 // 56 // objectpath = PO (OT TT* TO)* 57 // 58 // The concrete encoding follows directly: 59 // - The only PO operator is Package.Scope.Lookup, which requires an identifier. 60 // - The only OT operator is Object.Type, 61 // which we encode as '.' because dot cannot appear in an identifier. 62 // - The TT operators are encoded as [EKPRUTC]; 63 // one of these (TypeParam) requires an integer operand, 64 // which is encoded as a string of decimal digits. 65 // - The TO operators are encoded as [AFMO]; 66 // three of these (At,Field,Method) require an integer operand, 67 // which is encoded as a string of decimal digits. 68 // These indices are stable across different representations 69 // of the same package, even source and export data. 70 // The indices used are implementation specific and may not correspond to 71 // the argument to the go/types function. 72 // 73 // In the example below, 74 // 75 // package p 76 // 77 // type T interface { 78 // f() (a string, b struct{ X int }) 79 // } 80 // 81 // field X has the path "T.UM0.RA1.F0", 82 // representing the following sequence of operations: 83 // 84 // p.Lookup("T") T 85 // .Type().Underlying().Method(0). f 86 // .Type().Results().At(1) b 87 // .Type().Field(0) X 88 // 89 // The encoding is not maximally compact---every R or P is 90 // followed by an A, for example---but this simplifies the 91 // encoder and decoder. 92 // 93 const ( 94 // object->type operators 95 opType = '.' // .Type() (Object) 96 97 // type->type operators 98 opElem = 'E' // .Elem() (Pointer, Slice, Array, Chan, Map) 99 opKey = 'K' // .Key() (Map) 100 opParams = 'P' // .Params() (Signature) 101 opResults = 'R' // .Results() (Signature) 102 opUnderlying = 'U' // .Underlying() (Named) 103 opTypeParam = 'T' // .TypeParams.At(i) (Named, Signature) 104 opConstraint = 'C' // .Constraint() (TypeParam) 105 106 // type->object operators 107 opAt = 'A' // .At(i) (Tuple) 108 opField = 'F' // .Field(i) (Struct) 109 opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored) 110 opObj = 'O' // .Obj() (Named, TypeParam) 111 ) 112 113 // The For function returns the path to an object relative to its package, 114 // or an error if the object is not accessible from the package's Scope. 115 // 116 // The For function guarantees to return a path only for the following objects: 117 // - package-level types 118 // - exported package-level non-types 119 // - methods 120 // - parameter and result variables 121 // - struct fields 122 // These objects are sufficient to define the API of their package. 123 // The objects described by a package's export data are drawn from this set. 124 // 125 // For does not return a path for predeclared names, imported package 126 // names, local names, and unexported package-level names (except 127 // types). 128 // 129 // Example: given this definition, 130 // 131 // package p 132 // 133 // type T interface { 134 // f() (a string, b struct{ X int }) 135 // } 136 // 137 // For(X) would return a path that denotes the following sequence of operations: 138 // 139 // p.Scope().Lookup("T") (TypeName T) 140 // .Type().Underlying().Method(0). (method Func f) 141 // .Type().Results().At(1) (field Var b) 142 // .Type().Field(0) (field Var X) 143 // 144 // where p is the package (*types.Package) to which X belongs. 145 func For(obj types.Object) (Path, error) { 146 pkg := obj.Pkg() 147 148 // This table lists the cases of interest. 149 // 150 // Object Action 151 // ------ ------ 152 // nil reject 153 // builtin reject 154 // pkgname reject 155 // label reject 156 // var 157 // package-level accept 158 // func param/result accept 159 // local reject 160 // struct field accept 161 // const 162 // package-level accept 163 // local reject 164 // func 165 // package-level accept 166 // init functions reject 167 // concrete method accept 168 // interface method accept 169 // type 170 // package-level accept 171 // local reject 172 // 173 // The only accessible package-level objects are members of pkg itself. 174 // 175 // The cases are handled in four steps: 176 // 177 // 1. reject nil and builtin 178 // 2. accept package-level objects 179 // 3. reject obviously invalid objects 180 // 4. search the API for the path to the param/result/field/method. 181 182 // 1. reference to nil or builtin? 183 if pkg == nil { 184 return "", fmt.Errorf("predeclared %s has no path", obj) 185 } 186 scope := pkg.Scope() 187 188 // 2. package-level object? 189 if scope.Lookup(obj.Name()) == obj { 190 // Only exported objects (and non-exported types) have a path. 191 // Non-exported types may be referenced by other objects. 192 if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() { 193 return "", fmt.Errorf("no path for non-exported %v", obj) 194 } 195 return Path(obj.Name()), nil 196 } 197 198 // 3. Not a package-level object. 199 // Reject obviously non-viable cases. 200 switch obj := obj.(type) { 201 case *types.TypeName: 202 if _, ok := obj.Type().(*typeparams.TypeParam); !ok { 203 // With the exception of type parameters, only package-level type names 204 // have a path. 205 return "", fmt.Errorf("no path for %v", obj) 206 } 207 case *types.Const, // Only package-level constants have a path. 208 *types.Label, // Labels are function-local. 209 *types.PkgName: // PkgNames are file-local. 210 return "", fmt.Errorf("no path for %v", obj) 211 212 case *types.Var: 213 // Could be: 214 // - a field (obj.IsField()) 215 // - a func parameter or result 216 // - a local var. 217 // Sadly there is no way to distinguish 218 // a param/result from a local 219 // so we must proceed to the find. 220 221 case *types.Func: 222 // A func, if not package-level, must be a method. 223 if recv := obj.Type().(*types.Signature).Recv(); recv == nil { 224 return "", fmt.Errorf("func is not a method: %v", obj) 225 } 226 // TODO(adonovan): opt: if the method is concrete, 227 // do a specialized version of the rest of this function so 228 // that it's O(1) not O(|scope|). Basically 'find' is needed 229 // only for struct fields and interface methods. 230 231 default: 232 panic(obj) 233 } 234 235 // 4. Search the API for the path to the var (field/param/result) or method. 236 237 // First inspect package-level named types. 238 // In the presence of path aliases, these give 239 // the best paths because non-types may 240 // refer to types, but not the reverse. 241 empty := make([]byte, 0, 48) // initial space 242 names := scope.Names() 243 for _, name := range names { 244 o := scope.Lookup(name) 245 tname, ok := o.(*types.TypeName) 246 if !ok { 247 continue // handle non-types in second pass 248 } 249 250 path := append(empty, name...) 251 path = append(path, opType) 252 253 T := o.Type() 254 255 if tname.IsAlias() { 256 // type alias 257 if r := find(obj, T, path); r != nil { 258 return Path(r), nil 259 } 260 } else { 261 if named, _ := T.(*types.Named); named != nil { 262 if r := findTypeParam(obj, typeparams.ForNamed(named), path); r != nil { 263 // generic named type 264 return Path(r), nil 265 } 266 } 267 // defined (named) type 268 if r := find(obj, T.Underlying(), append(path, opUnderlying)); r != nil { 269 return Path(r), nil 270 } 271 } 272 } 273 274 // Then inspect everything else: 275 // non-types, and declared methods of defined types. 276 for _, name := range names { 277 o := scope.Lookup(name) 278 path := append(empty, name...) 279 if _, ok := o.(*types.TypeName); !ok { 280 if o.Exported() { 281 // exported non-type (const, var, func) 282 if r := find(obj, o.Type(), append(path, opType)); r != nil { 283 return Path(r), nil 284 } 285 } 286 continue 287 } 288 289 // Inspect declared methods of defined types. 290 if T, ok := o.Type().(*types.Named); ok { 291 path = append(path, opType) 292 // Note that method index here is always with respect 293 // to canonical ordering of methods, regardless of how 294 // they appear in the underlying type. 295 canonical := canonicalize(T) 296 for i := 0; i < len(canonical); i++ { 297 m := canonical[i] 298 path2 := appendOpArg(path, opMethod, i) 299 if m == obj { 300 return Path(path2), nil // found declared method 301 } 302 if r := find(obj, m.Type(), append(path2, opType)); r != nil { 303 return Path(r), nil 304 } 305 } 306 } 307 } 308 309 return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path()) 310 } 311 312 func appendOpArg(path []byte, op byte, arg int) []byte { 313 path = append(path, op) 314 path = strconv.AppendInt(path, int64(arg), 10) 315 return path 316 } 317 318 // find finds obj within type T, returning the path to it, or nil if not found. 319 func find(obj types.Object, T types.Type, path []byte) []byte { 320 switch T := T.(type) { 321 case *types.Basic, *types.Named: 322 // Named types belonging to pkg were handled already, 323 // so T must belong to another package. No path. 324 return nil 325 case *types.Pointer: 326 return find(obj, T.Elem(), append(path, opElem)) 327 case *types.Slice: 328 return find(obj, T.Elem(), append(path, opElem)) 329 case *types.Array: 330 return find(obj, T.Elem(), append(path, opElem)) 331 case *types.Chan: 332 return find(obj, T.Elem(), append(path, opElem)) 333 case *types.Map: 334 if r := find(obj, T.Key(), append(path, opKey)); r != nil { 335 return r 336 } 337 return find(obj, T.Elem(), append(path, opElem)) 338 case *types.Signature: 339 if r := findTypeParam(obj, typeparams.ForSignature(T), path); r != nil { 340 return r 341 } 342 if r := find(obj, T.Params(), append(path, opParams)); r != nil { 343 return r 344 } 345 return find(obj, T.Results(), append(path, opResults)) 346 case *types.Struct: 347 for i := 0; i < T.NumFields(); i++ { 348 f := T.Field(i) 349 path2 := appendOpArg(path, opField, i) 350 if f == obj { 351 return path2 // found field var 352 } 353 if r := find(obj, f.Type(), append(path2, opType)); r != nil { 354 return r 355 } 356 } 357 return nil 358 case *types.Tuple: 359 for i := 0; i < T.Len(); i++ { 360 v := T.At(i) 361 path2 := appendOpArg(path, opAt, i) 362 if v == obj { 363 return path2 // found param/result var 364 } 365 if r := find(obj, v.Type(), append(path2, opType)); r != nil { 366 return r 367 } 368 } 369 return nil 370 case *types.Interface: 371 for i := 0; i < T.NumMethods(); i++ { 372 m := T.Method(i) 373 path2 := appendOpArg(path, opMethod, i) 374 if m == obj { 375 return path2 // found interface method 376 } 377 if r := find(obj, m.Type(), append(path2, opType)); r != nil { 378 return r 379 } 380 } 381 return nil 382 case *typeparams.TypeParam: 383 name := T.Obj() 384 if name == obj { 385 return append(path, opObj) 386 } 387 if r := find(obj, T.Constraint(), append(path, opConstraint)); r != nil { 388 return r 389 } 390 return nil 391 } 392 panic(T) 393 } 394 395 func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte) []byte { 396 for i := 0; i < list.Len(); i++ { 397 tparam := list.At(i) 398 path2 := appendOpArg(path, opTypeParam, i) 399 if r := find(obj, tparam, path2); r != nil { 400 return r 401 } 402 } 403 return nil 404 } 405 406 // Object returns the object denoted by path p within the package pkg. 407 func Object(pkg *types.Package, p Path) (types.Object, error) { 408 if p == "" { 409 return nil, fmt.Errorf("empty path") 410 } 411 412 pathstr := string(p) 413 var pkgobj, suffix string 414 if dot := strings.IndexByte(pathstr, opType); dot < 0 { 415 pkgobj = pathstr 416 } else { 417 pkgobj = pathstr[:dot] 418 suffix = pathstr[dot:] // suffix starts with "." 419 } 420 421 obj := pkg.Scope().Lookup(pkgobj) 422 if obj == nil { 423 return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj) 424 } 425 426 // abstraction of *types.{Pointer,Slice,Array,Chan,Map} 427 type hasElem interface { 428 Elem() types.Type 429 } 430 // abstraction of *types.{Named,Signature} 431 type hasTypeParams interface { 432 TypeParams() *typeparams.TypeParamList 433 } 434 // abstraction of *types.{Named,TypeParam} 435 type hasObj interface { 436 Obj() *types.TypeName 437 } 438 439 // The loop state is the pair (t, obj), 440 // exactly one of which is non-nil, initially obj. 441 // All suffixes start with '.' (the only object->type operation), 442 // followed by optional type->type operations, 443 // then a type->object operation. 444 // The cycle then repeats. 445 var t types.Type 446 for suffix != "" { 447 code := suffix[0] 448 suffix = suffix[1:] 449 450 // Codes [AFM] have an integer operand. 451 var index int 452 switch code { 453 case opAt, opField, opMethod, opTypeParam: 454 rest := strings.TrimLeft(suffix, "0123456789") 455 numerals := suffix[:len(suffix)-len(rest)] 456 suffix = rest 457 i, err := strconv.Atoi(numerals) 458 if err != nil { 459 return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code) 460 } 461 index = int(i) 462 case opObj: 463 // no operand 464 default: 465 // The suffix must end with a type->object operation. 466 if suffix == "" { 467 return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code) 468 } 469 } 470 471 if code == opType { 472 if t != nil { 473 return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType) 474 } 475 t = obj.Type() 476 obj = nil 477 continue 478 } 479 480 if t == nil { 481 return nil, fmt.Errorf("invalid path: code %q in object context", code) 482 } 483 484 // Inv: t != nil, obj == nil 485 486 switch code { 487 case opElem: 488 hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map 489 if !ok { 490 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t) 491 } 492 t = hasElem.Elem() 493 494 case opKey: 495 mapType, ok := t.(*types.Map) 496 if !ok { 497 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t) 498 } 499 t = mapType.Key() 500 501 case opParams: 502 sig, ok := t.(*types.Signature) 503 if !ok { 504 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) 505 } 506 t = sig.Params() 507 508 case opResults: 509 sig, ok := t.(*types.Signature) 510 if !ok { 511 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) 512 } 513 t = sig.Results() 514 515 case opUnderlying: 516 named, ok := t.(*types.Named) 517 if !ok { 518 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t) 519 } 520 t = named.Underlying() 521 522 case opTypeParam: 523 hasTypeParams, ok := t.(hasTypeParams) // Named, Signature 524 if !ok { 525 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t) 526 } 527 tparams := hasTypeParams.TypeParams() 528 if n := tparams.Len(); index >= n { 529 return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) 530 } 531 t = tparams.At(index) 532 533 case opConstraint: 534 tparam, ok := t.(*typeparams.TypeParam) 535 if !ok { 536 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t) 537 } 538 t = tparam.Constraint() 539 540 case opAt: 541 tuple, ok := t.(*types.Tuple) 542 if !ok { 543 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want tuple)", code, t, t) 544 } 545 if n := tuple.Len(); index >= n { 546 return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) 547 } 548 obj = tuple.At(index) 549 t = nil 550 551 case opField: 552 structType, ok := t.(*types.Struct) 553 if !ok { 554 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t) 555 } 556 if n := structType.NumFields(); index >= n { 557 return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n) 558 } 559 obj = structType.Field(index) 560 t = nil 561 562 case opMethod: 563 hasMethods, ok := t.(hasMethods) // Interface or Named 564 if !ok { 565 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t) 566 } 567 canonical := canonicalize(hasMethods) 568 if n := len(canonical); index >= n { 569 return nil, fmt.Errorf("method index %d out of range [0-%d)", index, n) 570 } 571 obj = canonical[index] 572 t = nil 573 574 case opObj: 575 hasObj, ok := t.(hasObj) 576 if !ok { 577 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t) 578 } 579 obj = hasObj.Obj() 580 t = nil 581 582 default: 583 return nil, fmt.Errorf("invalid path: unknown code %q", code) 584 } 585 } 586 587 if obj.Pkg() != pkg { 588 return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj) 589 } 590 591 return obj, nil // success 592 } 593 594 // hasMethods is an abstraction of *types.{Interface,Named}. This is pulled up 595 // because it is used by methodOrdering, which is in turn used by both encoding 596 // and decoding. 597 type hasMethods interface { 598 Method(int) *types.Func 599 NumMethods() int 600 } 601 602 // canonicalize returns a canonical order for the methods in a hasMethod. 603 func canonicalize(hm hasMethods) []*types.Func { 604 count := hm.NumMethods() 605 if count <= 0 { 606 return nil 607 } 608 canon := make([]*types.Func, count) 609 for i := 0; i < count; i++ { 610 canon[i] = hm.Method(i) 611 } 612 less := func(i, j int) bool { 613 return canon[i].Id() < canon[j].Id() 614 } 615 sort.Slice(canon, less) 616 return canon 617 }