go/types, types2: better error when method is missing due to ambiguity

If a type doesn't implement an interface due to an ambiguous method,
say so in the error message instead of just reporting a missing method.

Fixes #57352.

Change-Id: I5608f893c485de578b7f86362ca6f66033653695
Reviewed-on: https://go-review.googlesource.com/c/go/+/473658
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2023-03-06 13:05:29 -08:00 committed by Gopher Robot
parent ebc2aaae24
commit c6cdfdabef
4 changed files with 54 additions and 23 deletions

View File

@ -343,6 +343,7 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y
notFound
wrongName
wrongSig
ambigSel
ptrRecv
field
)
@ -371,21 +372,23 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y
}
} else {
for _, m = range methods {
obj, _, indirect := lookupFieldOrMethodImpl(V, false, m.pkg, m.name, false)
obj, index, indirect := lookupFieldOrMethodImpl(V, false, m.pkg, m.name, false)
// check if m is on *V, or on V with case-folding
// check if m is ambiguous, on *V, or on V with case-folding
if obj == nil {
if indirect {
switch {
case index != nil:
state = ambigSel
case indirect:
state = ptrRecv
break
default:
state = notFound
obj, _, _ = lookupFieldOrMethodImpl(V, false, m.pkg, m.name, true /* fold case */)
f, _ = obj.(*Func)
if f != nil {
state = wrongName
}
}
obj, _, _ = lookupFieldOrMethodImpl(V, false, m.pkg, m.name, true /* fold case */)
f, _ = obj.(*Func)
if f != nil {
state = wrongName
break
}
state = notFound
break
}
@ -436,6 +439,8 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y
}
*cause = check.sprintf("(wrong type for method %s)\n\t\thave %s\n\t\twant %s",
m.Name(), fs, ms)
case ambigSel:
*cause = check.sprintf("(ambiguous selector %s.%s)", V, m.Name())
case ptrRecv:
*cause = check.sprintf("(method %s has pointer receiver)", m.Name())
case field:

View File

@ -345,6 +345,7 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y
notFound
wrongName
wrongSig
ambigSel
ptrRecv
field
)
@ -373,21 +374,23 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y
}
} else {
for _, m = range methods {
obj, _, indirect := lookupFieldOrMethodImpl(V, false, m.pkg, m.name, false)
obj, index, indirect := lookupFieldOrMethodImpl(V, false, m.pkg, m.name, false)
// check if m is on *V, or on V with case-folding
// check if m is ambiguous, on *V, or on V with case-folding
if obj == nil {
if indirect {
switch {
case index != nil:
state = ambigSel
case indirect:
state = ptrRecv
break
default:
state = notFound
obj, _, _ = lookupFieldOrMethodImpl(V, false, m.pkg, m.name, true /* fold case */)
f, _ = obj.(*Func)
if f != nil {
state = wrongName
}
}
obj, _, _ = lookupFieldOrMethodImpl(V, false, m.pkg, m.name, true /* fold case */)
f, _ = obj.(*Func)
if f != nil {
state = wrongName
break
}
state = notFound
break
}
@ -438,6 +441,8 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y
}
*cause = check.sprintf("(wrong type for method %s)\n\t\thave %s\n\t\twant %s",
m.Name(), fs, ms)
case ambigSel:
*cause = check.sprintf("(ambiguous selector %s.%s)", V, m.Name())
case ptrRecv:
*cause = check.sprintf("(method %s has pointer receiver)", m.Name())
case field:

View File

@ -195,5 +195,5 @@ var (
)
var (
_ interface{ xm() } = eD /* ERROR "missing method xm" */ {}
_ interface{ xm() } = eD /* ERROR "ambiguous selector eD.xm" */ {}
)

View File

@ -0,0 +1,21 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package p
type A interface {
a()
}
type AB interface {
A
b()
}
type AAB struct {
A
AB
}
var _ AB = AAB /* ERROR "ambiguous selector AAB.a" */ {}