Also, rewrite some uses of LookupFieldOrMethod in terms of it.
+ doc, relnote
Fixes #70737
Change-Id: I58a6dd78ee78560d8b6ea2d821381960a72660ab
Reviewed-on: https://go-review.googlesource.com/c/go/+/647196
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Griesemer <gri@google.com>
pkg go/types, const RecvVar VarKind #70250
pkg go/types, const ResultVar = 5 #70250
pkg go/types, const ResultVar VarKind #70250
+pkg go/types, func LookupSelection(Type, bool, *Package, string) (Selection, bool) #70737
pkg go/types, method (*Var) Kind() VarKind #70250
pkg go/types, method (*Var) SetKind(VarKind) #70250
pkg go/types, method (VarKind) String() string #70250
--- /dev/null
+The new [LookupSelection] function looks up the field or method of a
+given name and receiver type, like the existing [LookupFieldOrMethod]
+function, but returns the result in the form of a [Selection].
src := prefix + test.decl
pkg := mustTypecheck(src, nil, nil)
typ := NewPointer(pkg.Scope().Lookup("X").Type())
- obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
- m, _ := obj.(*Func)
- if m == nil {
- t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
+ sel, ok := LookupSelection(typ, false, pkg, "m")
+ if !ok {
+ t.Fatalf(`LookupSelection(%s, "m") failed, want func m`, typ)
}
- if got := ObjectString(m, RelativeTo(pkg)); got != test.want {
+ if got := ObjectString(sel.Obj(), RelativeTo(pkg)); got != test.want {
t.Errorf("instantiated %q, want %q", got, test.want)
}
}
`
pkg := mustTypecheck(src, nil, nil)
typ := pkg.Scope().Lookup("T").Type().(*Named)
- obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
- if obj == nil {
- t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
+ sel, ok := LookupSelection(typ, false, pkg, "m")
+ if !ok {
+ t.Fatalf(`LookupSelection(%s, "m") failed, want func m`, typ)
}
// Verify that the original method is not mutated by instantiating T (this
// bug manifested when subst did not return a new signature).
want := "func (T[P]).m()"
- if got := stripAnnotations(ObjectString(obj, RelativeTo(pkg))); got != want {
+ if got := stripAnnotations(ObjectString(sel.Obj(), RelativeTo(pkg))); got != want {
t.Errorf("instantiated %q, want %q", got, want)
}
}
import "bytes"
+// LookupSelection selects the field or method whose ID is Id(pkg,
+// name), on a value of type T. If addressable is set, T is the type
+// of an addressable variable (this matters only for method lookups).
+// T must not be nil.
+//
+// If the selection is valid:
+//
+// - [Selection.Obj] returns the field ([Var]) or method ([Func]);
+// - [Selection.Indirect] reports whether there were any pointer
+// indirections on the path to the field or method.
+// - [Selection.Index] returns the index sequence, defined below.
+//
+// The last index entry is the field or method index in the (possibly
+// embedded) type where the entry was found, either:
+//
+// 1. the list of declared methods of a named type; or
+// 2. the list of all methods (method set) of an interface type; or
+// 3. the list of fields of a struct type.
+//
+// The earlier index entries are the indices of the embedded struct
+// fields traversed to get to the found entry, starting at depth 0.
+//
+// See also [LookupFieldOrMethod], which returns the components separately.
+func LookupSelection(T Type, addressable bool, pkg *Package, name string) (Selection, bool) {
+ obj, index, indirect := LookupFieldOrMethod(T, addressable, pkg, name)
+ var kind SelectionKind
+ switch obj.(type) {
+ case nil:
+ return Selection{}, false
+ case *Func:
+ kind = MethodVal
+ case *Var:
+ kind = FieldVal
+ default:
+ panic(obj) // can't happen
+ }
+ return Selection{kind, T, obj, index, indirect}, true
+}
+
// Internal use of LookupFieldOrMethod: If the obj result is a method
// associated with a concrete (non-interface) type, the method's signature
// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing
// - If indirect is set, a method with a pointer receiver type was found
// but there was no pointer on the path from the actual receiver type to
// the method's formal receiver base type, nor was the receiver addressable.
+//
+// See also [LookupSelection], which returns the result as a [Selection].
func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
if T == nil {
panic("LookupFieldOrMethod on nil type")
}
// lookup go/types.Object.Pkg method
- m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg")
- if m == nil {
- t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect)
+ sel, ok := types.LookupSelection(typ, false, nil, "Pkg")
+ if !ok {
+ t.Fatalf("go/types.Object.Pkg not found")
}
// the method must belong to go/types
- if m.Pkg().Path() != "go/types" {
- t.Fatalf("found %v; want go/types", m.Pkg())
+ if sel.Obj().Pkg().Path() != "go/types" {
+ t.Fatalf("found %v; want go/types", sel.Obj().Pkg())
}
}
// "./issue20046".V.M must exist
pkg := compileAndImportPkg(t, "issue20046")
obj := lookupObj(t, pkg.Scope(), "V")
- if m, index, indirect := types.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil {
- t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect)
+ if _, ok := types.LookupSelection(obj.Type(), false, nil, "M"); !ok {
+ t.Fatalf("V.M not found")
}
}
func TestIssue25301(t *testing.T) {
src := prefix + test.decl
pkg := mustTypecheck(src, nil, nil)
typ := NewPointer(pkg.Scope().Lookup("X").Type())
- obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
- m, _ := obj.(*Func)
- if m == nil {
- t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
+ sel, ok := LookupSelection(typ, false, pkg, "m")
+ if !ok {
+ t.Fatalf(`LookupSelection(%s, "m") failed, want func m`, typ)
}
- if got := ObjectString(m, RelativeTo(pkg)); got != test.want {
+ if got := ObjectString(sel.Obj(), RelativeTo(pkg)); got != test.want {
t.Errorf("instantiated %q, want %q", got, test.want)
}
}
`
pkg := mustTypecheck(src, nil, nil)
typ := pkg.Scope().Lookup("T").Type().(*Named)
- obj, _, _ := LookupFieldOrMethod(typ, false, pkg, "m")
- if obj == nil {
- t.Fatalf(`LookupFieldOrMethod(%s, "m") = %v, want func m`, typ, obj)
+ sel, ok := LookupSelection(typ, false, pkg, "m")
+ if !ok {
+ t.Fatalf(`LookupSelection(%s, "m") failed, want func m`, typ)
}
// Verify that the original method is not mutated by instantiating T (this
// bug manifested when subst did not return a new signature).
want := "func (T[P]).m()"
- if got := stripAnnotations(ObjectString(obj, RelativeTo(pkg))); got != want {
+ if got := stripAnnotations(ObjectString(sel.Obj(), RelativeTo(pkg))); got != want {
t.Errorf("instantiated %q, want %q", got, want)
}
}
import "bytes"
+// LookupSelection selects the field or method whose ID is Id(pkg,
+// name), on a value of type T. If addressable is set, T is the type
+// of an addressable variable (this matters only for method lookups).
+// T must not be nil.
+//
+// If the selection is valid:
+//
+// - [Selection.Obj] returns the field ([Var]) or method ([Func]);
+// - [Selection.Indirect] reports whether there were any pointer
+// indirections on the path to the field or method.
+// - [Selection.Index] returns the index sequence, defined below.
+//
+// The last index entry is the field or method index in the (possibly
+// embedded) type where the entry was found, either:
+//
+// 1. the list of declared methods of a named type; or
+// 2. the list of all methods (method set) of an interface type; or
+// 3. the list of fields of a struct type.
+//
+// The earlier index entries are the indices of the embedded struct
+// fields traversed to get to the found entry, starting at depth 0.
+//
+// See also [LookupFieldOrMethod], which returns the components separately.
+func LookupSelection(T Type, addressable bool, pkg *Package, name string) (Selection, bool) {
+ obj, index, indirect := LookupFieldOrMethod(T, addressable, pkg, name)
+ var kind SelectionKind
+ switch obj.(type) {
+ case nil:
+ return Selection{}, false
+ case *Func:
+ kind = MethodVal
+ case *Var:
+ kind = FieldVal
+ default:
+ panic(obj) // can't happen
+ }
+ return Selection{kind, T, obj, index, indirect}, true
+}
+
// Internal use of LookupFieldOrMethod: If the obj result is a method
// associated with a concrete (non-interface) type, the method's signature
// may not be fully set up. Call Checker.objDecl(obj, nil) before accessing
// - If indirect is set, a method with a pointer receiver type was found
// but there was no pointer on the path from the actual receiver type to
// the method's formal receiver base type, nor was the receiver addressable.
+//
+// See also [LookupSelection], which returns the result as a [Selection].
func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
if T == nil {
panic("LookupFieldOrMethod on nil type")