Skip to content

Commit

Permalink
cmd/compile: add missing method info for method with correct name exc…
Browse files Browse the repository at this point in the history
…ept for case

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>
  • Loading branch information
danscales committed Nov 12, 2021
1 parent c8d6ee1 commit 429d1e0
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 12 deletions.
48 changes: 38 additions & 10 deletions src/cmd/compile/internal/types2/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ 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
}
return
}
}

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
Expand All @@ -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 == "_" {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
4 changes: 2 additions & 2 deletions test/fixedbugs/issue48471.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 429d1e0

Please sign in to comment.