golang.org/x/tools@v0.21.0/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  //
    18  //	type A struct{ X int }
    19  //	type B A
    20  //
    21  // the field X has two paths due to its membership of both A and B.
    22  // The For(obj) function always returns one of these paths, arbitrarily
    23  // but consistently.
    24  package objectpath
    25  
    26  import (
    27  	"fmt"
    28  	"go/types"
    29  	"strconv"
    30  	"strings"
    31  
    32  	"golang.org/x/tools/internal/aliases"
    33  	"golang.org/x/tools/internal/typesinternal"
    34  )
    35  
    36  // TODO(adonovan): think about generic aliases.
    37  
    38  // A Path is an opaque name that identifies a types.Object
    39  // relative to its package. Conceptually, the name consists of a
    40  // sequence of destructuring operations applied to the package scope
    41  // to obtain the original object.
    42  // The name does not include the package itself.
    43  type Path string
    44  
    45  // Encoding
    46  //
    47  // An object path is a textual and (with training) human-readable encoding
    48  // of a sequence of destructuring operators, starting from a types.Package.
    49  // The sequences represent a path through the package/object/type graph.
    50  // We classify these operators by their type:
    51  //
    52  //	PO package->object	Package.Scope.Lookup
    53  //	OT  object->type 	Object.Type
    54  //	TT    type->type 	Type.{Elem,Key,Params,Results,Underlying} [EKPRU]
    55  //	TO   type->object	Type.{At,Field,Method,Obj} [AFMO]
    56  //
    57  // All valid paths start with a package and end at an object
    58  // and thus may be defined by the regular language:
    59  //
    60  //	objectpath = PO (OT TT* TO)*
    61  //
    62  // The concrete encoding follows directly:
    63  //   - The only PO operator is Package.Scope.Lookup, which requires an identifier.
    64  //   - The only OT operator is Object.Type,
    65  //     which we encode as '.' because dot cannot appear in an identifier.
    66  //   - The TT operators are encoded as [EKPRUTC];
    67  //     one of these (TypeParam) requires an integer operand,
    68  //     which is encoded as a string of decimal digits.
    69  //   - The TO operators are encoded as [AFMO];
    70  //     three of these (At,Field,Method) require an integer operand,
    71  //     which is encoded as a string of decimal digits.
    72  //     These indices are stable across different representations
    73  //     of the same package, even source and export data.
    74  //     The indices used are implementation specific and may not correspond to
    75  //     the argument to the go/types function.
    76  //
    77  // In the example below,
    78  //
    79  //	package p
    80  //
    81  //	type T interface {
    82  //		f() (a string, b struct{ X int })
    83  //	}
    84  //
    85  // field X has the path "T.UM0.RA1.F0",
    86  // representing the following sequence of operations:
    87  //
    88  //	p.Lookup("T")					T
    89  //	.Type().Underlying().Method(0).			f
    90  //	.Type().Results().At(1)				b
    91  //	.Type().Field(0)					X
    92  //
    93  // The encoding is not maximally compact---every R or P is
    94  // followed by an A, for example---but this simplifies the
    95  // encoder and decoder.
    96  const (
    97  	// object->type operators
    98  	opType = '.' // .Type()		  (Object)
    99  
   100  	// type->type operators
   101  	opElem       = 'E' // .Elem()		        (Pointer, Slice, Array, Chan, Map)
   102  	opKey        = 'K' // .Key()		        (Map)
   103  	opParams     = 'P' // .Params()		      (Signature)
   104  	opResults    = 'R' // .Results()	      (Signature)
   105  	opUnderlying = 'U' // .Underlying()	    (Named)
   106  	opTypeParam  = 'T' // .TypeParams.At(i) (Named, Signature)
   107  	opConstraint = 'C' // .Constraint()     (TypeParam)
   108  
   109  	// type->object operators
   110  	opAt     = 'A' // .At(i)		 (Tuple)
   111  	opField  = 'F' // .Field(i)	 (Struct)
   112  	opMethod = 'M' // .Method(i) (Named or Interface; not Struct: "promoted" names are ignored)
   113  	opObj    = 'O' // .Obj()		 (Named, TypeParam)
   114  )
   115  
   116  // For is equivalent to new(Encoder).For(obj).
   117  //
   118  // It may be more efficient to reuse a single Encoder across several calls.
   119  func For(obj types.Object) (Path, error) {
   120  	return new(Encoder).For(obj)
   121  }
   122  
   123  // An Encoder amortizes the cost of encoding the paths of multiple objects.
   124  // The zero value of an Encoder is ready to use.
   125  type Encoder struct {
   126  	scopeMemo map[*types.Scope][]types.Object // memoization of scopeObjects
   127  }
   128  
   129  // For returns the path to an object relative to its package,
   130  // or an error if the object is not accessible from the package's Scope.
   131  //
   132  // The For function guarantees to return a path only for the following objects:
   133  // - package-level types
   134  // - exported package-level non-types
   135  // - methods
   136  // - parameter and result variables
   137  // - struct fields
   138  // These objects are sufficient to define the API of their package.
   139  // The objects described by a package's export data are drawn from this set.
   140  //
   141  // The set of objects accessible from a package's Scope depends on
   142  // whether the package was produced by type-checking syntax, or
   143  // reading export data; the latter may have a smaller Scope since
   144  // export data trims objects that are not reachable from an exported
   145  // declaration. For example, the For function will return a path for
   146  // an exported method of an unexported type that is not reachable
   147  // from any public declaration; this path will cause the Object
   148  // function to fail if called on a package loaded from export data.
   149  // TODO(adonovan): is this a bug or feature? Should this package
   150  // compute accessibility in the same way?
   151  //
   152  // For does not return a path for predeclared names, imported package
   153  // names, local names, and unexported package-level names (except
   154  // types).
   155  //
   156  // Example: given this definition,
   157  //
   158  //	package p
   159  //
   160  //	type T interface {
   161  //		f() (a string, b struct{ X int })
   162  //	}
   163  //
   164  // For(X) would return a path that denotes the following sequence of operations:
   165  //
   166  //	p.Scope().Lookup("T")				(TypeName T)
   167  //	.Type().Underlying().Method(0).			(method Func f)
   168  //	.Type().Results().At(1)				(field Var b)
   169  //	.Type().Field(0)					(field Var X)
   170  //
   171  // where p is the package (*types.Package) to which X belongs.
   172  func (enc *Encoder) For(obj types.Object) (Path, error) {
   173  	pkg := obj.Pkg()
   174  
   175  	// This table lists the cases of interest.
   176  	//
   177  	// Object				Action
   178  	// ------                               ------
   179  	// nil					reject
   180  	// builtin				reject
   181  	// pkgname				reject
   182  	// label				reject
   183  	// var
   184  	//    package-level			accept
   185  	//    func param/result			accept
   186  	//    local				reject
   187  	//    struct field			accept
   188  	// const
   189  	//    package-level			accept
   190  	//    local				reject
   191  	// func
   192  	//    package-level			accept
   193  	//    init functions			reject
   194  	//    concrete method			accept
   195  	//    interface method			accept
   196  	// type
   197  	//    package-level			accept
   198  	//    local				reject
   199  	//
   200  	// The only accessible package-level objects are members of pkg itself.
   201  	//
   202  	// The cases are handled in four steps:
   203  	//
   204  	// 1. reject nil and builtin
   205  	// 2. accept package-level objects
   206  	// 3. reject obviously invalid objects
   207  	// 4. search the API for the path to the param/result/field/method.
   208  
   209  	// 1. reference to nil or builtin?
   210  	if pkg == nil {
   211  		return "", fmt.Errorf("predeclared %s has no path", obj)
   212  	}
   213  	scope := pkg.Scope()
   214  
   215  	// 2. package-level object?
   216  	if scope.Lookup(obj.Name()) == obj {
   217  		// Only exported objects (and non-exported types) have a path.
   218  		// Non-exported types may be referenced by other objects.
   219  		if _, ok := obj.(*types.TypeName); !ok && !obj.Exported() {
   220  			return "", fmt.Errorf("no path for non-exported %v", obj)
   221  		}
   222  		return Path(obj.Name()), nil
   223  	}
   224  
   225  	// 3. Not a package-level object.
   226  	//    Reject obviously non-viable cases.
   227  	switch obj := obj.(type) {
   228  	case *types.TypeName:
   229  		if _, ok := aliases.Unalias(obj.Type()).(*types.TypeParam); !ok {
   230  			// With the exception of type parameters, only package-level type names
   231  			// have a path.
   232  			return "", fmt.Errorf("no path for %v", obj)
   233  		}
   234  	case *types.Const, // Only package-level constants have a path.
   235  		*types.Label,   // Labels are function-local.
   236  		*types.PkgName: // PkgNames are file-local.
   237  		return "", fmt.Errorf("no path for %v", obj)
   238  
   239  	case *types.Var:
   240  		// Could be:
   241  		// - a field (obj.IsField())
   242  		// - a func parameter or result
   243  		// - a local var.
   244  		// Sadly there is no way to distinguish
   245  		// a param/result from a local
   246  		// so we must proceed to the find.
   247  
   248  	case *types.Func:
   249  		// A func, if not package-level, must be a method.
   250  		if recv := obj.Type().(*types.Signature).Recv(); recv == nil {
   251  			return "", fmt.Errorf("func is not a method: %v", obj)
   252  		}
   253  
   254  		if path, ok := enc.concreteMethod(obj); ok {
   255  			// Fast path for concrete methods that avoids looping over scope.
   256  			return path, nil
   257  		}
   258  
   259  	default:
   260  		panic(obj)
   261  	}
   262  
   263  	// 4. Search the API for the path to the var (field/param/result) or method.
   264  
   265  	// First inspect package-level named types.
   266  	// In the presence of path aliases, these give
   267  	// the best paths because non-types may
   268  	// refer to types, but not the reverse.
   269  	empty := make([]byte, 0, 48) // initial space
   270  	objs := enc.scopeObjects(scope)
   271  	for _, o := range objs {
   272  		tname, ok := o.(*types.TypeName)
   273  		if !ok {
   274  			continue // handle non-types in second pass
   275  		}
   276  
   277  		path := append(empty, o.Name()...)
   278  		path = append(path, opType)
   279  
   280  		T := o.Type()
   281  
   282  		if tname.IsAlias() {
   283  			// type alias
   284  			if r := find(obj, T, path, nil); r != nil {
   285  				return Path(r), nil
   286  			}
   287  		} else {
   288  			if named, _ := T.(*types.Named); named != nil {
   289  				if r := findTypeParam(obj, named.TypeParams(), path, nil); r != nil {
   290  					// generic named type
   291  					return Path(r), nil
   292  				}
   293  			}
   294  			// defined (named) type
   295  			if r := find(obj, T.Underlying(), append(path, opUnderlying), nil); r != nil {
   296  				return Path(r), nil
   297  			}
   298  		}
   299  	}
   300  
   301  	// Then inspect everything else:
   302  	// non-types, and declared methods of defined types.
   303  	for _, o := range objs {
   304  		path := append(empty, o.Name()...)
   305  		if _, ok := o.(*types.TypeName); !ok {
   306  			if o.Exported() {
   307  				// exported non-type (const, var, func)
   308  				if r := find(obj, o.Type(), append(path, opType), nil); r != nil {
   309  					return Path(r), nil
   310  				}
   311  			}
   312  			continue
   313  		}
   314  
   315  		// Inspect declared methods of defined types.
   316  		if T, ok := aliases.Unalias(o.Type()).(*types.Named); ok {
   317  			path = append(path, opType)
   318  			// The method index here is always with respect
   319  			// to the underlying go/types data structures,
   320  			// which ultimately derives from source order
   321  			// and must be preserved by export data.
   322  			for i := 0; i < T.NumMethods(); i++ {
   323  				m := T.Method(i)
   324  				path2 := appendOpArg(path, opMethod, i)
   325  				if m == obj {
   326  					return Path(path2), nil // found declared method
   327  				}
   328  				if r := find(obj, m.Type(), append(path2, opType), nil); r != nil {
   329  					return Path(r), nil
   330  				}
   331  			}
   332  		}
   333  	}
   334  
   335  	return "", fmt.Errorf("can't find path for %v in %s", obj, pkg.Path())
   336  }
   337  
   338  func appendOpArg(path []byte, op byte, arg int) []byte {
   339  	path = append(path, op)
   340  	path = strconv.AppendInt(path, int64(arg), 10)
   341  	return path
   342  }
   343  
   344  // concreteMethod returns the path for meth, which must have a non-nil receiver.
   345  // The second return value indicates success and may be false if the method is
   346  // an interface method or if it is an instantiated method.
   347  //
   348  // This function is just an optimization that avoids the general scope walking
   349  // approach. You are expected to fall back to the general approach if this
   350  // function fails.
   351  func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) {
   352  	// Concrete methods can only be declared on package-scoped named types. For
   353  	// that reason we can skip the expensive walk over the package scope: the
   354  	// path will always be package -> named type -> method. We can trivially get
   355  	// the type name from the receiver, and only have to look over the type's
   356  	// methods to find the method index.
   357  	//
   358  	// Methods on generic types require special consideration, however. Consider
   359  	// the following package:
   360  	//
   361  	// 	L1: type S[T any] struct{}
   362  	// 	L2: func (recv S[A]) Foo() { recv.Bar() }
   363  	// 	L3: func (recv S[B]) Bar() { }
   364  	// 	L4: type Alias = S[int]
   365  	// 	L5: func _[T any]() { var s S[int]; s.Foo() }
   366  	//
   367  	// The receivers of methods on generic types are instantiations. L2 and L3
   368  	// instantiate S with the type-parameters A and B, which are scoped to the
   369  	// respective methods. L4 and L5 each instantiate S with int. Each of these
   370  	// instantiations has its own method set, full of methods (and thus objects)
   371  	// with receivers whose types are the respective instantiations. In other
   372  	// words, we have
   373  	//
   374  	// S[A].Foo, S[A].Bar
   375  	// S[B].Foo, S[B].Bar
   376  	// S[int].Foo, S[int].Bar
   377  	//
   378  	// We may thus be trying to produce object paths for any of these objects.
   379  	//
   380  	// S[A].Foo and S[B].Bar are the origin methods, and their paths are S.Foo
   381  	// and S.Bar, which are the paths that this function naturally produces.
   382  	//
   383  	// S[A].Bar, S[B].Foo, and both methods on S[int] are instantiations that
   384  	// don't correspond to the origin methods. For S[int], this is significant.
   385  	// The most precise object path for S[int].Foo, for example, is Alias.Foo,
   386  	// not S.Foo. Our function, however, would produce S.Foo, which would
   387  	// resolve to a different object.
   388  	//
   389  	// For S[A].Bar and S[B].Foo it could be argued that S.Bar and S.Foo are
   390  	// still the correct paths, since only the origin methods have meaningful
   391  	// paths. But this is likely only true for trivial cases and has edge cases.
   392  	// Since this function is only an optimization, we err on the side of giving
   393  	// up, deferring to the slower but definitely correct algorithm. Most users
   394  	// of objectpath will only be giving us origin methods, anyway, as referring
   395  	// to instantiated methods is usually not useful.
   396  
   397  	if meth.Origin() != meth {
   398  		return "", false
   399  	}
   400  
   401  	_, named := typesinternal.ReceiverNamed(meth.Type().(*types.Signature).Recv())
   402  	if named == nil {
   403  		return "", false
   404  	}
   405  
   406  	if types.IsInterface(named) {
   407  		// Named interfaces don't have to be package-scoped
   408  		//
   409  		// TODO(dominikh): opt: if scope.Lookup(name) == named, then we can apply this optimization to interface
   410  		// methods, too, I think.
   411  		return "", false
   412  	}
   413  
   414  	// Preallocate space for the name, opType, opMethod, and some digits.
   415  	name := named.Obj().Name()
   416  	path := make([]byte, 0, len(name)+8)
   417  	path = append(path, name...)
   418  	path = append(path, opType)
   419  
   420  	// Method indices are w.r.t. the go/types data structures,
   421  	// ultimately deriving from source order,
   422  	// which is preserved by export data.
   423  	for i := 0; i < named.NumMethods(); i++ {
   424  		if named.Method(i) == meth {
   425  			path = appendOpArg(path, opMethod, i)
   426  			return Path(path), true
   427  		}
   428  	}
   429  
   430  	// Due to golang/go#59944, go/types fails to associate the receiver with
   431  	// certain methods on cgo types.
   432  	//
   433  	// TODO(rfindley): replace this panic once golang/go#59944 is fixed in all Go
   434  	// versions gopls supports.
   435  	return "", false
   436  	// panic(fmt.Sprintf("couldn't find method %s on type %s; methods: %#v", meth, named, enc.namedMethods(named)))
   437  }
   438  
   439  // find finds obj within type T, returning the path to it, or nil if not found.
   440  //
   441  // The seen map is used to short circuit cycles through type parameters. If
   442  // nil, it will be allocated as necessary.
   443  func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte {
   444  	switch T := T.(type) {
   445  	case *aliases.Alias:
   446  		return find(obj, aliases.Unalias(T), path, seen)
   447  	case *types.Basic, *types.Named:
   448  		// Named types belonging to pkg were handled already,
   449  		// so T must belong to another package. No path.
   450  		return nil
   451  	case *types.Pointer:
   452  		return find(obj, T.Elem(), append(path, opElem), seen)
   453  	case *types.Slice:
   454  		return find(obj, T.Elem(), append(path, opElem), seen)
   455  	case *types.Array:
   456  		return find(obj, T.Elem(), append(path, opElem), seen)
   457  	case *types.Chan:
   458  		return find(obj, T.Elem(), append(path, opElem), seen)
   459  	case *types.Map:
   460  		if r := find(obj, T.Key(), append(path, opKey), seen); r != nil {
   461  			return r
   462  		}
   463  		return find(obj, T.Elem(), append(path, opElem), seen)
   464  	case *types.Signature:
   465  		if r := findTypeParam(obj, T.TypeParams(), path, seen); r != nil {
   466  			return r
   467  		}
   468  		if r := find(obj, T.Params(), append(path, opParams), seen); r != nil {
   469  			return r
   470  		}
   471  		return find(obj, T.Results(), append(path, opResults), seen)
   472  	case *types.Struct:
   473  		for i := 0; i < T.NumFields(); i++ {
   474  			fld := T.Field(i)
   475  			path2 := appendOpArg(path, opField, i)
   476  			if fld == obj {
   477  				return path2 // found field var
   478  			}
   479  			if r := find(obj, fld.Type(), append(path2, opType), seen); r != nil {
   480  				return r
   481  			}
   482  		}
   483  		return nil
   484  	case *types.Tuple:
   485  		for i := 0; i < T.Len(); i++ {
   486  			v := T.At(i)
   487  			path2 := appendOpArg(path, opAt, i)
   488  			if v == obj {
   489  				return path2 // found param/result var
   490  			}
   491  			if r := find(obj, v.Type(), append(path2, opType), seen); r != nil {
   492  				return r
   493  			}
   494  		}
   495  		return nil
   496  	case *types.Interface:
   497  		for i := 0; i < T.NumMethods(); i++ {
   498  			m := T.Method(i)
   499  			path2 := appendOpArg(path, opMethod, i)
   500  			if m == obj {
   501  				return path2 // found interface method
   502  			}
   503  			if r := find(obj, m.Type(), append(path2, opType), seen); r != nil {
   504  				return r
   505  			}
   506  		}
   507  		return nil
   508  	case *types.TypeParam:
   509  		name := T.Obj()
   510  		if name == obj {
   511  			return append(path, opObj)
   512  		}
   513  		if seen[name] {
   514  			return nil
   515  		}
   516  		if seen == nil {
   517  			seen = make(map[*types.TypeName]bool)
   518  		}
   519  		seen[name] = true
   520  		if r := find(obj, T.Constraint(), append(path, opConstraint), seen); r != nil {
   521  			return r
   522  		}
   523  		return nil
   524  	}
   525  	panic(T)
   526  }
   527  
   528  func findTypeParam(obj types.Object, list *types.TypeParamList, path []byte, seen map[*types.TypeName]bool) []byte {
   529  	for i := 0; i < list.Len(); i++ {
   530  		tparam := list.At(i)
   531  		path2 := appendOpArg(path, opTypeParam, i)
   532  		if r := find(obj, tparam, path2, seen); r != nil {
   533  			return r
   534  		}
   535  	}
   536  	return nil
   537  }
   538  
   539  // Object returns the object denoted by path p within the package pkg.
   540  func Object(pkg *types.Package, p Path) (types.Object, error) {
   541  	pathstr := string(p)
   542  	if pathstr == "" {
   543  		return nil, fmt.Errorf("empty path")
   544  	}
   545  
   546  	var pkgobj, suffix string
   547  	if dot := strings.IndexByte(pathstr, opType); dot < 0 {
   548  		pkgobj = pathstr
   549  	} else {
   550  		pkgobj = pathstr[:dot]
   551  		suffix = pathstr[dot:] // suffix starts with "."
   552  	}
   553  
   554  	obj := pkg.Scope().Lookup(pkgobj)
   555  	if obj == nil {
   556  		return nil, fmt.Errorf("package %s does not contain %q", pkg.Path(), pkgobj)
   557  	}
   558  
   559  	// abstraction of *types.{Pointer,Slice,Array,Chan,Map}
   560  	type hasElem interface {
   561  		Elem() types.Type
   562  	}
   563  	// abstraction of *types.{Named,Signature}
   564  	type hasTypeParams interface {
   565  		TypeParams() *types.TypeParamList
   566  	}
   567  	// abstraction of *types.{Named,TypeParam}
   568  	type hasObj interface {
   569  		Obj() *types.TypeName
   570  	}
   571  
   572  	// The loop state is the pair (t, obj),
   573  	// exactly one of which is non-nil, initially obj.
   574  	// All suffixes start with '.' (the only object->type operation),
   575  	// followed by optional type->type operations,
   576  	// then a type->object operation.
   577  	// The cycle then repeats.
   578  	var t types.Type
   579  	for suffix != "" {
   580  		code := suffix[0]
   581  		suffix = suffix[1:]
   582  
   583  		// Codes [AFM] have an integer operand.
   584  		var index int
   585  		switch code {
   586  		case opAt, opField, opMethod, opTypeParam:
   587  			rest := strings.TrimLeft(suffix, "0123456789")
   588  			numerals := suffix[:len(suffix)-len(rest)]
   589  			suffix = rest
   590  			i, err := strconv.Atoi(numerals)
   591  			if err != nil {
   592  				return nil, fmt.Errorf("invalid path: bad numeric operand %q for code %q", numerals, code)
   593  			}
   594  			index = int(i)
   595  		case opObj:
   596  			// no operand
   597  		default:
   598  			// The suffix must end with a type->object operation.
   599  			if suffix == "" {
   600  				return nil, fmt.Errorf("invalid path: ends with %q, want [AFMO]", code)
   601  			}
   602  		}
   603  
   604  		if code == opType {
   605  			if t != nil {
   606  				return nil, fmt.Errorf("invalid path: unexpected %q in type context", opType)
   607  			}
   608  			t = obj.Type()
   609  			obj = nil
   610  			continue
   611  		}
   612  
   613  		if t == nil {
   614  			return nil, fmt.Errorf("invalid path: code %q in object context", code)
   615  		}
   616  
   617  		// Inv: t != nil, obj == nil
   618  
   619  		t = aliases.Unalias(t)
   620  		switch code {
   621  		case opElem:
   622  			hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map
   623  			if !ok {
   624  				return nil, fmt.Errorf("cannot apply %q to %s (got %T, want pointer, slice, array, chan or map)", code, t, t)
   625  			}
   626  			t = hasElem.Elem()
   627  
   628  		case opKey:
   629  			mapType, ok := t.(*types.Map)
   630  			if !ok {
   631  				return nil, fmt.Errorf("cannot apply %q to %s (got %T, want map)", code, t, t)
   632  			}
   633  			t = mapType.Key()
   634  
   635  		case opParams:
   636  			sig, ok := t.(*types.Signature)
   637  			if !ok {
   638  				return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
   639  			}
   640  			t = sig.Params()
   641  
   642  		case opResults:
   643  			sig, ok := t.(*types.Signature)
   644  			if !ok {
   645  				return nil, fmt.Errorf("cannot apply %q to %s (got %T, want signature)", code, t, t)
   646  			}
   647  			t = sig.Results()
   648  
   649  		case opUnderlying:
   650  			named, ok := t.(*types.Named)
   651  			if !ok {
   652  				return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named)", code, t, t)
   653  			}
   654  			t = named.Underlying()
   655  
   656  		case opTypeParam:
   657  			hasTypeParams, ok := t.(hasTypeParams) // Named, Signature
   658  			if !ok {
   659  				return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or signature)", code, t, t)
   660  			}
   661  			tparams := hasTypeParams.TypeParams()
   662  			if n := tparams.Len(); index >= n {
   663  				return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
   664  			}
   665  			t = tparams.At(index)
   666  
   667  		case opConstraint:
   668  			tparam, ok := t.(*types.TypeParam)
   669  			if !ok {
   670  				return nil, fmt.Errorf("cannot apply %q to %s (got %T, want type parameter)", code, t, t)
   671  			}
   672  			t = tparam.Constraint()
   673  
   674  		case opAt:
   675  			tuple, ok := t.(*types.Tuple)
   676  			if !ok {
   677  				return nil, fmt.Errorf("cannot apply %q to %s (got %T, want tuple)", code, t, t)
   678  			}
   679  			if n := tuple.Len(); index >= n {
   680  				return nil, fmt.Errorf("tuple index %d out of range [0-%d)", index, n)
   681  			}
   682  			obj = tuple.At(index)
   683  			t = nil
   684  
   685  		case opField:
   686  			structType, ok := t.(*types.Struct)
   687  			if !ok {
   688  				return nil, fmt.Errorf("cannot apply %q to %s (got %T, want struct)", code, t, t)
   689  			}
   690  			if n := structType.NumFields(); index >= n {
   691  				return nil, fmt.Errorf("field index %d out of range [0-%d)", index, n)
   692  			}
   693  			obj = structType.Field(index)
   694  			t = nil
   695  
   696  		case opMethod:
   697  			switch t := t.(type) {
   698  			case *types.Interface:
   699  				if index >= t.NumMethods() {
   700  					return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
   701  				}
   702  				obj = t.Method(index) // Id-ordered
   703  
   704  			case *types.Named:
   705  				if index >= t.NumMethods() {
   706  					return nil, fmt.Errorf("method index %d out of range [0-%d)", index, t.NumMethods())
   707  				}
   708  				obj = t.Method(index)
   709  
   710  			default:
   711  				return nil, fmt.Errorf("cannot apply %q to %s (got %T, want interface or named)", code, t, t)
   712  			}
   713  			t = nil
   714  
   715  		case opObj:
   716  			hasObj, ok := t.(hasObj)
   717  			if !ok {
   718  				return nil, fmt.Errorf("cannot apply %q to %s (got %T, want named or type param)", code, t, t)
   719  			}
   720  			obj = hasObj.Obj()
   721  			t = nil
   722  
   723  		default:
   724  			return nil, fmt.Errorf("invalid path: unknown code %q", code)
   725  		}
   726  	}
   727  
   728  	if obj.Pkg() != pkg {
   729  		return nil, fmt.Errorf("path denotes %s, which belongs to a different package", obj)
   730  	}
   731  
   732  	return obj, nil // success
   733  }
   734  
   735  // scopeObjects is a memoization of scope objects.
   736  // Callers must not modify the result.
   737  func (enc *Encoder) scopeObjects(scope *types.Scope) []types.Object {
   738  	m := enc.scopeMemo
   739  	if m == nil {
   740  		m = make(map[*types.Scope][]types.Object)
   741  		enc.scopeMemo = m
   742  	}
   743  	objs, ok := m[scope]
   744  	if !ok {
   745  		names := scope.Names() // allocates and sorts
   746  		objs = make([]types.Object, len(names))
   747  		for i, name := range names {
   748  			objs[i] = scope.Lookup(name)
   749  		}
   750  		m[scope] = objs
   751  	}
   752  	return objs
   753  }