| // Copyright 2021 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build go1.18 |
| // +build go1.18 |
| |
| package objectpath_test |
| |
| import ( |
| "go/types" |
| "testing" |
| |
| "golang.org/x/tools/go/buildutil" |
| "golang.org/x/tools/go/loader" |
| "golang.org/x/tools/go/types/objectpath" |
| ) |
| |
| func TestGenericPaths(t *testing.T) { |
| pkgs := map[string]map[string]string{ |
| "b": {"b.go": ` |
| package b |
| |
| const C int = 1 |
| |
| type T[TP0 any, TP1 interface{ M0(); M1() }] struct{} |
| |
| func (T[RP0, RP1]) M() {} |
| |
| type N int |
| |
| func (N) M0() |
| func (N) M1() |
| |
| type A = T[int, N] |
| |
| func F[FP0 any, FP1 interface{ M() }](FP0, FP1) {} |
| `}, |
| } |
| paths := []pathTest{ |
| // Good paths |
| {"b", "T", "type b.T[TP0 any, TP1 interface{M0(); M1()}] struct{}", ""}, |
| {"b", "T.O", "type b.T[TP0 any, TP1 interface{M0(); M1()}] struct{}", ""}, |
| {"b", "T.M0", "func (b.T[RP0, RP1]).M()", ""}, |
| {"b", "T.T0O", "type parameter TP0 any", ""}, |
| {"b", "T.T1O", "type parameter TP1 interface{M0(); M1()}", ""}, |
| {"b", "T.T1CM0", "func (interface).M0()", ""}, |
| {"b", "F.T0O", "type parameter FP0 any", ""}, |
| {"b", "F.T1CM0", "func (interface).M()", ""}, |
| // Obj of an instance is the generic declaration. |
| {"b", "A.O", "type b.T[TP0 any, TP1 interface{M0(); M1()}] struct{}", ""}, |
| {"b", "A.M0", "func (b.T[int, b.N]).M()", ""}, |
| |
| // Bad paths |
| {"b", "N.C", "", "invalid path: ends with 'C', want [AFMO]"}, |
| {"b", "N.CO", "", "cannot apply 'C' to b.N (got *types.Named, want type parameter)"}, |
| {"b", "N.T", "", `invalid path: bad numeric operand "" for code 'T'`}, |
| {"b", "N.T0", "", "tuple index 0 out of range [0-0)"}, |
| {"b", "T.T2O", "", "tuple index 2 out of range [0-2)"}, |
| {"b", "T.T1M0", "", "cannot apply 'M' to TP1 (got *types.TypeParam, want interface or named)"}, |
| {"b", "C.T0", "", "cannot apply 'T' to int (got *types.Basic, want named or signature)"}, |
| } |
| |
| conf := loader.Config{Build: buildutil.FakeContext(pkgs)} |
| conf.Import("b") |
| prog, err := conf.Load() |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| for _, test := range paths { |
| if err := testPath(prog, test); err != nil { |
| t.Error(err) |
| } |
| } |
| |
| // bad objects |
| for _, test := range []struct { |
| obj types.Object |
| wantErr string |
| }{ |
| {types.Universe.Lookup("any"), "predeclared type any = interface{} has no path"}, |
| {types.Universe.Lookup("comparable"), "predeclared type comparable interface{comparable} has no path"}, |
| } { |
| path, err := objectpath.For(test.obj) |
| if err == nil { |
| t.Errorf("Object(%s) = %q, want error", test.obj, path) |
| continue |
| } |
| if err.Error() != test.wantErr { |
| t.Errorf("Object(%s) error was %q, want %q", test.obj, err, test.wantErr) |
| continue |
| } |
| } |
| } |
| |
| func TestGenericPaths_Issue51717(t *testing.T) { |
| pkgs := map[string]map[string]string{ |
| "p": {"p.go": ` |
| package p |
| |
| type S struct{} |
| |
| func (_ S) M() { |
| // The go vet stackoverflow crash disappears when the following line is removed |
| panic("") |
| } |
| |
| func F[WL interface{ N(item W) WL }, W any]() { |
| } |
| |
| func main() {} |
| `}, |
| } |
| paths := []pathTest{ |
| {"p", "F.T0CM0.RA0", "var WL", ""}, |
| {"p", "F.T0CM0.RA0.CM0", "func (interface).N(item W) WL", ""}, |
| |
| // Finding S.M0 reproduced the infinite recursion reported in #51717, |
| // because F is searched before S. |
| {"p", "S.M0", "func (p.S).M()", ""}, |
| } |
| |
| conf := loader.Config{Build: buildutil.FakeContext(pkgs)} |
| conf.Import("p") |
| prog, err := conf.Load() |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| for _, test := range paths { |
| if err := testPath(prog, test); err != nil { |
| t.Error(err) |
| } |
| } |
| } |