1
0
mirror of https://github.com/golang/go synced 2024-09-29 04:14:27 -06:00

go/types, types2: better error reporting for Checker.implements

This CL copies (and adjusts as needed) the logic for error reporting
from operand.assignableTo to Checker.implements in the case of a missing
method failure and assignment to an interface pointer.

Preparation for using Checker.implements in operand.assignableTo
rather than implementing the same logic twice.

This also leads to better errors from Checker.implements as it's
using the same logic we already use elsewhere.

For #50646.

Change-Id: I199a1e02cf328b222ae52c10131db871539863bf
Reviewed-on: https://go-review.googlesource.com/c/go/+/381434
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2022-01-27 10:28:46 -08:00
parent 654d5f4b5d
commit 2e30c4b4bb
4 changed files with 48 additions and 29 deletions

View File

@ -173,7 +173,13 @@ func (check *Checker) implements(V, T Type, qf Qualifier) error {
Ti, _ := Tu.(*Interface)
if Ti == nil {
return errorf("%s is not an interface", T)
var cause string
if isInterfacePtr(Tu) {
cause = sprintf(qf, false, "type %s is pointer to interface, not interface", T)
} else {
cause = sprintf(qf, false, "%s is not an interface", T)
}
return errorf("%s does not implement %s (%s)", V, T, cause)
}
// Every type satisfies the empty interface.
@ -197,19 +203,21 @@ func (check *Checker) implements(V, T Type, qf Qualifier) error {
// V must implement T's methods, if any.
if Ti.NumMethods() > 0 {
if m, wrong := check.missingMethod(V, Ti, true); m != nil {
// TODO(gri) needs to print updated name to avoid major confusion in error message!
// (print warning for now)
// Old warning:
// check.softErrorf(pos, "%s does not implement %s (warning: name not updated) = %s (missing method %s)", V, T, Ti, m)
if wrong != nil {
// TODO(gri) This can still report uninstantiated types which makes the error message
// more difficult to read then necessary.
return errorf("%s does not implement %s: wrong method signature\n\tgot %s\n\twant %s",
V, T, wrong, m,
)
if m, wrong := check.missingMethod(V, Ti, true); m != nil /* !Implements(V, Ti) */ {
if check != nil && check.conf.CompilerErrorMessages {
return errorf("%s does not implement %s %s", V, T, check.missingMethodReason(V, T, m, wrong))
}
return errorf("%s does not implement %s (missing method %s)", V, T, m.name)
var cause string
if wrong != nil {
if Identical(m.typ, wrong.typ) {
cause = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
} else {
cause = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrong.typ, m.typ)
}
} else {
cause = "missing method " + m.Name()
}
return errorf("%s does not implement %s: %s", V, T, cause)
}
}

View File

@ -47,7 +47,7 @@ func (T) m1()
func (*T) m2()
func _() {
f2[T /* ERROR wrong method signature */ ]()
f2[T /* ERROR m2 has pointer receiver */ ]()
f2[*T]()
}

View File

@ -173,7 +173,17 @@ func (check *Checker) implements(V, T Type, qf Qualifier) error {
Ti, _ := Tu.(*Interface)
if Ti == nil {
return errorf("%s is not an interface", T)
var fset *token.FileSet
if check != nil {
fset = check.fset
}
var cause string
if isInterfacePtr(Tu) {
cause = sprintf(fset, qf, false, "type %s is pointer to interface, not interface", T)
} else {
cause = sprintf(fset, qf, false, "%s is not an interface", T)
}
return errorf("%s does not implement %s (%s)", V, T, cause)
}
// Every type satisfies the empty interface.
@ -197,20 +207,21 @@ func (check *Checker) implements(V, T Type, qf Qualifier) error {
// V must implement T's methods, if any.
if Ti.NumMethods() > 0 {
if m, wrong := check.missingMethod(V, Ti, true); m != nil {
// TODO(gri) needs to print updated name to avoid major confusion in error message!
// (print warning for now)
// Old warning:
// check.softErrorf(pos, "%s does not implement %s (warning: name not updated) = %s (missing method %s)", V, T, Ti, m)
if wrong != nil {
// TODO(gri) This can still report uninstantiated types which makes the error message
// more difficult to read then necessary.
// TODO(rFindley) should this use parentheses rather than ':' for qualification?
return errorf("%s does not implement %s: wrong method signature\n\tgot %s\n\twant %s",
V, T, wrong, m,
)
if m, wrong := check.missingMethod(V, Ti, true); m != nil /* !Implements(V, Ti) */ {
if check != nil && compilerErrorMessages {
return errorf("%s does not implement %s %s", V, T, check.missingMethodReason(V, T, m, wrong))
}
return errorf("%s does not implement %s (missing method %s)", V, T, m.name)
var cause string
if wrong != nil {
if Identical(m.typ, wrong.typ) {
cause = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
} else {
cause = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrong.typ, m.typ)
}
} else {
cause = "missing method " + m.Name()
}
return errorf("%s does not implement %s: %s", V, T, cause)
}
}

View File

@ -47,7 +47,7 @@ func (T) m1()
func (*T) m2()
func _() {
f2[T /* ERROR wrong method signature */ ]()
f2[T /* ERROR m2 has pointer receiver */ ]()
f2[*T]()
}