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  }