github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/go/types/typeutil/util.go (about)

     1  package typeutil
     2  
     3  import (
     4  	"bytes"
     5  	"go/types"
     6  	"sync"
     7  
     8  	"golang.org/x/exp/typeparams"
     9  )
    10  
    11  var bufferPool = &sync.Pool{
    12  	New: func() interface{} {
    13  		buf := bytes.NewBuffer(nil)
    14  		buf.Grow(64)
    15  		return buf
    16  	},
    17  }
    18  
    19  func FuncName(f *types.Func) string {
    20  	buf := bufferPool.Get().(*bytes.Buffer)
    21  	buf.Reset()
    22  	if f.Type() != nil {
    23  		sig := f.Type().(*types.Signature)
    24  		if recv := sig.Recv(); recv != nil {
    25  			buf.WriteByte('(')
    26  			if _, ok := recv.Type().(*types.Interface); ok {
    27  				// gcimporter creates abstract methods of
    28  				// named interfaces using the interface type
    29  				// (not the named type) as the receiver.
    30  				// Don't print it in full.
    31  				buf.WriteString("interface")
    32  			} else {
    33  				types.WriteType(buf, recv.Type(), nil)
    34  			}
    35  			buf.WriteByte(')')
    36  			buf.WriteByte('.')
    37  		} else if f.Pkg() != nil {
    38  			writePackage(buf, f.Pkg())
    39  		}
    40  	}
    41  	buf.WriteString(f.Name())
    42  	s := buf.String()
    43  	bufferPool.Put(buf)
    44  	return s
    45  }
    46  
    47  func writePackage(buf *bytes.Buffer, pkg *types.Package) {
    48  	if pkg == nil {
    49  		return
    50  	}
    51  	s := pkg.Path()
    52  	if s != "" {
    53  		buf.WriteString(s)
    54  		buf.WriteByte('.')
    55  	}
    56  }
    57  
    58  // Dereference returns a pointer's element type; otherwise it returns
    59  // T.
    60  func Dereference(T types.Type) types.Type {
    61  	if p, ok := T.Underlying().(*types.Pointer); ok {
    62  		return p.Elem()
    63  	}
    64  	return T
    65  }
    66  
    67  // DereferenceR returns a pointer's element type; otherwise it returns
    68  // T. If the element type is itself a pointer, DereferenceR will be
    69  // applied recursively.
    70  func DereferenceR(T types.Type) types.Type {
    71  	if p, ok := T.Underlying().(*types.Pointer); ok {
    72  		return DereferenceR(p.Elem())
    73  	}
    74  	return T
    75  }
    76  
    77  func IsObject(obj types.Object, name string) bool {
    78  	var path string
    79  	if pkg := obj.Pkg(); pkg != nil {
    80  		path = pkg.Path() + "."
    81  	}
    82  	return path+obj.Name() == name
    83  }
    84  
    85  // OPT(dh): IsType is kind of expensive; should we really use it?
    86  func IsType(T types.Type, name string) bool { return types.TypeString(T, nil) == name }
    87  
    88  // IsPointerLike returns true if type T is like a pointer. This returns true for all nillable types,
    89  // unsafe.Pointer, and type sets where at least one term is pointer-like.
    90  func IsPointerLike(T types.Type) bool {
    91  	switch T := T.Underlying().(type) {
    92  	case *types.Interface:
    93  		if T.IsMethodSet() {
    94  			return true
    95  		} else {
    96  			terms, err := typeparams.NormalTerms(T)
    97  			if err != nil {
    98  				return false
    99  			}
   100  			for _, term := range terms {
   101  				if IsPointerLike(term.Type()) {
   102  					return true
   103  				}
   104  			}
   105  			return false
   106  		}
   107  	case *types.Chan, *types.Map, *types.Signature, *types.Pointer, *types.Slice:
   108  		return true
   109  	case *types.Basic:
   110  		return T.Kind() == types.UnsafePointer
   111  	}
   112  	return false
   113  }
   114  
   115  type Field struct {
   116  	Var  *types.Var
   117  	Tag  string
   118  	Path []int
   119  }
   120  
   121  // FlattenFields recursively flattens T and embedded structs,
   122  // returning a list of fields. If multiple fields with the same name
   123  // exist, all will be returned.
   124  func FlattenFields(T *types.Struct) []Field {
   125  	return flattenFields(T, nil, nil)
   126  }
   127  
   128  func flattenFields(T *types.Struct, path []int, seen map[types.Type]bool) []Field {
   129  	if seen == nil {
   130  		seen = map[types.Type]bool{}
   131  	}
   132  	if seen[T] {
   133  		return nil
   134  	}
   135  	seen[T] = true
   136  	var out []Field
   137  	for i := 0; i < T.NumFields(); i++ {
   138  		field := T.Field(i)
   139  		tag := T.Tag(i)
   140  		np := append(path[:len(path):len(path)], i)
   141  		if field.Anonymous() {
   142  			if s, ok := Dereference(field.Type()).Underlying().(*types.Struct); ok {
   143  				out = append(out, flattenFields(s, np, seen)...)
   144  			} else {
   145  				out = append(out, Field{field, tag, np})
   146  			}
   147  		} else {
   148  			out = append(out, Field{field, tag, np})
   149  		}
   150  	}
   151  	return out
   152  }