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

go/types, types2: always use missingMethodReason in checker.Implements

Remove special case where we don't have a *Checker and always use
Checker.missingMethodReason in Checker.implements.

Look for zero methods rather than empty interface to exit early
from Checker.missingMethod, and remove the extra test in
Checker.implements.

With this change we get consistent and more detailed error messages
from all places where we do a form of the "implements" test.

To make this possible, allow for the receiver to be nil in
- Checker.sprintf
- Checker.missingMethodReason
- Checker.interfacePtrError
- Checker.funcString

Allowing Checker.sprintf with nil Checker permits further simplifying
in a couple of places.

Change-Id: I0ea7178c9efbcd4a25ded2a66e2b058db52dc4d4
Reviewed-on: https://go-review.googlesource.com/c/go/+/383054
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
Robert Griesemer 2022-02-03 16:07:28 -08:00
parent f1903fd4ec
commit aadbfc30af
10 changed files with 90 additions and 107 deletions

View File

@ -167,8 +167,13 @@ func (check *Checker) markImports(pkg *Package) {
}
}
// check may be nil.
func (check *Checker) sprintf(format string, args ...interface{}) string {
return sprintf(check.qualifier, false, format, args...)
var qf Qualifier
if check != nil {
qf = check.qualifier
}
return sprintf(qf, false, format, args...)
}
func (check *Checker) report(err *error_) {

View File

@ -160,21 +160,17 @@ func (check *Checker) implements(V, T Type) error {
return nil // avoid follow-on errors (see issue #49541 for an example)
}
var qf Qualifier
if check != nil {
qf = check.qualifier
}
errorf := func(format string, args ...interface{}) error {
return errors.New(sprintf(qf, false, format, args...))
return errors.New(check.sprintf(format, args...))
}
Ti, _ := Tu.(*Interface)
if Ti == nil {
var cause string
if isInterfacePtr(Tu) {
cause = sprintf(qf, false, "type %s is pointer to interface, not interface", T)
cause = check.sprintf("type %s is pointer to interface, not interface", T)
} else {
cause = sprintf(qf, false, "%s is not an interface", T)
cause = check.sprintf("%s is not an interface", T)
}
return errorf("%s does not implement %s (%s)", V, T, cause)
}
@ -199,24 +195,9 @@ func (check *Checker) implements(V, T Type) error {
}
// V must implement T's methods, if any.
if Ti.NumMethods() > 0 {
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))
}
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)
}
}
// If T is comparable, V must be comparable.
// Remember as a pending error and report only if we don't have a more specific error.

View File

@ -297,7 +297,7 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b
// as the second result.
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, alt *Func) {
// fast path for common case
if T.Empty() {
if T.NumMethods() == 0 {
return
}
@ -368,9 +368,10 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// and may include more have/want info after that. If non-nil, alt is a relevant
// method that matches in some way. It may have the correct name, but wrong type, or
// it may have a pointer receiver, or it may have the correct name except wrong case.
// check may be nil.
func (check *Checker) missingMethodReason(V, T Type, m, alt *Func) string {
var mname string
if check.conf.CompilerErrorMessages {
if check != nil && check.conf.CompilerErrorMessages {
mname = m.Name() + " method"
} else {
mname = "method " + m.Name()
@ -406,6 +407,7 @@ func isInterfacePtr(T Type) bool {
return p != nil && IsInterface(p.base)
}
// check may be nil.
func (check *Checker) interfacePtrError(T Type) string {
assert(isInterfacePtr(T))
if p, _ := under(T).(*Pointer); isTypeParam(p.base) {
@ -415,9 +417,14 @@ func (check *Checker) interfacePtrError(T Type) string {
}
// funcString returns a string of the form name + signature for f.
// check may be nil.
func (check *Checker) funcString(f *Func) string {
buf := bytes.NewBufferString(f.name)
WriteSignature(buf, f.typ.(*Signature), check.qualifier)
var qf Qualifier
if check != nil {
qf = check.qualifier
}
WriteSignature(buf, f.typ.(*Signature), qf)
return buf.String()
}

View File

@ -131,7 +131,7 @@ func issue10260() {
)
var x I1
x = T1 /* ERROR cannot use .*: missing method foo \(foo has pointer receiver\) */ {}
x = T1 /* ERROR cannot use T1{} .* as I1 value in assignment: T1 does not implement I1 \(method foo has pointer receiver\) */ {}
_ = x /* ERROR impossible type assertion: x\.\(T1\)\n\tT1 does not implement I1 \(method foo has pointer receiver\) */ .(T1)
T1{}.foo /* ERROR cannot call pointer method foo on T1 */ ()
@ -139,34 +139,34 @@ func issue10260() {
_ = i2 /* ERROR impossible type assertion: i2\.\(\*T1\)\n\t\*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(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 */
i1 = t2 /* ERROR cannot use .* wrong type for method foo */
i2 = i1 /* ERROR cannot use .* wrong type for method foo */
i2 = t1 /* ERROR cannot use .* wrong type for method foo */
i1 = i0 /* ERROR cannot use i0 .* as I1 value in assignment: I0 does not implement I1 \(missing method foo\) */
i1 = t0 /* ERROR .* t0 .* as I1 .*: \*T0 does not implement I1 \(missing method foo\) */
i1 = i2 /* ERROR .* i2 .* as I1 .*: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */
i1 = t2 /* ERROR .* t2 .* as I1 .*: \*T2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */
i2 = i1 /* ERROR .* i1 .* as I2 .*: I1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */
i2 = t1 /* ERROR .* t1 .* as I2 .*: \*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */
_ = func() I1 { return i0 /* ERROR cannot use .* missing method foo */ }
_ = func() I1 { return t0 /* ERROR cannot use .* missing method foo */ }
_ = func() I1 { return i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I1 { return t2 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I2 { return i1 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I2 { return t1 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I1 { return i0 /* ERROR cannot use i0 .* as I1 value in return statement: I0 does not implement I1 \(missing method foo\) */ }
_ = func() I1 { return t0 /* ERROR .* t0 .* as I1 .*: \*T0 does not implement I1 \(missing method foo\) */ }
_ = func() I1 { return i2 /* ERROR .* i2 .* as I1 .*: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ }
_ = func() I1 { return t2 /* ERROR .* t2 .* as I1 .*: \*T2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ }
_ = func() I2 { return i1 /* ERROR .* i1 .* as I2 .*: I1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ }
_ = func() I2 { return t1 /* ERROR .* t1 .* as I2 .*: \*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ }
// 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 \(have func\(\), want func\(x int\)\) */ )
f(i0 /* ERROR missing method foo */ , i1 /* ERROR wrong type for method foo */ )
_ = [...]I1{i0 /* ERROR cannot use .* missing method foo */ }
_ = [...]I1{i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = []I1{i0 /* ERROR cannot use .* missing method foo */ }
_ = []I1{i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = map[int]I1{0: i0 /* ERROR cannot use .* missing method foo */ }
_ = map[int]I1{0: i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = [...]I1{i0 /* ERROR cannot use i0 .* as I1 value in array or slice literal: I0 does not implement I1 \(missing method foo\) */ }
_ = [...]I1{i2 /* ERROR cannot use i2 .* as I1 value in array or slice literal: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ }
_ = []I1{i0 /* ERROR missing method foo */ }
_ = []I1{i2 /* ERROR wrong type for method foo */ }
_ = map[int]I1{0: i0 /* ERROR missing method foo */ }
_ = map[int]I1{0: i2 /* ERROR wrong type for method foo */ }
make(chan I1) <- i0 /* ERROR I0 does not implement I1: missing method foo */
make(chan I1) <- i2 /* ERROR wrong type for method foo \(have func\(x int\), want func\(\)\) */
make(chan I1) <- i0 /* ERROR missing method foo */
make(chan I1) <- i2 /* ERROR wrong type for method foo */
}
// Check that constants representable as integers are in integer form

View File

@ -9,7 +9,7 @@ type I[F any] interface {
}
func G[F any]() I[any] {
return g /* ERROR "missing method Q \(Q has pointer receiver\)" */ [F]{}
return g /* ERROR cannot use g\[F\]{} .* as I\[any\] value in return statement: g\[F\] does not implement I\[any\] \(method Q has pointer receiver\) */ [F]{}
}
type g[F any] struct{}

View File

@ -63,8 +63,15 @@ func (check *Checker) markImports(pkg *Package) {
}
}
// check may be nil.
func (check *Checker) sprintf(format string, args ...any) string {
return sprintf(check.fset, check.qualifier, false, format, args...)
var fset *token.FileSet
var qf Qualifier
if check != nil {
fset = check.fset
qf = check.qualifier
}
return sprintf(fset, qf, false, format, args...)
}
func sprintf(fset *token.FileSet, qf Qualifier, debug bool, format string, args ...any) string {

View File

@ -160,25 +160,17 @@ func (check *Checker) implements(V, T Type) error {
return nil // avoid follow-on errors (see issue #49541 for an example)
}
var qf Qualifier
if check != nil {
qf = check.qualifier
}
errorf := func(format string, args ...any) error {
return errors.New(sprintf(nil, qf, false, format, args...))
return errors.New(check.sprintf(format, args...))
}
Ti, _ := Tu.(*Interface)
if Ti == nil {
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)
cause = check.sprintf("type %s is pointer to interface, not interface", T)
} else {
cause = sprintf(fset, qf, false, "%s is not an interface", T)
cause = check.sprintf("%s is not an interface", T)
}
return errorf("%s does not implement %s (%s)", V, T, cause)
}
@ -203,24 +195,9 @@ func (check *Checker) implements(V, T Type) error {
}
// V must implement T's methods, if any.
if Ti.NumMethods() > 0 {
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))
}
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)
}
}
// If T is comparable, V must be comparable.
// Remember as a pending error and report only if we don't have a more specific error.

View File

@ -298,7 +298,7 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b
// Note: case-folding lookup is currently disabled
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, alt *Func) {
// fast path for common case
if T.Empty() {
if T.NumMethods() == 0 {
return
}
@ -370,9 +370,10 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
// and may include more have/want info after that. If non-nil, alt is a relevant
// method that matches in some way. It may have the correct name, but wrong type, or
// it may have a pointer receiver, or it may have the correct name except wrong case.
// check may be nil.
func (check *Checker) missingMethodReason(V, T Type, m, alt *Func) string {
var mname string
if compilerErrorMessages {
if check != nil && compilerErrorMessages {
mname = m.Name() + " method"
} else {
mname = "method " + m.Name()
@ -408,6 +409,7 @@ func isInterfacePtr(T Type) bool {
return p != nil && IsInterface(p.base)
}
// check may be nil.
func (check *Checker) interfacePtrError(T Type) string {
assert(isInterfacePtr(T))
if p, _ := under(T).(*Pointer); isTypeParam(p.base) {
@ -416,10 +418,14 @@ func (check *Checker) interfacePtrError(T Type) string {
return check.sprintf("type %s is pointer to interface, not interface", T)
}
// funcString returns a string of the form name + signature for f.
// check may be nil.
func (check *Checker) funcString(f *Func) string {
buf := bytes.NewBufferString(f.name)
WriteSignature(buf, f.typ.(*Signature), check.qualifier)
var qf Qualifier
if check != nil {
qf = check.qualifier
}
WriteSignature(buf, f.typ.(*Signature), qf)
return buf.String()
}

View File

@ -131,7 +131,7 @@ func issue10260() {
)
var x I1
x = T1 /* ERROR cannot use .*: missing method foo \(foo has pointer receiver\) */ {}
x = T1 /* ERROR cannot use \(T1 literal\) .* as I1 value in assignment: T1 does not implement I1 \(method foo has pointer receiver\) */ {}
_ = x /* ERROR impossible type assertion: x\.\(T1\)\n\tT1 does not implement I1 \(method foo has pointer receiver\) */ .(T1)
T1{}.foo /* ERROR cannot call pointer method foo on T1 */ ()
@ -139,34 +139,34 @@ func issue10260() {
_ = i2 /* ERROR impossible type assertion: i2\.\(\*T1\)\n\t\*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(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 */
i1 = t2 /* ERROR cannot use .* wrong type for method foo */
i2 = i1 /* ERROR cannot use .* wrong type for method foo */
i2 = t1 /* ERROR cannot use .* wrong type for method foo */
i1 = i0 /* ERROR cannot use i0 .* as I1 value in assignment: I0 does not implement I1 \(missing method foo\) */
i1 = t0 /* ERROR .* t0 .* as I1 .*: \*T0 does not implement I1 \(missing method foo\) */
i1 = i2 /* ERROR .* i2 .* as I1 .*: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */
i1 = t2 /* ERROR .* t2 .* as I1 .*: \*T2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */
i2 = i1 /* ERROR .* i1 .* as I2 .*: I1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */
i2 = t1 /* ERROR .* t1 .* as I2 .*: \*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */
_ = func() I1 { return i0 /* ERROR cannot use .* missing method foo */ }
_ = func() I1 { return t0 /* ERROR cannot use .* missing method foo */ }
_ = func() I1 { return i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I1 { return t2 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I2 { return i1 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I2 { return t1 /* ERROR cannot use .* wrong type for method foo */ }
_ = func() I1 { return i0 /* ERROR cannot use i0 .* as I1 value in return statement: I0 does not implement I1 \(missing method foo\) */ }
_ = func() I1 { return t0 /* ERROR .* t0 .* as I1 .*: \*T0 does not implement I1 \(missing method foo\) */ }
_ = func() I1 { return i2 /* ERROR .* i2 .* as I1 .*: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ }
_ = func() I1 { return t2 /* ERROR .* t2 .* as I1 .*: \*T2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ }
_ = func() I2 { return i1 /* ERROR .* i1 .* as I2 .*: I1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ }
_ = func() I2 { return t1 /* ERROR .* t1 .* as I2 .*: \*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ }
// 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 \(have func\(\), want func\(x int\)\) */ )
f(i0 /* ERROR missing method foo */ , i1 /* ERROR wrong type for method foo */ )
_ = [...]I1{i0 /* ERROR cannot use .* missing method foo */ }
_ = [...]I1{i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = []I1{i0 /* ERROR cannot use .* missing method foo */ }
_ = []I1{i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = map[int]I1{0: i0 /* ERROR cannot use .* missing method foo */ }
_ = map[int]I1{0: i2 /* ERROR cannot use .* wrong type for method foo */ }
_ = [...]I1{i0 /* ERROR cannot use i0 .* as I1 value in array or slice literal: I0 does not implement I1 \(missing method foo\) */ }
_ = [...]I1{i2 /* ERROR cannot use i2 .* as I1 value in array or slice literal: I2 does not implement I1 \(wrong type for method foo\)\n\t\thave foo\(x int\)\n\t\twant foo\(\) */ }
_ = []I1{i0 /* ERROR missing method foo */ }
_ = []I1{i2 /* ERROR wrong type for method foo */ }
_ = map[int]I1{0: i0 /* ERROR missing method foo */ }
_ = map[int]I1{0: i2 /* ERROR wrong type for method foo */ }
make(chan I1) <- i0 /* ERROR I0 does not implement I1: missing method foo */
make(chan I1) <- i2 /* ERROR wrong type for method foo \(have func\(x int\), want func\(\)\) */
make(chan I1) <- i0 /* ERROR missing method foo */
make(chan I1) <- i2 /* ERROR wrong type for method foo */
}
// Check that constants representable as integers are in integer form

View File

@ -9,7 +9,7 @@ type I[F any] interface {
}
func G[F any]() I[any] {
return g /* ERROR "missing method Q \(Q has pointer receiver\)" */ [F]{}
return g /* ERROR cannot use \(g\[F\] literal\) .* as I\[any\] value in return statement: g\[F\] does not implement I\[any\] \(method Q has pointer receiver\) */ [F]{}
}
type g[F any] struct{}