1
0
mirror of https://github.com/golang/go synced 2024-09-30 08:18:40 -06:00

go/types: add detail to missing method error messages

When a concrete type doesn't exactly implement an interface, the error
messages produced by go/types are often unhelpful. The compiler shows
the expected signature versus the one found, which is useful, so add
this behavior here.

Fixes golang/go#38475

Change-Id: I8b780b7e1f1f433a0efe670de3b1437053f42fba
Reviewed-on: https://go-review.googlesource.com/c/go/+/228457
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Rebecca Stambler 2020-04-15 18:31:29 -04:00
parent 4eaf855155
commit 71a671839f
4 changed files with 21 additions and 14 deletions

View File

@ -1568,12 +1568,12 @@ func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface,
}
var msg string
if wrongType {
msg = "wrong type for method"
if wrongType != nil {
msg = fmt.Sprintf("wrong type for method %s (have %s, want %s)", method.name, wrongType.typ, method.typ)
} else {
msg = "missing method"
msg = "missing method " + method.name
}
check.errorf(pos, "%s cannot have dynamic type %s (%s %s)", x, T, msg, method.name)
check.errorf(pos, "%s cannot have dynamic type %s (%s)", x, T, msg)
}
func (check *Checker) singleValue(x *operand) {

View File

@ -263,14 +263,17 @@ func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) {
// x is of interface type V).
//
func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
return (*Checker)(nil).missingMethod(V, T, static)
m, typ := (*Checker)(nil).missingMethod(V, T, static)
return m, typ != nil
}
// missingMethod is like MissingMethod but accepts a receiver.
// The receiver may be nil if missingMethod is invoked through
// an exported API call (such as MissingMethod), i.e., when all
// methods have been type-checked.
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
// If the type has the correctly names method, but with the wrong
// signature, the existing method is returned as well.
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) {
check.completeInterface(T)
// fast path for common case
@ -286,10 +289,10 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *
switch {
case obj == nil:
if static {
return m, false
return m, nil
}
case !check.identical(obj.Type(), m.typ):
return m, true
return m, obj
}
}
return
@ -302,7 +305,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *
// we must have a method (not a field of matching function type)
f, _ := obj.(*Func)
if f == nil {
return m, false
return m, nil
}
// methods may not have a fully set up signature yet
@ -311,7 +314,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *
}
if !check.identical(f.typ, m.typ) {
return m, true
return m, f
}
}
@ -323,7 +326,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *
// method required by V and whether it is missing or just has the wrong type.
// The receiver may be nil if assertableTo is invoked through an exported API call
// (such as AssertableTo), i.e., when all methods have been type-checked.
func (check *Checker) assertableTo(V *Interface, T Type) (method *Func, wrongType bool) {
func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Func) {
// no static check is required if T is an interface
// spec: "If T is an interface type, x.(T) asserts that the
// dynamic type of x implements the interface T."

View File

@ -8,6 +8,7 @@ package types
import (
"bytes"
"fmt"
"go/ast"
"go/constant"
"go/token"
@ -254,8 +255,8 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
if Ti, ok := Tu.(*Interface); ok {
if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ {
if reason != nil {
if wrongType {
*reason = "wrong type for method " + m.Name()
if wrongType != nil {
*reason = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrongType.typ, m.typ)
} else {
*reason = "missing method " + m.Name()
}

View File

@ -129,6 +129,9 @@ func issue10260() {
t1 *T1
t2 *T2
)
_ = i2 /* ERROR i2 .* cannot have dynamic type \*T1 \(wrong type for method foo \(have func\(\), want func\(x int\)\)\) */ .(*T1)
i1 = i0 /* ERROR cannot use .* missing method foo */
i1 = t0 /* ERROR cannot use .* missing method foo */
i1 = i2 /* ERROR cannot use .* wrong type for method foo */
@ -146,7 +149,7 @@ func issue10260() {
// a few more - less exhaustive now
f := func(I1, I2){}
f(i0 /* ERROR cannot use .* missing method foo */ , i1 /* ERROR cannot use .* wrong type for method foo */)
f(i0 /* ERROR cannot use .* missing method foo */ , i1 /* ERROR cannot use .* wrong type for method foo \(have func\(\), want func\(x int\)\) */ )
_ = [...]I1{i0 /* ERROR cannot use .* missing method foo */ }
_ = [...]I1{i2 /* ERROR cannot use .* wrong type for method foo */ }