implements.go raw

   1  package unused
   2  
   3  import (
   4  	"go/types"
   5  )
   6  
   7  // lookupMethod returns the index of and method with matching package and name, or (-1, nil).
   8  func lookupMethod(T *types.Interface, pkg *types.Package, name string) (int, *types.Func) {
   9  	if name != "_" {
  10  		for i := 0; i < T.NumMethods(); i++ {
  11  			m := T.Method(i)
  12  			if sameId(m, pkg, name) {
  13  				return i, m
  14  			}
  15  		}
  16  	}
  17  	return -1, nil
  18  }
  19  
  20  func sameId(obj types.Object, pkg *types.Package, name string) bool {
  21  	// spec:
  22  	// "Two identifiers are different if they are spelled differently,
  23  	// or if they appear in different packages and are not exported.
  24  	// Otherwise, they are the same."
  25  	if name != obj.Name() {
  26  		return false
  27  	}
  28  	// obj.Name == name
  29  	if obj.Exported() {
  30  		return true
  31  	}
  32  	// not exported, so packages must be the same (pkg == nil for
  33  	// fields in Universe scope; this can only happen for types
  34  	// introduced via Eval)
  35  	if pkg == nil || obj.Pkg() == nil {
  36  		return pkg == obj.Pkg()
  37  	}
  38  	// pkg != nil && obj.pkg != nil
  39  	return pkg.Path() == obj.Pkg().Path()
  40  }
  41  
  42  func implements(V types.Type, T *types.Interface, msV *types.MethodSet) ([]*types.Selection, bool) {
  43  	// fast path for common case
  44  	if T.Empty() {
  45  		return nil, true
  46  	}
  47  
  48  	if ityp, _ := V.Underlying().(*types.Interface); ityp != nil {
  49  		// TODO(dh): is this code reachable?
  50  		for i := 0; i < T.NumMethods(); i++ {
  51  			m := T.Method(i)
  52  			_, obj := lookupMethod(ityp, m.Pkg(), m.Name())
  53  			switch {
  54  			case obj == nil:
  55  				return nil, false
  56  			case !types.Identical(obj.Type(), m.Type()):
  57  				return nil, false
  58  			}
  59  		}
  60  		return nil, true
  61  	}
  62  
  63  	// A concrete type implements T if it implements all methods of T.
  64  	var sels []*types.Selection
  65  	var c methodsChecker
  66  	for i := 0; i < T.NumMethods(); i++ {
  67  		m := T.Method(i)
  68  		sel := msV.Lookup(m.Pkg(), m.Name())
  69  		if sel == nil {
  70  			return nil, false
  71  		}
  72  
  73  		f, _ := sel.Obj().(*types.Func)
  74  		if f == nil {
  75  			return nil, false
  76  		}
  77  
  78  		if !c.methodIsCompatible(f, m) {
  79  			return nil, false
  80  		}
  81  
  82  		sels = append(sels, sel)
  83  	}
  84  	return sels, true
  85  }
  86  
  87  type methodsChecker struct {
  88  	typeParams map[*types.TypeParam]types.Type
  89  }
  90  
  91  // Currently, this doesn't support methods like `foo(x []T)`.
  92  func (c *methodsChecker) methodIsCompatible(implFunc *types.Func, interfaceFunc *types.Func) bool {
  93  	if types.Identical(implFunc.Type(), interfaceFunc.Type()) {
  94  		return true
  95  	}
  96  	implSig, implOk := implFunc.Type().(*types.Signature)
  97  	interfaceSig, interfaceOk := interfaceFunc.Type().(*types.Signature)
  98  	if !implOk || !interfaceOk {
  99  		// probably not reachable. handle conservatively.
 100  		return false
 101  	}
 102  
 103  	if !c.typesAreCompatible(implSig.Params(), interfaceSig.Params()) {
 104  		return false
 105  	}
 106  
 107  	if !c.typesAreCompatible(implSig.Results(), interfaceSig.Results()) {
 108  		return false
 109  	}
 110  
 111  	return true
 112  }
 113  
 114  func (c *methodsChecker) typesAreCompatible(implTypes, interfaceTypes *types.Tuple) bool {
 115  	if implTypes.Len() != interfaceTypes.Len() {
 116  		return false
 117  	}
 118  	for i := 0; i < implTypes.Len(); i++ {
 119  		if !c.typeIsCompatible(implTypes.At(i).Type(), interfaceTypes.At(i).Type()) {
 120  			return false
 121  		}
 122  	}
 123  	return true
 124  }
 125  
 126  func (c *methodsChecker) typeIsCompatible(implType, interfaceType types.Type) bool {
 127  	if types.Identical(implType, interfaceType) {
 128  		return true
 129  	}
 130  	// We only support trivial use of type parameters. This isn't fully compatible with compiler type checking yet.
 131  	tp, ok := interfaceType.(*types.TypeParam)
 132  	if !ok {
 133  		return false
 134  	}
 135  	if c.typeParams == nil {
 136  		c.typeParams = make(map[*types.TypeParam]types.Type)
 137  	}
 138  	if c.typeParams[tp] == nil {
 139  		if !satisfiesConstraint(implType, tp) {
 140  			return false
 141  		}
 142  		c.typeParams[tp] = implType
 143  		return true
 144  	}
 145  	return types.Identical(c.typeParams[tp], implType)
 146  }
 147  
 148  func satisfiesConstraint(t types.Type, tp *types.TypeParam) bool {
 149  	bound := tp.Constraint().Underlying().(*types.Interface)
 150  	return types.Satisfies(t, bound)
 151  }
 152