mirror of
https://github.com/golang/go
synced 2024-11-26 22:01:27 -07:00
cmd/compile: add missing method info for method with correct name except 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>
This commit is contained in:
parent
c8d6ee12d5
commit
429d1e0155
@ -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
|
||||
}
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user