From fd13444b2b4bf1ce6b05980b1b139402048f0363 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 6 Jun 2023 13:34:39 -0700 Subject: [PATCH] go/types: fix method set computation if receiver is a named pointer 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 Auto-Submit: Robert Griesemer TryBot-Result: Gopher Robot Reviewed-by: Robert Findley Run-TryBot: Robert Griesemer --- src/go/types/methodset.go | 8 +++++++ src/go/types/methodset_test.go | 41 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/src/go/types/methodset.go b/src/go/types/methodset.go index 2bf30286153a5..0d9d9b481771a 100644 --- a/src/go/types/methodset.go +++ b/src/go/types/methodset.go @@ -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 diff --git a/src/go/types/methodset_test.go b/src/go/types/methodset_test.go index 918b51d93bf78..c40d05fc37b12 100644 --- a/src/go/types/methodset_test.go +++ b/src/go/types/methodset_test.go @@ -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) + } + } + } +}