github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/internal/gcimporter/iexport_go118_test.go (about)

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build go1.18
     6  // +build go1.18
     7  
     8  package gcimporter_test
     9  
    10  import (
    11  	"bytes"
    12  	"fmt"
    13  	"go/ast"
    14  	"go/importer"
    15  	"go/parser"
    16  	"go/token"
    17  	"go/types"
    18  	"os"
    19  	"path/filepath"
    20  	"runtime"
    21  	"strings"
    22  	"testing"
    23  
    24  	"golang.org/x/tools/internal/gcimporter"
    25  	"golang.org/x/tools/internal/testenv"
    26  )
    27  
    28  // TODO(rfindley): migrate this to testdata, as has been done in the standard library.
    29  func TestGenericExport(t *testing.T) {
    30  	const src = `
    31  package generic
    32  
    33  type Any any
    34  
    35  type T[A, B any] struct { Left A; Right B }
    36  
    37  func (T[P, Q]) m() {}
    38  
    39  var X T[int, string] = T[int, string]{1, "hi"}
    40  
    41  func ToInt[P interface{ ~int }](p P) int { return int(p) }
    42  
    43  var IntID = ToInt[int]
    44  
    45  type G[C comparable] int
    46  
    47  func ImplicitFunc[T ~int]() {}
    48  
    49  type ImplicitType[T ~int] int
    50  
    51  // Exercise constant import/export
    52  const C1 = 42
    53  const C2 int = 42
    54  const C3 float64 = 42
    55  
    56  type Constraint[T any] interface {
    57         m(T)
    58  }
    59  
    60  // TODO(rfindley): revert to multiple blanks once the restriction on multiple
    61  // blanks is removed from the type checker.
    62  // type Blanks[_ any, _ Constraint[int]] int
    63  // func (Blanks[_, _]) m() {}
    64  type Blanks[_ any] int
    65  func (Blanks[_]) m() {}
    66  `
    67  	testExportSrc(t, []byte(src))
    68  }
    69  
    70  func testExportSrc(t *testing.T, src []byte) {
    71  	// This package only handles gc export data.
    72  	if runtime.Compiler != "gc" {
    73  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
    74  	}
    75  
    76  	fset := token.NewFileSet()
    77  	f, err := parser.ParseFile(fset, "g.go", src, 0)
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  	conf := types.Config{
    82  		Importer: importer.Default(),
    83  	}
    84  	pkg, err := conf.Check("", fset, []*ast.File{f}, nil)
    85  	if err != nil {
    86  		t.Fatal(err)
    87  	}
    88  
    89  	// export
    90  	version := gcimporter.IExportVersion
    91  	data, err := iexport(fset, version, pkg)
    92  	if err != nil {
    93  		t.Fatal(err)
    94  	}
    95  
    96  	testPkgData(t, fset, version, pkg, data)
    97  }
    98  
    99  func TestImportTypeparamTests(t *testing.T) {
   100  	testenv.NeedsGoBuild(t) // to find stdlib export data in the build cache
   101  
   102  	// Check go files in test/typeparam.
   103  	rootDir := filepath.Join(runtime.GOROOT(), "test", "typeparam")
   104  	list, err := os.ReadDir(rootDir)
   105  	if err != nil {
   106  		t.Fatal(err)
   107  	}
   108  
   109  	if isUnifiedBuilder() {
   110  		t.Skip("unified export data format is currently unsupported")
   111  	}
   112  
   113  	for _, entry := range list {
   114  		if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
   115  			// For now, only consider standalone go files.
   116  			continue
   117  		}
   118  
   119  		t.Run(entry.Name(), func(t *testing.T) {
   120  			filename := filepath.Join(rootDir, entry.Name())
   121  			src, err := os.ReadFile(filename)
   122  			if err != nil {
   123  				t.Fatal(err)
   124  			}
   125  
   126  			if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) {
   127  				// We're bypassing the logic of run.go here, so be conservative about
   128  				// the files we consider in an attempt to make this test more robust to
   129  				// changes in test/typeparams.
   130  				t.Skipf("not detected as a run test")
   131  			}
   132  
   133  			testExportSrc(t, src)
   134  		})
   135  	}
   136  }
   137  
   138  func TestRecursiveExport_Issue51219(t *testing.T) {
   139  	const srca = `
   140  package a
   141  
   142  type Interaction[DataT InteractionDataConstraint] struct {
   143  }
   144  
   145  type InteractionDataConstraint interface {
   146  	[]byte |
   147  		UserCommandInteractionData
   148  }
   149  
   150  type UserCommandInteractionData struct {
   151  	resolvedInteractionWithOptions
   152  }
   153  
   154  type resolvedInteractionWithOptions struct {
   155  	Resolved Resolved
   156  }
   157  
   158  type Resolved struct {
   159  	Users ResolvedData[User]
   160  }
   161  
   162  type ResolvedData[T ResolvedDataConstraint] map[uint64]T
   163  
   164  type ResolvedDataConstraint interface {
   165  	User | Message
   166  }
   167  
   168  type User struct{}
   169  
   170  type Message struct {
   171  	Interaction *Interaction[[]byte]
   172  }
   173  `
   174  
   175  	const srcb = `
   176  package b
   177  
   178  import (
   179  	"a"
   180  )
   181  
   182  // InteractionRequest is an incoming request Interaction
   183  type InteractionRequest[T a.InteractionDataConstraint] struct {
   184  	a.Interaction[T]
   185  }
   186  `
   187  
   188  	const srcp = `
   189  package p
   190  
   191  import (
   192  	"b"
   193  )
   194  
   195  // ResponseWriterMock mocks corde's ResponseWriter interface
   196  type ResponseWriterMock struct {
   197  	x b.InteractionRequest[[]byte]
   198  }
   199  `
   200  
   201  	importer := &testImporter{
   202  		src: map[string][]byte{
   203  			"a": []byte(srca),
   204  			"b": []byte(srcb),
   205  			"p": []byte(srcp),
   206  		},
   207  		pkgs: make(map[string]*types.Package),
   208  	}
   209  	_, err := importer.Import("p")
   210  	if err != nil {
   211  		t.Fatal(err)
   212  	}
   213  }
   214  
   215  // testImporter is a helper to test chains of imports using export data.
   216  type testImporter struct {
   217  	src  map[string][]byte         // original source
   218  	pkgs map[string]*types.Package // memoized imported packages
   219  }
   220  
   221  func (t *testImporter) Import(path string) (*types.Package, error) {
   222  	if pkg, ok := t.pkgs[path]; ok {
   223  		return pkg, nil
   224  	}
   225  	src, ok := t.src[path]
   226  	if !ok {
   227  		return nil, fmt.Errorf("unknown path %v", path)
   228  	}
   229  
   230  	// Type-check, but don't return this package directly.
   231  	fset := token.NewFileSet()
   232  	f, err := parser.ParseFile(fset, path+".go", src, 0)
   233  	if err != nil {
   234  		return nil, err
   235  	}
   236  	conf := types.Config{
   237  		Importer: t,
   238  	}
   239  	pkg, err := conf.Check(path, fset, []*ast.File{f}, nil)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	// Export and import to get the package imported from export data.
   245  	exportdata, err := iexport(fset, gcimporter.IExportVersion, pkg)
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  	imports := make(map[string]*types.Package)
   250  	fset2 := token.NewFileSet()
   251  	_, pkg2, err := gcimporter.IImportData(fset2, imports, exportdata, pkg.Path())
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  	t.pkgs[path] = pkg2
   256  	return pkg2, nil
   257  }