github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/go/types/typeutil/typeparams.go (about) 1 package typeutil 2 3 import ( 4 "errors" 5 "go/types" 6 7 "golang.org/x/exp/typeparams" 8 ) 9 10 type TypeSet struct { 11 Terms []*types.Term 12 empty bool 13 } 14 15 func NewTypeSet(typ types.Type) TypeSet { 16 terms, err := typeparams.NormalTerms(typ) 17 if err != nil { 18 if errors.Is(err, typeparams.ErrEmptyTypeSet) { 19 return TypeSet{nil, true} 20 } else { 21 // We couldn't determine the type set. Assume it's all types. 22 return TypeSet{nil, false} 23 } 24 } 25 return TypeSet{terms, false} 26 } 27 28 // CoreType returns the type set's core type, or nil if it has none. 29 // The function only looks at type terms and may thus return core types for some empty type sets, such as 30 // 'interface { map[int]string; foo() }' 31 func (ts TypeSet) CoreType() types.Type { 32 if len(ts.Terms) == 0 { 33 // Either the type set is empty, or it isn't constrained. Either way it doesn't have a core type. 34 return nil 35 } 36 typ := ts.Terms[0].Type().Underlying() 37 for _, term := range ts.Terms[1:] { 38 ut := term.Type().Underlying() 39 if types.Identical(typ, ut) { 40 continue 41 } 42 43 ch1, ok := typ.(*types.Chan) 44 if !ok { 45 return nil 46 } 47 ch2, ok := ut.(*types.Chan) 48 if !ok { 49 return nil 50 } 51 if ch1.Dir() == types.SendRecv { 52 // typ is currently a bidirectional channel. The term's type is either also bidirectional, or 53 // unidirectional. Use the term's type. 54 typ = ut 55 } else if ch2.Dir() == types.SendRecv { 56 // typ is currently a unidirectional channel and the term's type is bidirectional, which means it has no 57 // effect. 58 continue 59 } else if ch1.Dir() != ch2.Dir() { 60 // typ is not bidirectional and typ and term disagree about the direction 61 return nil 62 } 63 } 64 return typ 65 } 66 67 // CoreType is a wrapper for NewTypeSet(typ).CoreType() 68 func CoreType(typ types.Type) types.Type { 69 return NewTypeSet(typ).CoreType() 70 } 71 72 // All calls fn for each term in the type set and reports whether all invocations returned true. 73 // If the type set is empty or unconstrained, All immediately returns false. 74 func (ts TypeSet) All(fn func(*types.Term) bool) bool { 75 if len(ts.Terms) == 0 { 76 return false 77 } 78 for _, term := range ts.Terms { 79 if !fn(term) { 80 return false 81 } 82 } 83 return true 84 } 85 86 // Any calls fn for each term in the type set and reports whether any invocation returned true. 87 // It stops after the first call that returned true. 88 func (ts TypeSet) Any(fn func(*types.Term) bool) bool { 89 for _, term := range ts.Terms { 90 if fn(term) { 91 return true 92 } 93 } 94 return false 95 } 96 97 // All is a wrapper for NewTypeSet(typ).All(fn). 98 func All(typ types.Type, fn func(*types.Term) bool) bool { 99 return NewTypeSet(typ).All(fn) 100 } 101 102 // Any is a wrapper for NewTypeSet(typ).Any(fn). 103 func Any(typ types.Type, fn func(*types.Term) bool) bool { 104 return NewTypeSet(typ).Any(fn) 105 } 106 107 func IsSlice(term *types.Term) bool { 108 _, ok := term.Type().Underlying().(*types.Slice) 109 return ok 110 }