github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/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/powerman/golang-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, nil); 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, nil); 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), nil); 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), nil); 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), nil); 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 // 320 // The seen map is used to short circuit cycles through type parameters. If 321 // nil, it will be allocated as necessary. 322 func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte { 323 switch T := T.(type) { 324 case *types.Basic, *types.Named: 325 // Named types belonging to pkg were handled already, 326 // so T must belong to another package. No path. 327 return nil 328 case *types.Pointer: 329 return find(obj, T.Elem(), append(path, opElem), seen) 330 case *types.Slice: 331 return find(obj, T.Elem(), append(path, opElem), seen) 332 case *types.Array: 333 return find(obj, T.Elem(), append(path, opElem), seen) 334 case *types.Chan: 335 return find(obj, T.Elem(), append(path, opElem), seen) 336 case *types.Map: 337 if r := find(obj, T.Key(), append(path, opKey), seen); r != nil { 338 return r 339 } 340 return find(obj, T.Elem(), append(path, opElem), seen) 341 case *types.Signature: 342 if r := findTypeParam(obj, typeparams.ForSignature(T), path, seen); r != nil { 343 return r 344 } 345 if r := find(obj, T.Params(), append(path, opParams), seen); r != nil { 346 return r 347 } 348 return find(obj, T.Results(), append(path, opResults), seen) 349 case *types.Struct: 350 for i := 0; i < T.NumFields(); i++ { 351 fld := T.Field(i) 352 path2 := appendOpArg(path, opField, i) 353 if fld == obj { 354 return path2 // found field var 355 } 356 if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil { 357 return r 358 } 359 } 360 return nil 361 case *types.Tuple: 362 for i := 0; i < T.Len(); i++ { 363 v := T.At(i) 364 path2 := appendOpArg(path, opAt, i) 365 if v == obj { 366 return path2 // found param/result var 367 } 368 if r := find(obj, v.Type(), append(path2, opType), seen); r != nil { 369 return r 370 } 371 } 372 return nil 373 case *types.Interface: 374 for i := 0; i < T.NumMethods(); i++ { 375 m := T.Method(i) 376 path2 := appendOpArg(path, opMethod, i) 377 if m == obj { 378 return path2 // found interface method 379 } 380 if r := find(obj, m.Type(), append(path2, opType), seen); r != nil { 381 return r 382 } 383 } 384 return nil 385 case *typeparams.TypeParam: 386 name := T.Obj() 387 if name == obj { 388 return append(path, opObj) 389 } 390 if seen[name] { 391 return nil 392 } 393 if seen == nil { 394 seen = make(map[*types.TypeName]bool) 395 } 396 seen[name] = true 397 if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil { 398 return r 399 } 400 return nil 401 } 402 panic(T) 403 } 404 405 func findTypeParam(obj types.Object, list *typeparams.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte { 406 for i := 0; i < list.Len(); i++ { 407 tparam := list.At(i) 408 path2 := appendOpArg(path, opTypeParam, i) 409 if r := find(obj, tparam, path2, seen); r != nil { 410 return r 411 } 412 } 413 return nil 414 } 415 416 // Object returns the object denoted by path p within the package pkg. 417 func Object(pkg *types.Package, p Path) (types.Object, error) { 418 if p == "" { 419 return nil, fmt.Errorf("empty path") 420 } 421 422 pathstr := string(p) 423 var pkgobj, suffix string 424 if dot := strings.IndexByte(pathstr, opType); dot < 0 { 425 pkgobj = pathstr 426 } else { 427 pkgobj = pathstr[:dot] 428 suffix = pathstr[dot:] // suffix starts with "." 429 } 430 431 obj := pkg.Scope().Lookup(pkgobj) 432 if obj == nil { 433 return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj) 434 } 435 436 // abstraction of *types.{Pointer,Slice,Array,Chan,Map} 437 type hasElem interface { 438 Elem() types.Type 439 } 440 // abstraction of *types.{Named,Signature} 441 type hasTypeParams interface { 442 TypeParams() *typeparams.TypeParamList 443 } 444 // abstraction of *types.{Named,TypeParam} 445 type hasObj interface { 446 Obj() *types.TypeName 447 } 448 449 // The loop state is the pair (t, obj), 450 // exactly one of which is non-nil, initially obj. 451 // All suffixes start with '.' (the only object->type operation), 452 // followed by optional type->type operations, 453 // then a type->object operation. 454 // The cycle then repeats. 455 var t types.Type 456 for suffix != "" { 457 code := suffix[0] 458 suffix = suffix[1:] 459 460 // Codes [AFM] have an integer operand. 461 var index int 462 switch code { 463 case opAt, opField, opMethod, opTypeParam: 464 rest := strings.TrimLeft(suffix, "0123456789") 465 numerals := suffix[:len(suffix)-len(rest)] 466 suffix = rest 467 i, err := strconv.Atoi(numerals) 468 if err != nil { 469 return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code) 470 } 471 index = int(i) 472 case opObj: 473 // no operand 474 default: 475 // The suffix must end with a type->object operation. 476 if suffix == "" { 477 return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code) 478 } 479 } 480 481 if code == opType { 482 if t != nil { 483 return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType) 484 } 485 t = obj.Type() 486 obj = nil 487 continue 488 } 489 490 if t == nil { 491 return nil, fmt.Errorf("invalid path: code %q in object context", code) 492 } 493 494 // Inv: t != nil, obj == nil 495 496 switch code { 497 case opElem: 498 hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map 499 if !ok { 500 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t) 501 } 502 t = hasElem.Elem() 503 504 case opKey: 505 mapType, ok := t.(*types.Map) 506 if !ok { 507 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t) 508 } 509 t = mapType.Key() 510 511 case opParams: 512 sig, ok := t.(*types.Signature) 513 if !ok { 514 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) 515 } 516 t = sig.Params() 517 518 case opResults: 519 sig, ok := t.(*types.Signature) 520 if !ok { 521 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t) 522 } 523 t = sig.Results() 524 525 case opUnderlying: 526 named, ok := t.(*types.Named) 527 if !ok { 528 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t) 529 } 530 t = named.Underlying() 531 532 case opTypeParam: 533 hasTypeParams, ok := t.(hasTypeParams) // Named, Signature 534 if !ok { 535 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t) 536 } 537 tparams := hasTypeParams.TypeParams() 538 if n := tparams.Len(); index >= n { 539 return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) 540 } 541 t = tparams.At(index) 542 543 case opConstraint: 544 tparam, ok := t.(*typeparams.TypeParam) 545 if !ok { 546 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t) 547 } 548 t = tparam.Constraint() 549 550 case opAt: 551 tuple, ok := t.(*types.Tuple) 552 if !ok { 553 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want tuple)", code, t, t) 554 } 555 if n := tuple.Len(); index >= n { 556 return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n) 557 } 558 obj = tuple.At(index) 559 t = nil 560 561 case opField: 562 structType, ok := t.(*types.Struct) 563 if !ok { 564 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t) 565 } 566 if n := structType.NumFields(); index >= n { 567 return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n) 568 } 569 obj = structType.Field(index) 570 t = nil 571 572 case opMethod: 573 hasMethods, ok := t.(hasMethods) // Interface or Named 574 if !ok { 575 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t) 576 } 577 canonical := canonicalize(hasMethods) 578 if n := len(canonical); index >= n { 579 return nil, fmt.Errorf("method index %d out of range [0-%d)", index, n) 580 } 581 obj = canonical[index] 582 t = nil 583 584 case opObj: 585 hasObj, ok := t.(hasObj) 586 if !ok { 587 return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t) 588 } 589 obj = hasObj.Obj() 590 t = nil 591 592 default: 593 return nil, fmt.Errorf("invalid path: unknown code %q", code) 594 } 595 } 596 597 if obj.Pkg() != pkg { 598 return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj) 599 } 600 601 return obj, nil // success 602 } 603 604 // hasMethods is an abstraction of *types.{Interface,Named}. This is pulled up 605 // because it is used by methodOrdering, which is in turn used by both encoding 606 // and decoding. 607 type hasMethods interface { 608 Method(int) *types.Func 609 NumMethods() int 610 } 611 612 // canonicalize returns a canonical order for the methods in a hasMethod. 613 func canonicalize(hm hasMethods) []*types.Func { 614 count := hm.NumMethods() 615 if count <= 0 { 616 return nil 617 } 618 canon := make([]*types.Func, count) 619 for i := 0; i < count; i++ { 620 canon[i] = hm.Method(i) 621 } 622 less := func(i, j int) bool { 623 return canon[i].Id() < canon[j].Id() 624 } 625 sort.Slice(canon, less) 626 return canon 627 }