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  }