]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types: fix method set computation if receiver is a named pointer
authorRobert Griesemer <gri@golang.org>
Tue, 6 Jun 2023 20:34:39 +0000 (13:34 -0700)
committerGopher Robot <gobot@golang.org>
Thu, 8 Jun 2023 15:27:57 +0000 (15:27 +0000)
Per the spec, methods cannot be associated with a named pointer type.
Exit early with an empty method set in this case.

This matches the corresponding check in LookupFieldOrMethod;
the check is not present in (lowercase) lookupFieldOrMethod
because it (the check) doesn't apply to struct fields.

Fixes #60634.

Change-Id: Ica6ca8be6b850ea0da6f0b441fbf5b99cb0b6b17
Reviewed-on: https://go-review.googlesource.com/c/go/+/501299
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Griesemer <gri@google.com>

src/go/types/methodset.go
src/go/types/methodset_test.go

index 2bf30286153a5d3d79c1d517fcdd8ad61e74c90f..0d9d9b481771af6f7976079e072c36d0efd39990 100644 (file)
@@ -76,6 +76,14 @@ func NewMethodSet(T Type) *MethodSet {
        // TODO(rfindley) confirm that this code is in sync with lookupFieldOrMethod
        //                with respect to type params.
 
+       // Methods cannot be associated with a named pointer type.
+       // (spec: "The type denoted by T is called the receiver base type;
+       // it must not be a pointer or interface type and it must be declared
+       // in the same package as the method.").
+       if t, _ := T.(*Named); t != nil && isPointer(t) {
+               return &emptyMethodSet
+       }
+
        // method set up to the current depth, allocated lazily
        var base methodSet
 
index 918b51d93bf782336d026c8ee011691c1a623b6a..c40d05fc37b12e76b6ad549e07116a677c98dd50 100644 (file)
@@ -5,6 +5,7 @@
 package types_test
 
 import (
+       "strings"
        "testing"
 
        "go/ast"
@@ -154,3 +155,43 @@ type Instance = *Tree[int]
        T := pkg.Scope().Lookup("Instance").Type()
        _ = NewMethodSet(T) // verify that NewMethodSet terminates
 }
+
+func TestIssue60634(t *testing.T) {
+       const src = `
+package p
+type T *int
+func (T) m() {} // expected error: invalid receiver type
+`
+
+       fset := token.NewFileSet()
+       f, err := parser.ParseFile(fset, "p.go", src, 0)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       var conf Config
+       pkg, err := conf.Check("p", fset, []*ast.File{f}, nil)
+       if err == nil || !strings.Contains(err.Error(), "invalid receiver type") {
+               t.Fatalf("missing or unexpected error: %v", err)
+       }
+
+       // look up T.m and (*T).m
+       T := pkg.Scope().Lookup("T").Type()
+       name := "m"
+       for _, recv := range []Type{T, NewPointer(T)} {
+               // LookupFieldOrMethod and NewMethodSet must match:
+               // either both find m or neither finds it.
+               obj1, _, _ := LookupFieldOrMethod(recv, false, pkg, name)
+               mset := NewMethodSet(recv)
+               if (obj1 != nil) != (mset.Len() == 1) {
+                       t.Fatalf("lookup(%v.%s): got obj = %v, mset = %v", recv, name, obj1, mset)
+               }
+               // If the method exists, both must return the same object.
+               if obj1 != nil {
+                       obj2 := mset.At(0).Obj()
+                       if obj1 != obj2 {
+                               t.Fatalf("%v != %v", obj1, obj2)
+                       }
+               }
+       }
+}