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  }