Skip to content

Commit 5ef4fc9

Browse files
committed
gopls/internal/golang/completion: fix the isEmptyInterface predicate
The isEmptyInterface predicate had a TODO noting that it should probably be considering the underlying type. After looking at usage, I agree that this was probably simple any oversight, but didn't matter because most uses of the empty interface were uses of any or interface{}, both of which were an interface. But with CL 580355, as the 'any' type is becoming a types.Alias, and this logic is caused inference scoring to change and gopls tests to fail. Fix isEmptyInterface to check underlying, and add a bit of commentary for the future. For golang/go#66921 Change-Id: I7ba3db1b04d83dda0372d9c39b943965f4d8c955 Reviewed-on: https://go-review.googlesource.com/c/tools/+/582335 Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 77f691b commit 5ef4fc9

File tree

2 files changed

+17
-3
lines changed

2 files changed

+17
-3
lines changed

gopls/internal/golang/completion/completion.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2936,6 +2936,13 @@ func (ci *candidateInference) candTypeMatches(cand *candidate) bool {
29362936

29372937
for _, expType := range expTypes {
29382938
if isEmptyInterface(expType) {
2939+
// If any type matches the expected type, fall back to other
2940+
// considerations below.
2941+
//
2942+
// TODO(rfindley): can this be expressed via scoring, rather than a boolean?
2943+
// Why is it the case that we break ties for the empty interface, but
2944+
// not for other expected types that may be satisfied by a lot of
2945+
// types, such as fmt.Stringer?
29392946
continue
29402947
}
29412948

gopls/internal/golang/completion/util.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,10 +142,17 @@ func isPkgName(obj types.Object) bool { return is[*types.PkgName](obj) }
142142
// TODO(adonovan): shouldn't this use CoreType(T)?
143143
func isPointer(T types.Type) bool { return is[*types.Pointer](aliases.Unalias(T)) }
144144

145+
// isEmptyInterface whether T is a (possibly Named or Alias) empty interface
146+
// type, such that every type is assignable to T.
147+
//
148+
// isEmptyInterface returns false for type parameters, since they have
149+
// different assignability rules.
145150
func isEmptyInterface(T types.Type) bool {
146-
// TODO(adonovan): shouldn't this use Underlying?
147-
intf, _ := T.(*types.Interface)
148-
return intf != nil && intf.NumMethods() == 0 && intf.IsMethodSet()
151+
if _, ok := T.(*types.TypeParam); ok {
152+
return false
153+
}
154+
intf, _ := T.Underlying().(*types.Interface)
155+
return intf != nil && intf.Empty()
149156
}
150157

151158
func isUntyped(T types.Type) bool {

0 commit comments

Comments
 (0)