From 40370f8a2a08dcc0d379e4ff22364a094043c97b Mon Sep 17 00:00:00 2001 From: Robert Findley Date: Mon, 14 Mar 2022 14:15:14 -0400 Subject: [PATCH] go/internal/gcimporter: add a test case for issue 51219 Based on CL 387814, construct a reproducer for issue 51219 using x/tools/go/internal/gcimporter. Confirmed that this fails without the fix in CL 392475. Additionally, this CL includes some minor cleanup: - no need to take two passes in testExportSrc, as IExportVersionGenerics and IExportVersionGo1_18 are the same - remove skips that are no longer needed. Fixes golang/go#51219 Change-Id: Ia76fe9038aab7a2b9efc8429dc211b03adbb5560 Reviewed-on: https://go-review.googlesource.com/c/tools/+/392734 Trust: Robert Findley Run-TryBot: Robert Findley Reviewed-by: Matthew Dempsky gopls-CI: kokoro TryBot-Result: Gopher Robot --- go/internal/gcimporter/iexport_go118_test.go | 178 +++++++++++++++---- 1 file changed, 140 insertions(+), 38 deletions(-) diff --git a/go/internal/gcimporter/iexport_go118_test.go b/go/internal/gcimporter/iexport_go118_test.go index 1c98291b72d..5dfa2580f6b 100644 --- a/go/internal/gcimporter/iexport_go118_test.go +++ b/go/internal/gcimporter/iexport_go118_test.go @@ -9,6 +9,7 @@ package gcimporter_test import ( "bytes" + "fmt" "go/ast" "go/importer" "go/parser" @@ -71,39 +72,27 @@ func testExportSrc(t *testing.T, src []byte) { t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) } - // Test at both stages of the 1.18 export data format change. - tests := []struct { - name string - version int - }{ - {"legacy generics", gcimporter.IExportVersionGenerics}, - {"go1.18", gcimporter.IExportVersionGo1_18}, + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "g.go", src, 0) + if err != nil { + t.Fatal(err) + } + conf := types.Config{ + Importer: importer.Default(), + } + pkg, err := conf.Check("", fset, []*ast.File{f}, nil) + if err != nil { + t.Fatal(err) } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "g.go", src, 0) - if err != nil { - t.Fatal(err) - } - conf := types.Config{ - Importer: importer.Default(), - } - pkg, err := conf.Check("", fset, []*ast.File{f}, nil) - if err != nil { - t.Fatal(err) - } - - // export - data, err := iexport(fset, test.version, pkg) - if err != nil { - t.Fatal(err) - } - - testPkgData(t, fset, test.version, pkg, data) - }) + // export + version := gcimporter.IExportVersion + data, err := iexport(fset, version, pkg) + if err != nil { + t.Fatal(err) } + + testPkgData(t, fset, version, pkg, data) } func TestImportTypeparamTests(t *testing.T) { @@ -118,10 +107,6 @@ func TestImportTypeparamTests(t *testing.T) { t.Skip("unified export data format is currently unsupported") } - skip := map[string]string{ - "issue48424.go": "go/types support missing", // TODO: need to implement this if #48424 is accepted - } - for _, entry := range list { if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { // For now, only consider standalone go files. @@ -129,10 +114,6 @@ func TestImportTypeparamTests(t *testing.T) { } t.Run(entry.Name(), func(t *testing.T) { - if reason, ok := skip[entry.Name()]; ok { - t.Skip(reason) - } - filename := filepath.Join(rootDir, entry.Name()) src, err := os.ReadFile(filename) if err != nil { @@ -150,3 +131,124 @@ func TestImportTypeparamTests(t *testing.T) { }) } } + +func TestRecursiveExport_Issue51219(t *testing.T) { + const srca = ` +package a + +type Interaction[DataT InteractionDataConstraint] struct { +} + +type InteractionDataConstraint interface { + []byte | + UserCommandInteractionData +} + +type UserCommandInteractionData struct { + resolvedInteractionWithOptions +} + +type resolvedInteractionWithOptions struct { + Resolved Resolved +} + +type Resolved struct { + Users ResolvedData[User] +} + +type ResolvedData[T ResolvedDataConstraint] map[uint64]T + +type ResolvedDataConstraint interface { + User | Message +} + +type User struct{} + +type Message struct { + Interaction *Interaction[[]byte] +} +` + + const srcb = ` +package b + +import ( + "a" +) + +// InteractionRequest is an incoming request Interaction +type InteractionRequest[T a.InteractionDataConstraint] struct { + a.Interaction[T] +} +` + + const srcp = ` +package p + +import ( + "b" +) + +// ResponseWriterMock mocks corde's ResponseWriter interface +type ResponseWriterMock struct { + x b.InteractionRequest[[]byte] +} +` + + importer := &testImporter{ + src: map[string][]byte{ + "a": []byte(srca), + "b": []byte(srcb), + "p": []byte(srcp), + }, + pkgs: make(map[string]*types.Package), + } + _, err := importer.Import("p") + if err != nil { + t.Fatal(err) + } +} + +// testImporter is a helper to test chains of imports using export data. +type testImporter struct { + src map[string][]byte // original source + pkgs map[string]*types.Package // memoized imported packages +} + +func (t *testImporter) Import(path string) (*types.Package, error) { + if pkg, ok := t.pkgs[path]; ok { + return pkg, nil + } + src, ok := t.src[path] + if !ok { + return nil, fmt.Errorf("unknown path %v", path) + } + + // Type-check, but don't return this package directly. + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, path+".go", src, 0) + if err != nil { + return nil, err + } + conf := types.Config{ + Importer: t, + } + pkg, err := conf.Check(path, fset, []*ast.File{f}, nil) + if err != nil { + return nil, err + } + + // Export and import to get the package imported from export data. + exportdata, err := iexport(fset, gcimporter.IExportVersion, pkg) + if err != nil { + return nil, err + } + imports := make(map[string]*types.Package) + fset2 := token.NewFileSet() + _, pkg2, err := gcimporter.IImportData(fset2, imports, exportdata, pkg.Path()) + if err != nil { + return nil, err + } + t.pkgs[path] = pkg2 + return pkg2, nil +}