Source file src/go/internal/gcimporter/gcimporter_test.go

     1  // Copyright 2011 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  package gcimporter_test
     6  
     7  import (
     8  	"bytes"
     9  	"fmt"
    10  	"internal/godebug"
    11  	"internal/testenv"
    12  	"os"
    13  	"os/exec"
    14  	"path"
    15  	"path/filepath"
    16  	"runtime"
    17  	"strings"
    18  	"testing"
    19  	"time"
    20  
    21  	"go/ast"
    22  	"go/build"
    23  	"go/importer"
    24  	"go/parser"
    25  	"go/token"
    26  	"go/types"
    27  
    28  	. "go/internal/gcimporter"
    29  )
    30  
    31  func TestMain(m *testing.M) {
    32  	build.Default.GOROOT = testenv.GOROOT(nil)
    33  	os.Exit(m.Run())
    34  }
    35  
    36  // compile runs the compiler on filename, with dirname as the working directory,
    37  // and writes the output file to outdirname.
    38  // compile gives the resulting package a packagepath of testdata/<filebasename>.
    39  func compile(t *testing.T, dirname, filename, outdirname string, packageFiles map[string]string, pkgImports ...string) string {
    40  	// filename must end with ".go"
    41  	basename, ok := strings.CutSuffix(filepath.Base(filename), ".go")
    42  	if !ok {
    43  		t.Fatalf("filename doesn't end in .go: %s", filename)
    44  	}
    45  	objname := basename + ".o"
    46  	outname := filepath.Join(outdirname, objname)
    47  
    48  	importcfgfile := os.DevNull
    49  	if len(packageFiles) > 0 || len(pkgImports) > 0 {
    50  		importcfgfile = filepath.Join(outdirname, basename) + ".importcfg"
    51  		testenv.WriteImportcfg(t, importcfgfile, packageFiles, pkgImports...)
    52  	}
    53  
    54  	pkgpath := path.Join("testdata", basename)
    55  	cmd := testenv.Command(t, testenv.GoToolPath(t), "tool", "compile", "-p", pkgpath, "-D", "testdata", "-importcfg", importcfgfile, "-o", outname, filename)
    56  	cmd.Dir = dirname
    57  	out, err := cmd.CombinedOutput()
    58  	if err != nil {
    59  		t.Logf("%s", out)
    60  		t.Fatalf("go tool compile %s failed: %s", filename, err)
    61  	}
    62  	return outname
    63  }
    64  
    65  func testPath(t *testing.T, path, srcDir string) *types.Package {
    66  	t0 := time.Now()
    67  	fset := token.NewFileSet()
    68  	pkg, err := Import(fset, make(map[string]*types.Package), path, srcDir, nil)
    69  	if err != nil {
    70  		t.Errorf("testPath(%s): %s", path, err)
    71  		return nil
    72  	}
    73  	t.Logf("testPath(%s): %v", path, time.Since(t0))
    74  	return pkg
    75  }
    76  
    77  var pkgExts = [...]string{".a", ".o"} // keep in sync with gcimporter.go
    78  
    79  func mktmpdir(t *testing.T) string {
    80  	tmpdir, err := os.MkdirTemp("", "gcimporter_test")
    81  	if err != nil {
    82  		t.Fatal("mktmpdir:", err)
    83  	}
    84  	if err := os.Mkdir(filepath.Join(tmpdir, "testdata"), 0700); err != nil {
    85  		os.RemoveAll(tmpdir)
    86  		t.Fatal("mktmpdir:", err)
    87  	}
    88  	return tmpdir
    89  }
    90  
    91  func TestImportTestdata(t *testing.T) {
    92  	// This package only handles gc export data.
    93  	if runtime.Compiler != "gc" {
    94  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
    95  	}
    96  
    97  	testenv.MustHaveGoBuild(t)
    98  
    99  	testfiles := map[string][]string{
   100  		"exports.go":  {"go/ast", "go/token"},
   101  		"generics.go": nil,
   102  	}
   103  	if true /* was goexperiment.Unified */ {
   104  		// TODO(mdempsky): Fix test below to flatten the transitive
   105  		// Package.Imports graph. Unified IR is more precise about
   106  		// recreating the package import graph.
   107  		testfiles["exports.go"] = []string{"go/ast"}
   108  	}
   109  
   110  	for testfile, wantImports := range testfiles {
   111  		tmpdir := mktmpdir(t)
   112  		defer os.RemoveAll(tmpdir)
   113  
   114  		compile(t, "testdata", testfile, filepath.Join(tmpdir, "testdata"), nil, wantImports...)
   115  		path := "./testdata/" + strings.TrimSuffix(testfile, ".go")
   116  
   117  		if pkg := testPath(t, path, tmpdir); pkg != nil {
   118  			// The package's Imports list must include all packages
   119  			// explicitly imported by testfile, plus all packages
   120  			// referenced indirectly via exported objects in testfile.
   121  			got := fmt.Sprint(pkg.Imports())
   122  			for _, want := range wantImports {
   123  				if !strings.Contains(got, want) {
   124  					t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
   125  				}
   126  			}
   127  		}
   128  	}
   129  }
   130  
   131  func TestImportTypeparamTests(t *testing.T) {
   132  	if testing.Short() {
   133  		t.Skipf("in short mode, skipping test that requires export data for all of std")
   134  	}
   135  
   136  	// This package only handles gc export data.
   137  	if runtime.Compiler != "gc" {
   138  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   139  	}
   140  
   141  	// cmd/distpack removes the GOROOT/test directory, so skip if it isn't there.
   142  	// cmd/distpack also requires the presence of GOROOT/VERSION, so use that to
   143  	// avoid false-positive skips.
   144  	gorootTest := filepath.Join(testenv.GOROOT(t), "test")
   145  	if _, err := os.Stat(gorootTest); os.IsNotExist(err) {
   146  		if _, err := os.Stat(filepath.Join(testenv.GOROOT(t), "VERSION")); err == nil {
   147  			t.Skipf("skipping: GOROOT/test not present")
   148  		}
   149  	}
   150  
   151  	testenv.MustHaveGoBuild(t)
   152  
   153  	tmpdir := mktmpdir(t)
   154  	defer os.RemoveAll(tmpdir)
   155  
   156  	// Check go files in test/typeparam, except those that fail for a known
   157  	// reason.
   158  	rootDir := filepath.Join(gorootTest, "typeparam")
   159  	list, err := os.ReadDir(rootDir)
   160  	if err != nil {
   161  		t.Fatal(err)
   162  	}
   163  
   164  	for _, entry := range list {
   165  		if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") {
   166  			// For now, only consider standalone go files.
   167  			continue
   168  		}
   169  
   170  		t.Run(entry.Name(), func(t *testing.T) {
   171  			filename := filepath.Join(rootDir, entry.Name())
   172  			src, err := os.ReadFile(filename)
   173  			if err != nil {
   174  				t.Fatal(err)
   175  			}
   176  			if !bytes.HasPrefix(src, []byte("// run")) && !bytes.HasPrefix(src, []byte("// compile")) {
   177  				// We're bypassing the logic of run.go here, so be conservative about
   178  				// the files we consider in an attempt to make this test more robust to
   179  				// changes in test/typeparams.
   180  				t.Skipf("not detected as a run test")
   181  			}
   182  
   183  			// Compile and import, and compare the resulting package with the package
   184  			// that was type-checked directly.
   185  			compile(t, rootDir, entry.Name(), filepath.Join(tmpdir, "testdata"), nil, filename)
   186  			pkgName := strings.TrimSuffix(entry.Name(), ".go")
   187  			imported := importPkg(t, "./testdata/"+pkgName, tmpdir)
   188  			checked := checkFile(t, filename, src)
   189  
   190  			seen := make(map[string]bool)
   191  			for _, name := range imported.Scope().Names() {
   192  				if !token.IsExported(name) {
   193  					continue // ignore synthetic names like .inittask and .dict.*
   194  				}
   195  				seen[name] = true
   196  
   197  				importedObj := imported.Scope().Lookup(name)
   198  				got := types.ObjectString(importedObj, types.RelativeTo(imported))
   199  				got = sanitizeObjectString(got)
   200  
   201  				checkedObj := checked.Scope().Lookup(name)
   202  				if checkedObj == nil {
   203  					t.Fatalf("imported object %q was not type-checked", name)
   204  				}
   205  				want := types.ObjectString(checkedObj, types.RelativeTo(checked))
   206  				want = sanitizeObjectString(want)
   207  
   208  				// TODO(golang/go#66859): investigate and reenable these tests,
   209  				// which fail with gotypesalias=1, soon to be the default.
   210  				if godebug.New("gotypesalias").Value() != "0" {
   211  					symbol := name + " in " + filepath.Base(filename)
   212  					switch symbol {
   213  					case "Eint2 in struct.go", "A in issue50259.go":
   214  						t.Skipf("%s requires gotypesalias=1", symbol)
   215  					}
   216  				}
   217  
   218  				if got != want {
   219  					t.Errorf("imported %q as %q, want %q", name, got, want)
   220  				}
   221  			}
   222  
   223  			for _, name := range checked.Scope().Names() {
   224  				if !token.IsExported(name) || seen[name] {
   225  					continue
   226  				}
   227  				t.Errorf("did not import object %q", name)
   228  			}
   229  		})
   230  	}
   231  }
   232  
   233  // sanitizeObjectString removes type parameter debugging markers from an object
   234  // string, to normalize it for comparison.
   235  // TODO(rfindley): this should not be necessary.
   236  func sanitizeObjectString(s string) string {
   237  	var runes []rune
   238  	for _, r := range s {
   239  		if '₀' <= r && r < '₀'+10 {
   240  			continue // trim type parameter subscripts
   241  		}
   242  		runes = append(runes, r)
   243  	}
   244  	return string(runes)
   245  }
   246  
   247  func checkFile(t *testing.T, filename string, src []byte) *types.Package {
   248  	fset := token.NewFileSet()
   249  	f, err := parser.ParseFile(fset, filename, src, 0)
   250  	if err != nil {
   251  		t.Fatal(err)
   252  	}
   253  	config := types.Config{
   254  		Importer: importer.Default(),
   255  	}
   256  	pkg, err := config.Check("", fset, []*ast.File{f}, nil)
   257  	if err != nil {
   258  		t.Fatal(err)
   259  	}
   260  	return pkg
   261  }
   262  
   263  func TestVersionHandling(t *testing.T) {
   264  	testenv.MustHaveGoBuild(t)
   265  
   266  	// This package only handles gc export data.
   267  	if runtime.Compiler != "gc" {
   268  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   269  	}
   270  
   271  	const dir = "./testdata/versions"
   272  	list, err := os.ReadDir(dir)
   273  	if err != nil {
   274  		t.Fatal(err)
   275  	}
   276  
   277  	tmpdir := mktmpdir(t)
   278  	defer os.RemoveAll(tmpdir)
   279  	corruptdir := filepath.Join(tmpdir, "testdata", "versions")
   280  	if err := os.Mkdir(corruptdir, 0700); err != nil {
   281  		t.Fatal(err)
   282  	}
   283  
   284  	fset := token.NewFileSet()
   285  
   286  	for _, f := range list {
   287  		name := f.Name()
   288  		if !strings.HasSuffix(name, ".a") {
   289  			continue // not a package file
   290  		}
   291  		if strings.Contains(name, "corrupted") {
   292  			continue // don't process a leftover corrupted file
   293  		}
   294  		pkgpath := "./" + name[:len(name)-2]
   295  
   296  		if testing.Verbose() {
   297  			t.Logf("importing %s", name)
   298  		}
   299  
   300  		// test that export data can be imported
   301  		_, err := Import(fset, make(map[string]*types.Package), pkgpath, dir, nil)
   302  		if err != nil {
   303  			// ok to fail if it fails with a no longer supported error for select files
   304  			if strings.Contains(err.Error(), "no longer supported") {
   305  				switch name {
   306  				case "test_go1.7_0.a", "test_go1.7_1.a",
   307  					"test_go1.8_4.a", "test_go1.8_5.a",
   308  					"test_go1.11_6b.a", "test_go1.11_999b.a":
   309  					continue
   310  				}
   311  				// fall through
   312  			}
   313  			// ok to fail if it fails with a newer version error for select files
   314  			if strings.Contains(err.Error(), "newer version") {
   315  				switch name {
   316  				case "test_go1.11_999i.a":
   317  					continue
   318  				}
   319  				// fall through
   320  			}
   321  			t.Errorf("import %q failed: %v", pkgpath, err)
   322  			continue
   323  		}
   324  
   325  		// create file with corrupted export data
   326  		// 1) read file
   327  		data, err := os.ReadFile(filepath.Join(dir, name))
   328  		if err != nil {
   329  			t.Fatal(err)
   330  		}
   331  		// 2) find export data
   332  		i := bytes.Index(data, []byte("\n$$B\n")) + 5
   333  		j := bytes.Index(data[i:], []byte("\n$$\n")) + i
   334  		if i < 0 || j < 0 || i > j {
   335  			t.Fatalf("export data section not found (i = %d, j = %d)", i, j)
   336  		}
   337  		// 3) corrupt the data (increment every 7th byte)
   338  		for k := j - 13; k >= i; k -= 7 {
   339  			data[k]++
   340  		}
   341  		// 4) write the file
   342  		pkgpath += "_corrupted"
   343  		filename := filepath.Join(corruptdir, pkgpath) + ".a"
   344  		os.WriteFile(filename, data, 0666)
   345  
   346  		// test that importing the corrupted file results in an error
   347  		_, err = Import(fset, make(map[string]*types.Package), pkgpath, corruptdir, nil)
   348  		if err == nil {
   349  			t.Errorf("import corrupted %q succeeded", pkgpath)
   350  		} else if msg := err.Error(); !strings.Contains(msg, "version skew") {
   351  			t.Errorf("import %q error incorrect (%s)", pkgpath, msg)
   352  		}
   353  	}
   354  }
   355  
   356  func TestImportStdLib(t *testing.T) {
   357  	if testing.Short() {
   358  		t.Skip("the imports can be expensive, and this test is especially slow when the build cache is empty")
   359  	}
   360  	testenv.MustHaveGoBuild(t)
   361  
   362  	// This package only handles gc export data.
   363  	if runtime.Compiler != "gc" {
   364  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   365  	}
   366  
   367  	// Get list of packages in stdlib. Filter out test-only packages with {{if .GoFiles}} check.
   368  	var stderr bytes.Buffer
   369  	cmd := exec.Command("go", "list", "-f", "{{if .GoFiles}}{{.ImportPath}}{{end}}", "std")
   370  	cmd.Stderr = &stderr
   371  	out, err := cmd.Output()
   372  	if err != nil {
   373  		t.Fatalf("failed to run go list to determine stdlib packages: %v\nstderr:\n%v", err, stderr.String())
   374  	}
   375  	pkgs := strings.Fields(string(out))
   376  
   377  	var nimports int
   378  	for _, pkg := range pkgs {
   379  		t.Run(pkg, func(t *testing.T) {
   380  			if testPath(t, pkg, filepath.Join(testenv.GOROOT(t), "src", path.Dir(pkg))) != nil {
   381  				nimports++
   382  			}
   383  		})
   384  	}
   385  	const minPkgs = 225 // 'GOOS=plan9 go1.18 list std | wc -l' reports 228; most other platforms have more.
   386  	if len(pkgs) < minPkgs {
   387  		t.Fatalf("too few packages (%d) were imported", nimports)
   388  	}
   389  
   390  	t.Logf("tested %d imports", nimports)
   391  }
   392  
   393  var importedObjectTests = []struct {
   394  	name string
   395  	want string
   396  }{
   397  	// non-interfaces
   398  	{"crypto.Hash", "type Hash uint"},
   399  	{"go/ast.ObjKind", "type ObjKind int"},
   400  	{"go/types.Qualifier", "type Qualifier func(*Package) string"},
   401  	{"go/types.Comparable", "func Comparable(T Type) bool"},
   402  	{"math.Pi", "const Pi untyped float"},
   403  	{"math.Sin", "func Sin(x float64) float64"},
   404  	{"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"},
   405  	{"go/internal/gcimporter.FindPkg", "func FindPkg(path string, srcDir string) (filename string, id string, err error)"},
   406  
   407  	// interfaces
   408  	{"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key any) any}"},
   409  	{"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
   410  	{"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
   411  	{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
   412  	{"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"},
   413  	{"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"},
   414  	{"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
   415  }
   416  
   417  func TestImportedTypes(t *testing.T) {
   418  	testenv.MustHaveGoBuild(t)
   419  
   420  	// This package only handles gc export data.
   421  	if runtime.Compiler != "gc" {
   422  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   423  	}
   424  
   425  	fset := token.NewFileSet()
   426  	for _, test := range importedObjectTests {
   427  		s := strings.Split(test.name, ".")
   428  		if len(s) != 2 {
   429  			t.Fatal("inconsistent test data")
   430  		}
   431  		importPath := s[0]
   432  		objName := s[1]
   433  
   434  		pkg, err := Import(fset, make(map[string]*types.Package), importPath, ".", nil)
   435  		if err != nil {
   436  			t.Error(err)
   437  			continue
   438  		}
   439  
   440  		obj := pkg.Scope().Lookup(objName)
   441  		if obj == nil {
   442  			t.Errorf("%s: object not found", test.name)
   443  			continue
   444  		}
   445  
   446  		got := types.ObjectString(obj, types.RelativeTo(pkg))
   447  		if got != test.want {
   448  			t.Errorf("%s: got %q; want %q", test.name, got, test.want)
   449  		}
   450  
   451  		if named, _ := obj.Type().(*types.Named); named != nil {
   452  			verifyInterfaceMethodRecvs(t, named, 0)
   453  		}
   454  	}
   455  }
   456  
   457  // verifyInterfaceMethodRecvs verifies that method receiver types
   458  // are named if the methods belong to a named interface type.
   459  func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
   460  	// avoid endless recursion in case of an embedding bug that lead to a cycle
   461  	if level > 10 {
   462  		t.Errorf("%s: embeds itself", named)
   463  		return
   464  	}
   465  
   466  	iface, _ := named.Underlying().(*types.Interface)
   467  	if iface == nil {
   468  		return // not an interface
   469  	}
   470  
   471  	// check explicitly declared methods
   472  	for i := 0; i < iface.NumExplicitMethods(); i++ {
   473  		m := iface.ExplicitMethod(i)
   474  		recv := m.Type().(*types.Signature).Recv()
   475  		if recv == nil {
   476  			t.Errorf("%s: missing receiver type", m)
   477  			continue
   478  		}
   479  		if recv.Type() != named {
   480  			t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
   481  		}
   482  	}
   483  
   484  	// check embedded interfaces (if they are named, too)
   485  	for i := 0; i < iface.NumEmbeddeds(); i++ {
   486  		// embedding of interfaces cannot have cycles; recursion will terminate
   487  		if etype, _ := iface.EmbeddedType(i).(*types.Named); etype != nil {
   488  			verifyInterfaceMethodRecvs(t, etype, level+1)
   489  		}
   490  	}
   491  }
   492  
   493  func TestIssue5815(t *testing.T) {
   494  	testenv.MustHaveGoBuild(t)
   495  
   496  	// This package only handles gc export data.
   497  	if runtime.Compiler != "gc" {
   498  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   499  	}
   500  
   501  	pkg := importPkg(t, "strings", ".")
   502  
   503  	scope := pkg.Scope()
   504  	for _, name := range scope.Names() {
   505  		obj := scope.Lookup(name)
   506  		if obj.Pkg() == nil {
   507  			t.Errorf("no pkg for %s", obj)
   508  		}
   509  		if tname, _ := obj.(*types.TypeName); tname != nil {
   510  			named := tname.Type().(*types.Named)
   511  			for i := 0; i < named.NumMethods(); i++ {
   512  				m := named.Method(i)
   513  				if m.Pkg() == nil {
   514  					t.Errorf("no pkg for %s", m)
   515  				}
   516  			}
   517  		}
   518  	}
   519  }
   520  
   521  // Smoke test to ensure that imported methods get the correct package.
   522  func TestCorrectMethodPackage(t *testing.T) {
   523  	testenv.MustHaveGoBuild(t)
   524  
   525  	// This package only handles gc export data.
   526  	if runtime.Compiler != "gc" {
   527  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   528  	}
   529  
   530  	imports := make(map[string]*types.Package)
   531  	fset := token.NewFileSet()
   532  	_, err := Import(fset, imports, "net/http", ".", nil)
   533  	if err != nil {
   534  		t.Fatal(err)
   535  	}
   536  
   537  	mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
   538  	mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex
   539  	sel := mset.Lookup(nil, "Lock")
   540  	lock := sel.Obj().(*types.Func)
   541  	if got, want := lock.Pkg().Path(), "sync"; got != want {
   542  		t.Errorf("got package path %q; want %q", got, want)
   543  	}
   544  }
   545  
   546  func TestIssue13566(t *testing.T) {
   547  	testenv.MustHaveGoBuild(t)
   548  
   549  	// This package only handles gc export data.
   550  	if runtime.Compiler != "gc" {
   551  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   552  	}
   553  
   554  	tmpdir := mktmpdir(t)
   555  	defer os.RemoveAll(tmpdir)
   556  	testoutdir := filepath.Join(tmpdir, "testdata")
   557  
   558  	// b.go needs to be compiled from the output directory so that the compiler can
   559  	// find the compiled package a. We pass the full path to compile() so that we
   560  	// don't have to copy the file to that directory.
   561  	bpath, err := filepath.Abs(filepath.Join("testdata", "b.go"))
   562  	if err != nil {
   563  		t.Fatal(err)
   564  	}
   565  
   566  	compile(t, "testdata", "a.go", testoutdir, nil, "encoding/json")
   567  	compile(t, testoutdir, bpath, testoutdir, map[string]string{"testdata/a": filepath.Join(testoutdir, "a.o")}, "encoding/json")
   568  
   569  	// import must succeed (test for issue at hand)
   570  	pkg := importPkg(t, "./testdata/b", tmpdir)
   571  
   572  	// make sure all indirectly imported packages have names
   573  	for _, imp := range pkg.Imports() {
   574  		if imp.Name() == "" {
   575  			t.Errorf("no name for %s package", imp.Path())
   576  		}
   577  	}
   578  }
   579  
   580  func TestTypeNamingOrder(t *testing.T) {
   581  	testenv.MustHaveGoBuild(t)
   582  
   583  	// This package only handles gc export data.
   584  	if runtime.Compiler != "gc" {
   585  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   586  	}
   587  
   588  	tmpdir := mktmpdir(t)
   589  	defer os.RemoveAll(tmpdir)
   590  	testoutdir := filepath.Join(tmpdir, "testdata")
   591  
   592  	compile(t, "testdata", "g.go", testoutdir, nil)
   593  
   594  	// import must succeed (test for issue at hand)
   595  	_ = importPkg(t, "./testdata/g", tmpdir)
   596  }
   597  
   598  func TestIssue13898(t *testing.T) {
   599  	testenv.MustHaveGoBuild(t)
   600  
   601  	// This package only handles gc export data.
   602  	if runtime.Compiler != "gc" {
   603  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   604  	}
   605  
   606  	// import go/internal/gcimporter which imports go/types partially
   607  	fset := token.NewFileSet()
   608  	imports := make(map[string]*types.Package)
   609  	_, err := Import(fset, imports, "go/internal/gcimporter", ".", nil)
   610  	if err != nil {
   611  		t.Fatal(err)
   612  	}
   613  
   614  	// look for go/types package
   615  	var goTypesPkg *types.Package
   616  	for path, pkg := range imports {
   617  		if path == "go/types" {
   618  			goTypesPkg = pkg
   619  			break
   620  		}
   621  	}
   622  	if goTypesPkg == nil {
   623  		t.Fatal("go/types not found")
   624  	}
   625  
   626  	// look for go/types.Object type
   627  	obj := lookupObj(t, goTypesPkg.Scope(), "Object")
   628  	typ, ok := obj.Type().(*types.Named)
   629  	if !ok {
   630  		t.Fatalf("go/types.Object type is %v; wanted named type", typ)
   631  	}
   632  
   633  	// lookup go/types.Object.Pkg method
   634  	m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg")
   635  	if m == nil {
   636  		t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect)
   637  	}
   638  
   639  	// the method must belong to go/types
   640  	if m.Pkg().Path() != "go/types" {
   641  		t.Fatalf("found %v; want go/types", m.Pkg())
   642  	}
   643  }
   644  
   645  func TestIssue15517(t *testing.T) {
   646  	testenv.MustHaveGoBuild(t)
   647  
   648  	// This package only handles gc export data.
   649  	if runtime.Compiler != "gc" {
   650  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   651  	}
   652  
   653  	tmpdir := mktmpdir(t)
   654  	defer os.RemoveAll(tmpdir)
   655  
   656  	compile(t, "testdata", "p.go", filepath.Join(tmpdir, "testdata"), nil)
   657  
   658  	// Multiple imports of p must succeed without redeclaration errors.
   659  	// We use an import path that's not cleaned up so that the eventual
   660  	// file path for the package is different from the package path; this
   661  	// will expose the error if it is present.
   662  	//
   663  	// (Issue: Both the textual and the binary importer used the file path
   664  	// of the package to be imported as key into the shared packages map.
   665  	// However, the binary importer then used the package path to identify
   666  	// the imported package to mark it as complete; effectively marking the
   667  	// wrong package as complete. By using an "unclean" package path, the
   668  	// file and package path are different, exposing the problem if present.
   669  	// The same issue occurs with vendoring.)
   670  	imports := make(map[string]*types.Package)
   671  	fset := token.NewFileSet()
   672  	for i := 0; i < 3; i++ {
   673  		if _, err := Import(fset, imports, "./././testdata/p", tmpdir, nil); err != nil {
   674  			t.Fatal(err)
   675  		}
   676  	}
   677  }
   678  
   679  func TestIssue15920(t *testing.T) {
   680  	testenv.MustHaveGoBuild(t)
   681  
   682  	// This package only handles gc export data.
   683  	if runtime.Compiler != "gc" {
   684  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   685  	}
   686  
   687  	compileAndImportPkg(t, "issue15920")
   688  }
   689  
   690  func TestIssue20046(t *testing.T) {
   691  	testenv.MustHaveGoBuild(t)
   692  
   693  	// This package only handles gc export data.
   694  	if runtime.Compiler != "gc" {
   695  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   696  	}
   697  
   698  	// "./issue20046".V.M must exist
   699  	pkg := compileAndImportPkg(t, "issue20046")
   700  	obj := lookupObj(t, pkg.Scope(), "V")
   701  	if m, index, indirect := types.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil {
   702  		t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect)
   703  	}
   704  }
   705  func TestIssue25301(t *testing.T) {
   706  	testenv.MustHaveGoBuild(t)
   707  
   708  	// This package only handles gc export data.
   709  	if runtime.Compiler != "gc" {
   710  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   711  	}
   712  
   713  	compileAndImportPkg(t, "issue25301")
   714  }
   715  
   716  func TestIssue25596(t *testing.T) {
   717  	testenv.MustHaveGoBuild(t)
   718  
   719  	// This package only handles gc export data.
   720  	if runtime.Compiler != "gc" {
   721  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   722  	}
   723  
   724  	compileAndImportPkg(t, "issue25596")
   725  }
   726  
   727  func TestIssue57015(t *testing.T) {
   728  	testenv.MustHaveGoBuild(t)
   729  
   730  	// This package only handles gc export data.
   731  	if runtime.Compiler != "gc" {
   732  		t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
   733  	}
   734  
   735  	compileAndImportPkg(t, "issue57015")
   736  }
   737  
   738  func importPkg(t *testing.T, path, srcDir string) *types.Package {
   739  	fset := token.NewFileSet()
   740  	pkg, err := Import(fset, make(map[string]*types.Package), path, srcDir, nil)
   741  	if err != nil {
   742  		t.Helper()
   743  		t.Fatal(err)
   744  	}
   745  	return pkg
   746  }
   747  
   748  func compileAndImportPkg(t *testing.T, name string) *types.Package {
   749  	t.Helper()
   750  	tmpdir := mktmpdir(t)
   751  	defer os.RemoveAll(tmpdir)
   752  	compile(t, "testdata", name+".go", filepath.Join(tmpdir, "testdata"), nil)
   753  	return importPkg(t, "./testdata/"+name, tmpdir)
   754  }
   755  
   756  func lookupObj(t *testing.T, scope *types.Scope, name string) types.Object {
   757  	if obj := scope.Lookup(name); obj != nil {
   758  		return obj
   759  	}
   760  	t.Helper()
   761  	t.Fatalf("%s not found", name)
   762  	return nil
   763  }
   764  

View as plain text