]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: improve error message for pointer receiver errors
authorRebecca Stambler <rstambler@golang.org>
Fri, 24 Apr 2020 05:01:48 +0000 (01:01 -0400)
committerRebecca Stambler <rstambler@golang.org>
Fri, 24 Apr 2020 18:49:16 +0000 (18:49 +0000)
The compiler produces high quality error messages when an interface is
implemented by *T, rather than T. This change improves the analogous
error messages in go/types, from "missing method X" to "missing method
X (X has pointer receiver)".

I am open to improving this message further - I didn't copy the compiler
error message exactly because, at one of the call sites of
(*check).missingMethod, we no longer have access to the name of the
interface.

Fixes golang/go#36336

Change-Id: Ic4fc38b13fff9e5d9a69cc750c21e0b0c34d85a8
Reviewed-on: https://go-review.googlesource.com/c/go/+/229801
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 f88b2389c6a4b0d3cea90f2b6ec0739c5ead83e9..d1e892a9b70d8a9af3de2f194640e6de4b745c9c 100644 (file)
@@ -1566,10 +1566,13 @@ func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface,
        if method == nil {
                return
        }
-
        var msg string
        if wrongType != nil {
-               msg = fmt.Sprintf("wrong type for method %s (have %s, want %s)", method.name, wrongType.typ, method.typ)
+               if check.identical(method.typ, wrongType.typ) {
+                       msg = fmt.Sprintf("missing method %s (%s has pointer receiver)", method.name, method.name)
+               } else {
+                       msg = fmt.Sprintf("wrong type for method %s (have %s, want %s)", method.name, wrongType.typ, method.typ)
+               }
        } else {
                msg = "missing method " + method.name
        }
index d774dd5d5cca3a4cb02a5dbcb4525922618673ed..3c9ff182ec7c2446628ad857fa9264dbe2126115 100644 (file)
@@ -271,8 +271,10 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b
 // 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.
-// If the type has the correctly names method, but with the wrong
+// If the type has the correctly named method, but with the wrong
 // signature, the existing method is returned as well.
+// To improve error messages, also report the wrong signature
+// when the method exists on *V instead of V.
 func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) {
        check.completeInterface(T)
 
@@ -302,6 +304,15 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
        for _, m := range T.allMethods {
                obj, _, _ := check.rawLookupFieldOrMethod(V, false, m.pkg, m.name)
 
+               // Check if *V implements this method of T.
+               if obj == nil {
+                       ptr := NewPointer(V)
+                       obj, _, _ = check.rawLookupFieldOrMethod(ptr, false, m.pkg, m.name)
+                       if obj != nil {
+                               return m, obj.(*Func)
+                       }
+               }
+
                // we must have a method (not a field of matching function type)
                f, _ := obj.(*Func)
                if f == nil {
index eb49e6b1dc34940be70dbd36713e78ecbc270675..80d11e2f2153019a225b126dd0c12cbf9aafd612 100644 (file)
@@ -266,7 +266,12 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) bool {
                if m, wrongType := check.missingMethod(V, Ti, true); m != nil /* Implements(V, Ti) */ {
                        if reason != nil {
                                if wrongType != nil {
-                                       *reason = fmt.Sprintf("wrong type for method %s (have %s, want %s)", m.Name(), wrongType.typ, m.typ)
+                                       if check.identical(m.typ, wrongType.typ) {
+                                               *reason = fmt.Sprintf("missing method %s (%s has pointer receiver)", m.name, m.name)
+                                       } else {
+                                               *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 f8d037b99eafa7a6d5892839761c5f8e08176acd..6cf4420e51f70fb3a3422d404763071a1beb9135 100644 (file)
@@ -130,6 +130,9 @@ func issue10260() {
                t2 *T2
        )
 
+       var x I1 = T1 /* ERROR cannot use .*: missing method foo \(foo has pointer receiver\) */ {}
+       _ = x /* ERROR .* cannot have dynamic type T1 \(missing method foo \(foo has pointer receiver\)\) */ .(T1)
+
        _ = 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 */
@@ -355,4 +358,4 @@ func issue35895() {
        // Because both t1 and t2 have the same global package name (template),
        // qualify packages with full path name in this case.
        var _ t1.Template = t2 /* ERROR cannot use .* \(value of type "html/template".Template\) as "text/template".Template */ .Template{}
-}
\ No newline at end of file
+}