...

Source file src/golang.org/x/tools/go/types/objectpath/objectpath.go

Documentation: golang.org/x/tools/go/types/objectpath

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

View as plain text