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 +}