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 }