| // 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 gcimporter_test | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"fmt" | 
 | 	"go/ast" | 
 | 	"go/importer" | 
 | 	"go/parser" | 
 | 	"go/token" | 
 | 	"go/types" | 
 | 	"os" | 
 | 	"path/filepath" | 
 | 	"runtime" | 
 | 	"strings" | 
 | 	"testing" | 
 |  | 
 | 	"golang.org/x/tools/internal/gcimporter" | 
 | 	"golang.org/x/tools/internal/testenv" | 
 | ) | 
 |  | 
 | // TODO(rfindley): migrate this to testdata, as has been done in the standard library. | 
 | func TestGenericExport(t *testing.T) { | 
 | 	const src = ` | 
 | package generic | 
 |  | 
 | type Any any | 
 |  | 
 | type T[A, B any] struct { Left A; Right B } | 
 |  | 
 | func (T[P, Q]) m() {} | 
 |  | 
 | var X T[int, string] = T[int, string]{1, "hi"} | 
 |  | 
 | func ToInt[P interface{ ~int }](p P) int { return int(p) } | 
 |  | 
 | var IntID = ToInt[int] | 
 |  | 
 | type G[C comparable] int | 
 |  | 
 | func ImplicitFunc[T ~int]() {} | 
 |  | 
 | type ImplicitType[T ~int] int | 
 |  | 
 | // Exercise constant import/export | 
 | const C1 = 42 | 
 | const C2 int = 42 | 
 | const C3 float64 = 42 | 
 |  | 
 | type Constraint[T any] interface { | 
 |        m(T) | 
 | } | 
 |  | 
 | // TODO(rfindley): revert to multiple blanks once the restriction on multiple | 
 | // blanks is removed from the type checker. | 
 | // type Blanks[_ any, _ Constraint[int]] int | 
 | // func (Blanks[_, _]) m() {} | 
 | type Blanks[_ any] int | 
 | func (Blanks[_]) m() {} | 
 | ` | 
 | 	testExportSrc(t, []byte(src)) | 
 | } | 
 |  | 
 | func testExportSrc(t *testing.T, src []byte) { | 
 | 	// This package only handles gc export data. | 
 | 	if runtime.Compiler != "gc" { | 
 | 		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler) | 
 | 	} | 
 | 	testenv.NeedsGoBuild(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 | 
 | 	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) { | 
 | 	testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache | 
 |  | 
 | 	// Check go files in test/typeparam. | 
 | 	rootDir := filepath.Join(runtime.GOROOT(), "test", "typeparam") | 
 | 	list, err := os.ReadDir(rootDir) | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 |  | 
 | 	if isUnifiedBuilder() { | 
 | 		t.Skip("unified export data format is currently unsupported") | 
 | 	} | 
 |  | 
 | 	for _, entry := range list { | 
 | 		if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") { | 
 | 			// For now, only consider standalone go files. | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		t.Run(entry.Name(), func(t *testing.T) { | 
 | 			filename := filepath.Join(rootDir, entry.Name()) | 
 | 			src, err := os.ReadFile(filename) | 
 | 			if err != nil { | 
 | 				t.Fatal(err) | 
 | 			} | 
 |  | 
 | 			if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) { | 
 | 				// We're bypassing the logic of run.go here, so be conservative about | 
 | 				// the files we consider in an attempt to make this test more robust to | 
 | 				// changes in test/typeparams. | 
 | 				t.Skipf("not detected as a run test") | 
 | 			} | 
 |  | 
 | 			testExportSrc(t, src) | 
 | 		}) | 
 | 	} | 
 | } | 
 |  | 
 | 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 | 
 | } |