mirror of
https://github.com/golang/go
synced 2024-11-18 04:14:49 -07: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:
parent
4eaf855155
commit
71a671839f
@ -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) {
|
||||
|
@ -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."
|
||||
|
@ -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()
|
||||
}
|
||||
|
5
src/go/types/testdata/issues.src
vendored
5
src/go/types/testdata/issues.src
vendored
@ -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 */ }
|
||||
|
Loading…
Reference in New Issue
Block a user