mirror of
https://github.com/golang/go
synced 2024-11-26 05:17:58 -07:00
go/internal/gcimporter: add import tests for type parameters
Add a new test TestImportTypeparamTests that compiles and imports packages contained in test/typeparam, and compares the resulting package scope with the scope produced by type-checking directly. In the process, fix a bug in go/types affecting embedded instances with more than one type argument. This was uncovered by smoketest.go. To enable this new test it was easiest to move gcimporter_test.go to an external test, which required copying the pkgExts variable. Fixes #48101 Change-Id: Ie4d981bf463e886a8d141809805d184dbbf64607 Reviewed-on: https://go-review.googlesource.com/c/go/+/347070 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
b8420baf46
commit
4fb79569d2
@ -2,11 +2,12 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package gcimporter
|
||||
package gcimporter_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"internal/goexperiment"
|
||||
"internal/testenv"
|
||||
"os"
|
||||
"os/exec"
|
||||
@ -16,8 +17,13 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go/ast"
|
||||
"go/importer"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
|
||||
. "go/internal/gcimporter"
|
||||
)
|
||||
|
||||
// skipSpecialPlatforms causes the test to be skipped for platforms where
|
||||
@ -63,6 +69,8 @@ func testPath(t *testing.T, path, srcDir string) *types.Package {
|
||||
|
||||
const maxTime = 30 * time.Second
|
||||
|
||||
var pkgExts = [...]string{".a", ".o"} // keep in sync with gcimporter.go
|
||||
|
||||
func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
|
||||
dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
|
||||
list, err := os.ReadDir(dirname)
|
||||
@ -134,6 +142,129 @@ func TestImportTestdata(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportTypeparamTests(t *testing.T) {
|
||||
// This test doesn't yet work with the unified export format.
|
||||
if goexperiment.Unified {
|
||||
t.Skip("unified export data format is currently unsupported")
|
||||
}
|
||||
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
tmpdir := mktmpdir(t)
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
// Check go files in test/typeparam, except those that fail for a known
|
||||
// reason.
|
||||
rootDir := filepath.Join(runtime.GOROOT(), "test", "typeparam")
|
||||
list, err := os.ReadDir(rootDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
skip := map[string]string{
|
||||
"equal.go": "inconsistent embedded sorting", // TODO(rfindley): investigate this.
|
||||
"nested.go": "fails to compile", // TODO(rfindley): investigate this.
|
||||
}
|
||||
|
||||
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) {
|
||||
if reason, ok := skip[entry.Name()]; ok {
|
||||
t.Skip(reason)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
// Compile and import, and compare the resulting package with the package
|
||||
// that was type-checked directly.
|
||||
compile(t, rootDir, entry.Name(), filepath.Join(tmpdir, "testdata"))
|
||||
pkgName := strings.TrimSuffix(entry.Name(), ".go")
|
||||
imported := importPkg(t, "./testdata/"+pkgName, tmpdir)
|
||||
checked := checkFile(t, filename, src)
|
||||
|
||||
seen := make(map[string]bool)
|
||||
for _, name := range imported.Scope().Names() {
|
||||
if !token.IsExported(name) {
|
||||
continue // ignore synthetic names like .inittask and .dict.*
|
||||
}
|
||||
seen[name] = true
|
||||
|
||||
importedObj := imported.Scope().Lookup(name)
|
||||
got := types.ObjectString(importedObj, types.RelativeTo(imported))
|
||||
got = sanitizeObjectString(got)
|
||||
|
||||
checkedObj := checked.Scope().Lookup(name)
|
||||
if checkedObj == nil {
|
||||
t.Fatalf("imported object %q was not type-checked", name)
|
||||
}
|
||||
want := types.ObjectString(checkedObj, types.RelativeTo(checked))
|
||||
want = sanitizeObjectString(want)
|
||||
|
||||
if got != want {
|
||||
t.Errorf("imported %q as %q, want %q", name, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range checked.Scope().Names() {
|
||||
if !token.IsExported(name) || seen[name] {
|
||||
continue
|
||||
}
|
||||
t.Errorf("did not import object %q", name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// sanitizeObjectString removes type parameter debugging markers from an object
|
||||
// string, to normalize it for comparison.
|
||||
// TODO(rfindley): this should not be necessary.
|
||||
func sanitizeObjectString(s string) string {
|
||||
var runes []rune
|
||||
for _, r := range s {
|
||||
if r == '#' {
|
||||
continue // trim instance markers
|
||||
}
|
||||
if '₀' <= r && r < '₀'+10 {
|
||||
continue // trim type parameter subscripts
|
||||
}
|
||||
runes = append(runes, r)
|
||||
}
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
func checkFile(t *testing.T, filename string, src []byte) *types.Package {
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, filename, src, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
config := types.Config{
|
||||
Importer: importer.Default(),
|
||||
}
|
||||
pkg, err := config.Check("", fset, []*ast.File{f}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
|
||||
func TestVersionHandling(t *testing.T) {
|
||||
skipSpecialPlatforms(t)
|
||||
|
||||
|
@ -176,6 +176,8 @@ func embeddedFieldIdent(e ast.Expr) *ast.Ident {
|
||||
return e.Sel
|
||||
case *ast.IndexExpr:
|
||||
return embeddedFieldIdent(e.X)
|
||||
case *ast.MultiIndexExpr:
|
||||
return embeddedFieldIdent(e.X)
|
||||
}
|
||||
return nil // invalid embedded field
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user