Skip to content

Commit 71a6718

Browse files
committed
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 #38475 Change-Id: I8b780b7e1f1f433a0efe670de3b1437053f42fba Reviewed-on: https://go-review.googlesource.com/c/go/+/228457 Run-TryBot: Rebecca Stambler <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Robert Griesemer <[email protected]>
1 parent 4eaf855 commit 71a6718

File tree

4 files changed

+21
-14
lines changed

4 files changed

+21
-14
lines changed

src/go/types/expr.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,12 +1568,12 @@ func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface,
15681568
}
15691569

15701570
var msg string
1571-
if wrongType {
1572-
msg = "wrong type for method"
1571+
if wrongType != nil {
1572+
msg = fmt.Sprintf("wrong type for method %s (have %s, want %s)", method.name, wrongType.typ, method.typ)
15731573
} else {
1574-
msg = "missing method"
1574+
msg = "missing method " + method.name
15751575
}
1576-
check.errorf(pos, "%s cannot have dynamic type %s (%s %s)", x, T, msg, method.name)
1576+
check.errorf(pos, "%s cannot have dynamic type %s (%s)", x, T, msg)
15771577
}
15781578

15791579
func (check *Checker) singleValue(x *operand) {

src/go/types/lookup.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,14 +263,17 @@ func (check *Checker) lookupType(m map[Type]int, typ Type) (int, bool) {
263263
// x is of interface type V).
264264
//
265265
func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
266-
return (*Checker)(nil).missingMethod(V, T, static)
266+
m, typ := (*Checker)(nil).missingMethod(V, T, static)
267+
return m, typ != nil
267268
}
268269

269270
// missingMethod is like MissingMethod but accepts a receiver.
270271
// The receiver may be nil if missingMethod is invoked through
271272
// an exported API call (such as MissingMethod), i.e., when all
272273
// methods have been type-checked.
273-
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
274+
// If the type has the correctly names method, but with the wrong
275+
// signature, the existing method is returned as well.
276+
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) {
274277
check.completeInterface(T)
275278

276279
// fast path for common case
@@ -286,10 +289,10 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *
286289
switch {
287290
case obj == nil:
288291
if static {
289-
return m, false
292+
return m, nil
290293
}
291294
case !check.identical(obj.Type(), m.typ):
292-
return m, true
295+
return m, obj
293296
}
294297
}
295298
return
@@ -302,7 +305,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *
302305
// we must have a method (not a field of matching function type)
303306
f, _ := obj.(*Func)
304307
if f == nil {
305-
return m, false
308+
return m, nil
306309
}
307310

308311
// 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 *
311314
}
312315

313316
if !check.identical(f.typ, m.typ) {
314-
return m, true
317+
return m, f
315318
}
316319
}
317320

@@ -323,7 +326,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method *
323326
// method required by V and whether it is missing or just has the wrong type.
324327
// The receiver may be nil if assertableTo is invoked through an exported API call
325328
// (such as AssertableTo), i.e., when all methods have been type-checked.
326-
func (check *Checker) assertableTo(V *Interface, T Type) (method *Func, wrongType bool) {
329+
func (check *Checker) assertableTo(V *Interface, T Type) (method, wrongType *Func) {
327330
// no static check is required if T is an interface
328331
// spec: "If T is an interface type, x.(T) asserts that the
329332
// dynamic type of x implements the interface T."

src/go/types/operand.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package types
88

99
import (
1010
"bytes"
11+
"fmt"
1112
"go/ast"
1213
"go/constant"
1314
"go/token"
@@ -254,8 +255,8 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
254255
if Ti, ok := Tu.(*Interface); ok {
255256
if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ {
256257
if reason != nil {
257-
if wrongType {
258-
*reason = "wrong type for method " + m.Name()
258+
if wrongType != nil {
259+
*reason = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrongType.typ, m.typ)
259260
} else {
260261
*reason = "missing method " + m.Name()
261262
}

src/go/types/testdata/issues.src

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ func issue10260() {
129129
t1 *T1
130130
t2 *T2
131131
)
132+
133+
_ = i2 /* ERROR i2 .* cannot have dynamic type \*T1 \(wrong type for method foo \(have func\(\), want func\(x int\)\)\) */ .(*T1)
134+
132135
i1 = i0 /* ERROR cannot use .* missing method foo */
133136
i1 = t0 /* ERROR cannot use .* missing method foo */
134137
i1 = i2 /* ERROR cannot use .* wrong type for method foo */
@@ -146,7 +149,7 @@ func issue10260() {
146149
// a few more - less exhaustive now
147150

148151
f := func(I1, I2){}
149-
f(i0 /* ERROR cannot use .* missing method foo */ , i1 /* ERROR cannot use .* wrong type for method foo */)
152+
f(i0 /* ERROR cannot use .* missing method foo */ , i1 /* ERROR cannot use .* wrong type for method foo \(have func\(\), want func\(x int\)\) */ )
150153

151154
_ = [...]I1{i0 /* ERROR cannot use .* missing method foo */ }
152155
_ = [...]I1{i2 /* ERROR cannot use .* wrong type for method foo */ }

0 commit comments

Comments
 (0)