]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: add detail to missing method error messages
authorRebecca Stambler <rstambler@golang.org>
Wed, 15 Apr 2020 22:31:29 +0000 (18:31 -0400)
committerRebecca Stambler <rstambler@golang.org>
Thu, 16 Apr 2020 16:44:24 +0000 (16:44 +0000)
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>
src/go/types/expr.go
src/go/types/lookup.go
src/go/types/operand.go
src/go/types/testdata/issues.src

index 165778c2f76ad7301dc1e44a0ebdf2301ed230ed..f88b2389c6a4b0d3cea90f2b6ec0739c5ead83e9 100644 (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) {
index 342c8baab24f2628662feffc8f8b5f31431abd9c..d774dd5d5cca3a4cb02a5dbcb4525922618673ed 100644 (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."
index d0e7531d4f316d0a04b4b59727914af3a1a2adac..a762ad9bc8bad8106b68135207ac24eac7080cec 100644 (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()
                                }
index fe2407999c5e9e4208467fc3733e316f3c98e021..f8d037b99eafa7a6d5892839761c5f8e08176acd 100644 (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 */ }