]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile: add missing method info for method with correct name except for case
authorDan Scales <danscales@google.com>
Thu, 11 Nov 2021 18:28:17 +0000 (10:28 -0800)
committerDan Scales <danscales@google.com>
Fri, 12 Nov 2021 23:07:14 +0000 (23:07 +0000)
When being used by the compiler, augment the types2 missing method
message with extra info, if a method is missing, but a method with the
correct name except for case (i.e. equal via string.EqualFold()) is
present. In that case, print out the wanted method and the method that
is present (that has the wrong case).

In the 1.17 compiler, we don't do this case-folding check when assigning
an interface to an interface, so I didn't add that check, but we could
add that.

Fixes #48471

Change-Id: Ic54549c1f66297c9221d979d49c1daa719aa66cd
Reviewed-on: https://go-review.googlesource.com/c/go/+/363437
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/cmd/compile/internal/types2/lookup.go
test/fixedbugs/issue48471.go

index a05a5d6397d214bae617eb9dc928863c8df03279..8ed5ca837a01713a389e323efa4d0f906ca04ab6 100644 (file)
@@ -52,7 +52,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
        // not have found it for T (see also issue 8590).
        if t := asNamed(T); t != nil {
                if p, _ := safeUnderlying(t).(*Pointer); p != nil {
-                       obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name)
+                       obj, index, indirect = lookupFieldOrMethod(p, false, false, pkg, name)
                        if _, ok := obj.(*Func); ok {
                                return nil, nil, false
                        }
@@ -60,7 +60,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
                }
        }
 
-       return lookupFieldOrMethod(T, addressable, pkg, name)
+       return lookupFieldOrMethod(T, addressable, false, pkg, name)
 }
 
 // TODO(gri) The named type consolidation and seen maps below must be
@@ -69,7 +69,9 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
 //           indirectly via different packages.)
 
 // lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod.
-func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+// If checkFold is true, the lookup for methods will include looking for any method
+// which case-folds to the same as 'name' (used for giving helpful error messages).
+func lookupFieldOrMethod(T Type, addressable, checkFold bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
        // WARNING: The code in this function is extremely subtle - do not modify casually!
 
        if name == "_" {
@@ -127,7 +129,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
                                seen[named] = true
 
                                // look for a matching attached method
-                               if i, m := lookupMethod(named.methods, pkg, name); m != nil {
+                               if i, m := lookupMethodFold(named.methods, pkg, name, checkFold); m != nil {
                                        // potential match
                                        // caution: method may not have a proper signature yet
                                        index = concat(e.index, i)
@@ -178,7 +180,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
 
                        case *Interface:
                                // look for a matching method
-                               if i, m := t.typeSet().LookupMethod(pkg, name); m != nil {
+                               if i, m := lookupMethodFold(t.typeSet().methods, pkg, name, checkFold); m != nil {
                                        assert(m.typ != nil)
                                        index = concat(e.index, i)
                                        if obj != nil || e.multiples {
@@ -189,7 +191,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
                                }
 
                        case *TypeParam:
-                               if i, m := t.iface().typeSet().LookupMethod(pkg, name); m != nil {
+                               if i, m := lookupMethodFold(t.iface().typeSet().methods, pkg, name, checkFold); m != nil {
                                        assert(m.typ != nil)
                                        index = concat(e.index, i)
                                        if obj != nil || e.multiples {
@@ -315,6 +317,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
                                if !static {
                                        continue
                                }
+                               // We don't do any case-fold check if V is an interface.
                                return m, f
                        }
 
@@ -345,13 +348,20 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
 
        // A concrete type implements T if it implements all methods of T.
        for _, m := range T.typeSet().methods {
-               // TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)?
-               obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name)
+               // TODO(gri) should this be calling LookupFieldOrMethod instead (and why not)?
+               obj, _, _ := lookupFieldOrMethod(V, false, false, m.pkg, m.name)
 
                // Check if *V implements this method of T.
                if obj == nil {
                        ptr := NewPointer(V)
-                       obj, _, _ = lookupFieldOrMethod(ptr, false, m.pkg, m.name)
+                       obj, _, _ = lookupFieldOrMethod(ptr, false, false, m.pkg, m.name)
+                       if obj != nil {
+                               return m, obj.(*Func)
+                       }
+                       // If we didn't find the exact method (even with pointer
+                       // receiver), look to see if there is a method that
+                       // matches m.name with case-folding.
+                       obj, _, _ := lookupFieldOrMethod(V, false, true, m.pkg, m.name)
                        if obj != nil {
                                return m, obj.(*Func)
                        }
@@ -410,7 +420,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
 // where m is missing from V, but required by T. It puts the reason in parentheses,
 // and may include more have/want info after that. If non-nil, wrongType 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.
+// it may have a pointer receiver, or it may have the correct name except wrong case.
 func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string {
        var r string
        var mname string
@@ -527,3 +537,21 @@ func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
        }
        return -1, nil
 }
+
+// lookupMethodFold is like lookupMethod, but if checkFold is true, it matches a method
+// name if the names are equal with case folding.
+func lookupMethodFold(methods []*Func, pkg *Package, name string, checkFold bool) (int, *Func) {
+       if name != "_" {
+               for i, m := range methods {
+                       if m.name != name && !(checkFold && strings.EqualFold(m.name, name)) {
+                               continue
+                       }
+                       // Use m.name, since we've already checked that m.name and
+                       // name are equal with folding.
+                       if m.sameId(pkg, m.name) {
+                               return i, m
+                       }
+               }
+       }
+       return -1, nil
+}
index 0412d23b99cde1ea48cccd537c780e3a954950ed..2e00c87c6a93676a223d0ce97240550c47c2870e 100644 (file)
@@ -29,11 +29,11 @@ func g() {
        var i I
        i = new(T)    // ERROR "cannot use new\(T\) \(.*type \*T\) as type I in assignment:\n\t\*T does not implement I \(missing M method\)"
        i = I(new(T)) // ERROR "cannot convert new\(T\) \(.*type \*T\) to type I:\n\t\*T does not implement I \(missing M method\)"
-       i = new(T2)   // ERROR "cannot use new\(T2\) \(.*type \*T2\) as type I in assignment:\n\t\*T2 does not implement I \(missing M method\)"
+       i = new(T2)   // ERROR "cannot use new\(T2\) \(.*type \*T2\) as type I in assignment:\n\t\*T2 does not implement I \(missing M method\)\n\t\thave m\(int\)\n\t\twant M\(int\)"
        i = new(T3)   // ERROR "cannot use new\(T3\) \(.*type \*T3\) as type I in assignment:\n\t\*T3 does not implement I \(wrong type for M method\)\n\t\thave M\(string\)\n\t\twant M\(int\)"
        i = T4{}      // ERROR "cannot use T4\{\} \(.*type T4\) as type I in assignment:\n\tT4 does not implement I \(M method has pointer receiver\)"
        i = new(I)    // ERROR "cannot use new\(I\) \(.*type \*I\) as type I in assignment:\n\t\*I does not implement I \(\*I is pointer to interface, not interface\)"
-       _ = i.(*T2)   // ERROR "impossible type assertion: i.\(\*T2\)\n\t\*T2 does not implement I \(missing M method\)"
+       _ = i.(*T2)   // ERROR "impossible type assertion: i.\(\*T2\)\n\t\*T2 does not implement I \(missing M method\)\n\t\thave m\(int\)\n\t\twant M\(int\)"
        _ = i.(*T3)   // ERROR "impossible type assertion: i.\(\*T3\)\n\t\*T3 does not implement I \(wrong type for M method\)\n\t\thave M\(string\)\n\t\twant M\(int\)"
        var t *T4
        t = i // ERROR "cannot use i \(variable of type I\) as type \*T4 in assignment:\n\tneed type assertion"