]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types, types2: fix bug in types2.MissingMethod
authorRobert Griesemer <gri@golang.org>
Thu, 3 Feb 2022 00:31:26 +0000 (16:31 -0800)
committerRobert Griesemer <gri@golang.org>
Fri, 4 Feb 2022 23:42:43 +0000 (23:42 +0000)
Because Checker.missingMethod also looks up methods matching
matching case-folded names, when Checker.missingMethod returns
an alternative method, that method does not automatically have
the wrong type. It may be a method with a different name.

Adjust types2.MissingMethod to check the alternative method
name before reporting a wrong type.

Add API test that verifies (now correct) behavior for this
case.

Ported the code also to go/types, though it was not a bug
there yet because looking up with case-folding is not yet
enabled.

Change-Id: Iaa48808535c9265a9879338ea666c6c021e93a2b
Reviewed-on: https://go-review.googlesource.com/c/go/+/382634
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/api_test.go
src/cmd/compile/internal/types2/lookup.go
src/go/types/api_test.go
src/go/types/lookup.go

index 80e998ebeea3a2ec4f26b6a56f8ae39171e2cbc9..094374f7f1b5b0a9689ed16dd55a5e608acf787c 100644 (file)
@@ -2369,3 +2369,61 @@ type Bad Bad // invalid type
                }
        }
 }
+
+func TestMissingMethodAlternative(t *testing.T) {
+       const src = `
+package p
+type T interface {
+       m()
+}
+
+type V0 struct{}
+func (V0) m() {}
+
+type V1 struct{}
+
+type V2 struct{}
+func (V2) m() int
+
+type V3 struct{}
+func (*V3) m()
+
+type V4 struct{}
+func (V4) M()
+`
+
+       pkg, err := pkgFor("p.go", src, nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       T := pkg.Scope().Lookup("T").Type().Underlying().(*Interface)
+       lookup := func(name string) (*Func, bool) {
+               return MissingMethod(pkg.Scope().Lookup(name).Type(), T, true)
+       }
+
+       // V0 has method m with correct signature. Should not report wrongType.
+       method, wrongType := lookup("V0")
+       if method != nil || wrongType {
+               t.Fatalf("V0: got method = %v, wrongType = %v", method, wrongType)
+       }
+
+       checkMissingMethod := func(tname string, reportWrongType bool) {
+               method, wrongType := lookup(tname)
+               if method == nil || method.Name() != "m" || wrongType != reportWrongType {
+                       t.Fatalf("%s: got method = %v, wrongType = %v", tname, method, wrongType)
+               }
+       }
+
+       // V1 has no method m. Should not report wrongType.
+       checkMissingMethod("V1", false)
+
+       // V2 has method m with wrong signature type (ignoring receiver). Should report wrongType.
+       checkMissingMethod("V2", true)
+
+       // V3 has no method m but it exists on *V3. Should report wrongType.
+       checkMissingMethod("V3", true)
+
+       // V4 has no method m but has M. Should not report wrongType.
+       checkMissingMethod("V4", false)
+}
index 80f085803eb3a15607a431000acc8c2530962660..fc6b34941ab11c8e71e15222df2471d92b2b5b2d 100644 (file)
@@ -281,8 +281,9 @@ func 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) {
-       m, typ := (*Checker)(nil).missingMethod(V, T, static)
-       return m, typ != nil
+       m, alt := (*Checker)(nil).missingMethod(V, T, static)
+       // Only report a wrong type if the alternative method has the same name as m.
+       return m, alt != nil && alt.name == m.name // alt != nil implies m != nil
 }
 
 // missingMethod is like MissingMethod but accepts a *Checker as receiver.
index 5f4d48472cec39c9fb5be92c4547e73dc1a83319..a18ee16c7b57640ea988d3d8716e514f847ac07a 100644 (file)
@@ -2362,3 +2362,61 @@ type Bad Bad // invalid type
                }
        }
 }
+
+func TestMissingMethodAlternative(t *testing.T) {
+       const src = `
+package p
+type T interface {
+       m()
+}
+
+type V0 struct{}
+func (V0) m() {}
+
+type V1 struct{}
+
+type V2 struct{}
+func (V2) m() int
+
+type V3 struct{}
+func (*V3) m()
+
+type V4 struct{}
+func (V4) M()
+`
+
+       pkg, err := pkgFor("p.go", src, nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       T := pkg.Scope().Lookup("T").Type().Underlying().(*Interface)
+       lookup := func(name string) (*Func, bool) {
+               return MissingMethod(pkg.Scope().Lookup(name).Type(), T, true)
+       }
+
+       // V0 has method m with correct signature. Should not report wrongType.
+       method, wrongType := lookup("V0")
+       if method != nil || wrongType {
+               t.Fatalf("V0: got method = %v, wrongType = %v", method, wrongType)
+       }
+
+       checkMissingMethod := func(tname string, reportWrongType bool) {
+               method, wrongType := lookup(tname)
+               if method == nil || method.Name() != "m" || wrongType != reportWrongType {
+                       t.Fatalf("%s: got method = %v, wrongType = %v", tname, method, wrongType)
+               }
+       }
+
+       // V1 has no method m. Should not report wrongType.
+       checkMissingMethod("V1", false)
+
+       // V2 has method m with wrong signature type (ignoring receiver). Should report wrongType.
+       checkMissingMethod("V2", true)
+
+       // V3 has no method m but it exists on *V3. Should report wrongType.
+       checkMissingMethod("V3", true)
+
+       // V4 has no method m but has M. Should not report wrongType.
+       checkMissingMethod("V4", false)
+}
index b08308088cffb7eaf7471aba0cc9a2a7e06214e5..77e8fe9df50713a1d4eded63f11d8d11094acaa7 100644 (file)
@@ -281,8 +281,9 @@ func 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) {
-       m, typ := (*Checker)(nil).missingMethod(V, T, static)
-       return m, typ != nil
+       m, alt := (*Checker)(nil).missingMethod(V, T, static)
+       // Only report a wrong type if the alternative method has the same name as m.
+       return m, alt != nil && alt.name == m.name // alt != nil implies m != nil
 }
 
 // missingMethod is like MissingMethod but accepts a *Checker as receiver.