"regexp"
"sort"
"strings"
+ "sync"
"testing"
. "cmd/compile/internal/types2"
// nopos indicates an unknown position
var nopos syntax.Pos
-func parse(path, src string) (*syntax.File, error) {
- errh := func(error) {} // dummy error handler so that parsing continues in presence of errors
- return syntax.Parse(syntax.NewFileBase(path), strings.NewReader(src), errh, nil, 0)
-}
-
-func mustParse(path, src string) *syntax.File {
- f, err := parse(path, src)
+func mustParse(src string) *syntax.File {
+ f, err := syntax.Parse(syntax.NewFileBase(pkgName(src)), strings.NewReader(src), nil, nil, 0)
if err != nil {
panic(err) // so we don't need to pass *testing.T
}
return f
}
-func typecheck(path, src string, conf *Config, info *Info) (*Package, error) {
- f, err := parse(path, src)
- if f == nil { // ignore errors unless f is nil
- return nil, err
- }
+func typecheck(src string, conf *Config, info *Info) (*Package, error) {
+ f := mustParse(src)
if conf == nil {
conf = &Config{
Error: func(err error) {}, // collect all errors
return conf.Check(f.PkgName.Value, []*syntax.File{f}, info)
}
-func mustTypecheck(path, src string, conf *Config, info *Info) *Package {
- pkg, err := typecheck(path, src, conf, info)
+func mustTypecheck(src string, conf *Config, info *Info) *Package {
+ pkg, err := typecheck(src, conf, info)
if err != nil {
panic(err) // so we don't need to pass *testing.T
}
return pkg
}
+// pkgName extracts the package name from src, which must contain a package header.
+func pkgName(src string) string {
+ const kw = "package "
+ if i := strings.Index(src, kw); i >= 0 {
+ after := src[i+len(kw):]
+ n := len(after)
+ if i := strings.IndexAny(after, "\n\t ;/"); i >= 0 {
+ n = i
+ }
+ return after[:n]
+ }
+ panic("missing package header: " + src)
+}
+
func TestValuesInfo(t *testing.T) {
var tests = []struct {
src string
info := Info{
Types: make(map[syntax.Expr]TypeAndValue),
}
- name := mustTypecheck("ValuesInfo", test.src, nil, &info).Name()
+ name := mustTypecheck(test.src, nil, &info).Name()
// look for expression
var expr syntax.Expr
{`package issue47243_i; var x int32; var _ = 1 << (2 << x)`, `(2 << x)`, `untyped int`},
{`package issue47243_j; var x int32; var _ = 1 << (2 << x)`, `2`, `untyped int`},
- // tests for broken code that doesn't parse or type-check
+ // tests for broken code that doesn't type-check
{brokenPkg + `x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`},
{brokenPkg + `x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`},
{brokenPkg + `x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a, f: b,}}`, `b`, `string`},
{`package u3c; type _ interface{int | string | ~bool}`, `int | string`, `int | string`},
{`package u3c; type _ interface{int | string | ~bool}`, `~bool`, `~bool`},
{`package u3c; type _ interface{int | string | ~float64|~bool}`, `int | string | ~float64`, `int | string | ~float64`},
+
+ // reverse type inference
+ {`package r1; var _ func(int) = g; func g[P any](P) {}`, `g`, `func(int)`},
+ {`package r2; var _ func(int) = g[int]; func g[P any](P) {}`, `g`, `func[P any](P)`}, // go.dev/issues/60212
+ {`package r3; var _ func(int) = g[int]; func g[P any](P) {}`, `g[int]`, `func(int)`},
+ {`package r4; var _ func(int, string) = g; func g[P, Q any](P, Q) {}`, `g`, `func(int, string)`},
+ {`package r5; var _ func(int, string) = g[int]; func g[P, Q any](P, Q) {}`, `g`, `func[P, Q any](P, Q)`}, // go.dev/issues/60212
+ {`package r6; var _ func(int, string) = g[int]; func g[P, Q any](P, Q) {}`, `g[int]`, `func(int, string)`},
+
+ {`package s1; func _() { f(g) }; func f(func(int)) {}; func g[P any](P) {}`, `g`, `func(int)`},
+ {`package s2; func _() { f(g[int]) }; func f(func(int)) {}; func g[P any](P) {}`, `g`, `func[P any](P)`}, // go.dev/issues/60212
+ {`package s3; func _() { f(g[int]) }; func f(func(int)) {}; func g[P any](P) {}`, `g[int]`, `func(int)`},
+ {`package s4; func _() { f(g) }; func f(func(int, string)) {}; func g[P, Q any](P, Q) {}`, `g`, `func(int, string)`},
+ {`package s5; func _() { f(g[int]) }; func f(func(int, string)) {}; func g[P, Q any](P, Q) {}`, `g`, `func[P, Q any](P, Q)`}, // go.dev/issues/60212
+ {`package s6; func _() { f(g[int]) }; func f(func(int, string)) {}; func g[P, Q any](P, Q) {}`, `g[int]`, `func(int, string)`},
+
+ {`package s7; func _() { f(g, h) }; func f[P any](func(int, P), func(P, string)) {}; func g[P any](P, P) {}; func h[P, Q any](P, Q) {}`, `g`, `func(int, int)`},
+ {`package s8; func _() { f(g, h) }; func f[P any](func(int, P), func(P, string)) {}; func g[P any](P, P) {}; func h[P, Q any](P, Q) {}`, `h`, `func(int, string)`},
+ {`package s9; func _() { f(g, h[int]) }; func f[P any](func(int, P), func(P, string)) {}; func g[P any](P, P) {}; func h[P, Q any](P, Q) {}`, `h`, `func[P, Q any](P, Q)`}, // go.dev/issues/60212
+ {`package s10; func _() { f(g, h[int]) }; func f[P any](func(int, P), func(P, string)) {}; func g[P any](P, P) {}; func h[P, Q any](P, Q) {}`, `h[int]`, `func(int, string)`},
}
for _, test := range tests {
info := Info{Types: make(map[syntax.Expr]TypeAndValue)}
var name string
if strings.HasPrefix(test.src, brokenPkg) {
- pkg, err := typecheck("TypesInfo", test.src, nil, &info)
+ pkg, err := typecheck(test.src, nil, &info)
if err == nil {
t.Errorf("package %s: expected to fail but passed", pkg.Name())
continue
name = pkg.Name()
}
} else {
- name = mustTypecheck("TypesInfo", test.src, nil, &info).Name()
+ name = mustTypecheck(test.src, nil, &info).Name()
}
// look for expression type
// check that type is correct
if got := typ.String(); got != test.typ {
- t.Errorf("package %s: got %s; want %s", name, got, test.typ)
+ t.Errorf("package %s: expr = %s: got %s; want %s", name, test.expr, got, test.typ)
}
}
}
{`package issue51803; func foo[T any](T) {}; func _() { foo[int]( /* leave arg away on purpose */ ) }`,
[]testInst{{`foo`, []string{`int`}, `func(int)`}},
},
+
+ // reverse type inference
+ {`package reverse1a; var f func(int) = g; func g[P any](P) {}`,
+ []testInst{{`g`, []string{`int`}, `func(int)`}},
+ },
+ {`package reverse1b; func f(func(int)) {}; func g[P any](P) {}; func _() { f(g) }`,
+ []testInst{{`g`, []string{`int`}, `func(int)`}},
+ },
+ {`package reverse2a; var f func(int, string) = g; func g[P, Q any](P, Q) {}`,
+ []testInst{{`g`, []string{`int`, `string`}, `func(int, string)`}},
+ },
+ {`package reverse2b; func f(func(int, string)) {}; func g[P, Q any](P, Q) {}; func _() { f(g) }`,
+ []testInst{{`g`, []string{`int`, `string`}, `func(int, string)`}},
+ },
+ {`package reverse2c; func f(func(int, string)) {}; func g[P, Q any](P, Q) {}; func _() { f(g[int]) }`,
+ []testInst{{`g`, []string{`int`, `string`}, `func(int, string)`}},
+ },
+ // reverse3a not possible (cannot assign to generic function outside of argument passing)
+ {`package reverse3b; func f[R any](func(int) R) {}; func g[P any](P) string { return "" }; func _() { f(g) }`,
+ []testInst{
+ {`f`, []string{`string`}, `func(func(int) string)`},
+ {`g`, []string{`int`}, `func(int) string`},
+ },
+ },
+ {`package reverse4a; var _, _ func([]int, *float32) = g, h; func g[P, Q any]([]P, *Q) {}; func h[R any]([]R, *float32) {}`,
+ []testInst{
+ {`g`, []string{`int`, `float32`}, `func([]int, *float32)`},
+ {`h`, []string{`int`}, `func([]int, *float32)`},
+ },
+ },
+ {`package reverse4b; func f(_, _ func([]int, *float32)) {}; func g[P, Q any]([]P, *Q) {}; func h[R any]([]R, *float32) {}; func _() { f(g, h) }`,
+ []testInst{
+ {`g`, []string{`int`, `float32`}, `func([]int, *float32)`},
+ {`h`, []string{`int`}, `func([]int, *float32)`},
+ },
+ },
+ {`package issue59956; func f(func(int), func(string), func(bool)) {}; func g[P any](P) {}; func _() { f(g, g, g) }`,
+ []testInst{
+ {`g`, []string{`int`}, `func(int)`},
+ {`g`, []string{`string`}, `func(string)`},
+ {`g`, []string{`bool`}, `func(bool)`},
+ },
+ },
}
for _, test := range tests {
imports := make(testImporter)
- conf := Config{
- Importer: imports,
- Error: func(error) {}, // ignore errors
- }
+ conf := Config{Importer: imports}
instMap := make(map[*syntax.Name]Instance)
useMap := make(map[*syntax.Name]Object)
makePkg := func(src string) *Package {
- pkg, _ := typecheck("p.go", src, &conf, &Info{Instances: instMap, Uses: useMap})
+ pkg, err := typecheck(src, &conf, &Info{Instances: instMap, Uses: useMap})
+ // allow error for issue51803
+ if err != nil && (pkg == nil || pkg.Name() != "issue51803") {
+ t.Fatal(err)
+ }
imports[pkg.Name()] = pkg
return pkg
}
info := Info{
Defs: make(map[*syntax.Name]Object),
}
- name := mustTypecheck("DefsInfo", test.src, nil, &info).Name()
+ name := mustTypecheck(test.src, nil, &info).Name()
// find object
var def Object
info := Info{
Uses: make(map[*syntax.Name]Object),
}
- name := mustTypecheck("UsesInfo", test.src, nil, &info).Name()
+ name := mustTypecheck(test.src, nil, &info).Name()
// find object
var use Object
func (r *N[C]) n() { }
`
- f := mustParse("p.go", src)
+ f := mustParse(src)
info := Info{
Defs: make(map[*syntax.Name]Object),
Uses: make(map[*syntax.Name]Object),
info := Info{
Implicits: make(map[syntax.Node]Object),
}
- name := mustTypecheck("ImplicitsInfo", test.src, nil, &info).Name()
+ name := mustTypecheck(test.src, nil, &info).Name()
// the test cases expect at most one Implicits entry
if len(info.Implicits) > 1 {
}
}
+func TestPkgNameOf(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ const src = `
+package p
+
+import (
+ . "os"
+ _ "io"
+ "math"
+ "path/filepath"
+ snort "sort"
+)
+
+// avoid imported and not used errors
+var (
+ _ = Open // os.Open
+ _ = math.Sin
+ _ = filepath.Abs
+ _ = snort.Ints
+)
+`
+
+ var tests = []struct {
+ path string // path string enclosed in "'s
+ want string
+ }{
+ {`"os"`, "."},
+ {`"io"`, "_"},
+ {`"math"`, "math"},
+ {`"path/filepath"`, "filepath"},
+ {`"sort"`, "snort"},
+ }
+
+ f := mustParse(src)
+ info := Info{
+ Defs: make(map[*syntax.Name]Object),
+ Implicits: make(map[syntax.Node]Object),
+ }
+ var conf Config
+ conf.Importer = defaultImporter()
+ _, err := conf.Check("p", []*syntax.File{f}, &info)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // map import paths to importDecl
+ imports := make(map[string]*syntax.ImportDecl)
+ for _, d := range f.DeclList {
+ if imp, _ := d.(*syntax.ImportDecl); imp != nil {
+ imports[imp.Path.Value] = imp
+ }
+ }
+
+ for _, test := range tests {
+ imp := imports[test.path]
+ if imp == nil {
+ t.Fatalf("invalid test case: import path %s not found", test.path)
+ }
+ got := info.PkgNameOf(imp)
+ if got == nil {
+ t.Fatalf("import %s: package name not found", test.path)
+ }
+ if got.Name() != test.want {
+ t.Errorf("import %s: got %s; want %s", test.path, got.Name(), test.want)
+ }
+ }
+
+ // test non-existing importDecl
+ if got := info.PkgNameOf(new(syntax.ImportDecl)); got != nil {
+ t.Errorf("got %s for non-existing import declaration", got.Name())
+ }
+}
+
func predString(tv TypeAndValue) string {
var buf strings.Builder
pred := func(b bool, s string) {
for _, test := range tests {
info := Info{Types: make(map[syntax.Expr]TypeAndValue)}
- name := mustTypecheck("PredicatesInfo", test.src, nil, &info).Name()
+ name := mustTypecheck(test.src, nil, &info).Name()
// look for expression predicates
got := "<missing>"
for _, test := range tests {
info := Info{Scopes: make(map[syntax.Node]*Scope)}
- name := mustTypecheck("ScopesInfo", test.src, nil, &info).Name()
+ name := mustTypecheck(test.src, nil, &info).Name()
// number of scopes must match
if len(info.Scopes) != len(test.scopes) {
for _, test := range tests {
info := Info{}
- name := mustTypecheck("InitOrderInfo", test.src, nil, &info).Name()
+ name := mustTypecheck(test.src, nil, &info).Name()
// number of initializers must match
if len(info.InitOrder) != len(test.inits) {
}
func TestMultiFileInitOrder(t *testing.T) {
- fileA := mustParse("", `package main; var a = 1`)
- fileB := mustParse("", `package main; var b = 2`)
+ fileA := mustParse(`package main; var a = 1`)
+ fileB := mustParse(`package main; var b = 2`)
// The initialization order must not depend on the parse
// order of the files, only on the presentation order to
var info Info
check := NewChecker(&conf, pkg, &info)
- for i, src := range sources {
- filename := fmt.Sprintf("sources%d", i)
- f := mustParse(filename, src)
- if err := check.Files([]*syntax.File{f}); err != nil {
+ for _, src := range sources {
+ if err := check.Files([]*syntax.File{mustParse(src)}); err != nil {
t.Error(err)
}
}
imports := make(testImporter)
conf := Config{Importer: imports}
makePkg := func(path, src string) {
- pkg := mustTypecheck(path, src, &conf, &Info{Selections: selections})
+ pkg := mustTypecheck(src, &conf, &Info{Selections: selections})
imports[path] = pkg
}
Importer: imports,
}
makePkg := func(path, src string) {
- f := mustParse(path, src)
- pkg, _ := conf.Check(path, []*syntax.File{f}, nil) // errors logged via conf.Error
- imports[path] = pkg
+ imports[path], _ = conf.Check(path, []*syntax.File{mustParse(src)}, nil) // errors logged via conf.Error
}
const libSrc = `
Importer: imports,
}
makePkg := func(path, src string) {
- f := mustParse(path, src)
- pkg, _ := conf.Check(path, []*syntax.File{f}, nil) // errors logged via conf.Error
- imports[path] = pkg
+ imports[path], _ = conf.Check(path, []*syntax.File{mustParse(src)}, nil) // errors logged via conf.Error
}
const libSrc = `
}
for _, test := range tests {
- pkg := mustTypecheck("test", "package p;"+test.src, nil, nil)
+ pkg := mustTypecheck("package p;"+test.src, nil, nil)
obj := pkg.Scope().Lookup("a")
if obj == nil {
type Instance = *Tree[int]
`
- f := mustParse("foo.go", src)
+ f := mustParse(src)
pkg := NewPackage("pkg", f.PkgName.Value)
if err := NewChecker(nil, pkg, nil).Files([]*syntax.File{f}); err != nil {
panic(err)
conf := Config{Importer: imports}
var info Info
makePkg := func(path, src string) {
- f := mustParse(path, src)
var err error
- imports[path], err = conf.Check(path, []*syntax.File{f}, &info)
+ imports[path], err = conf.Check(path, []*syntax.File{mustParse(src)}, &info)
if err != nil {
t.Fatal(err)
}
}
for _, test := range tests {
- pkg := mustTypecheck("test", "package p;"+test.src, nil, nil)
+ pkg := mustTypecheck("package p;"+test.src, nil, nil)
X := pkg.Scope().Lookup("X")
Y := pkg.Scope().Lookup("Y")
if X == nil || Y == nil {
}
}
+func TestIssue61737(t *testing.T) {
+ // This test verifies that it is possible to construct invalid interfaces
+ // containing duplicate methods using the go/types API.
+ //
+ // It must be possible for importers to construct such invalid interfaces.
+ // Previously, this panicked.
+
+ sig1 := NewSignatureType(nil, nil, nil, NewTuple(NewParam(nopos, nil, "", Typ[Int])), nil, false)
+ sig2 := NewSignatureType(nil, nil, nil, NewTuple(NewParam(nopos, nil, "", Typ[String])), nil, false)
+
+ methods := []*Func{
+ NewFunc(nopos, nil, "M", sig1),
+ NewFunc(nopos, nil, "M", sig2),
+ }
+
+ embeddedMethods := []*Func{
+ NewFunc(nopos, nil, "M", sig2),
+ }
+ embedded := NewInterfaceType(embeddedMethods, nil)
+ iface := NewInterfaceType(methods, []Type{embedded})
+ iface.NumMethods() // unlike go/types, there is no Complete() method, so we complete implicitly
+}
+
func TestIssue15305(t *testing.T) {
const src = "package p; func f() int16; var _ = f(undef)"
- f := mustParse("issue15305.go", src)
+ f := mustParse(src)
conf := Config{
Error: func(err error) {}, // allow errors
}
// types for composite literal expressions and composite literal type
// expressions.
func TestCompositeLitTypes(t *testing.T) {
- for _, test := range []struct {
+ for i, test := range []struct {
lit, typ string
}{
{`[16]byte{}`, `[16]byte`},
{`struct{}{}`, `struct{}`},
{`struct{x, y int; z complex128}{}`, `struct{x int; y int; z complex128}`},
} {
- f := mustParse(test.lit, "package p; var _ = "+test.lit)
+ f := mustParse(fmt.Sprintf("package p%d; var _ = %s", i, test.lit))
types := make(map[syntax.Expr]TypeAndValue)
if _, err := new(Config).Check("p", []*syntax.File{f}, &Info{Types: types}); err != nil {
t.Fatalf("%s: %v", test.lit, err)
func f(x int) { y := x; print(y) }
`
- f := mustParse("src", src)
+ f := mustParse(src)
info := &Info{
Defs: make(map[*syntax.Name]Object),
var v T = c
func f(x T) T { return foo.F(x) }
`
- f := mustParse("src", src)
+ f := mustParse(src)
files := []*syntax.File{f}
// type-check using all possible importers
func TestInstantiate(t *testing.T) {
// eventually we like more tests but this is a start
const src = "package p; type T[P any] *T[P]"
- pkg := mustTypecheck(".", src, nil, nil)
+ pkg := mustTypecheck(src, nil, nil)
// type T should have one type parameter
T := pkg.Scope().Lookup("T").Type().(*Named)
}
}
+func TestInstantiateConcurrent(t *testing.T) {
+ const src = `package p
+
+type I[P any] interface {
+ m(P)
+ n() P
+}
+
+type J = I[int]
+
+type Nested[P any] *interface{b(P)}
+
+type K = Nested[string]
+`
+ pkg := mustTypecheck(src, nil, nil)
+
+ insts := []*Interface{
+ pkg.Scope().Lookup("J").Type().Underlying().(*Interface),
+ pkg.Scope().Lookup("K").Type().Underlying().(*Pointer).Elem().(*Interface),
+ }
+
+ // Use the interface instances concurrently.
+ for _, inst := range insts {
+ var (
+ counts [2]int // method counts
+ methods [2][]string // method strings
+ )
+ var wg sync.WaitGroup
+ for i := 0; i < 2; i++ {
+ i := i
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+
+ counts[i] = inst.NumMethods()
+ for mi := 0; mi < counts[i]; mi++ {
+ methods[i] = append(methods[i], inst.Method(mi).String())
+ }
+ }()
+ }
+ wg.Wait()
+
+ if counts[0] != counts[1] {
+ t.Errorf("mismatching method counts for %s: %d vs %d", inst, counts[0], counts[1])
+ continue
+ }
+ for i := 0; i < counts[0]; i++ {
+ if m0, m1 := methods[0][i], methods[1][i]; m0 != m1 {
+ t.Errorf("mismatching methods for %s: %s vs %s", inst, m0, m1)
+ }
+ }
+ }
+}
+
func TestInstantiateErrors(t *testing.T) {
tests := []struct {
src string // by convention, T must be the type being instantiated
for _, test := range tests {
src := "package p; " + test.src
- pkg := mustTypecheck(".", src, nil, nil)
+ pkg := mustTypecheck(src, nil, nil)
T := pkg.Scope().Lookup("T").Type().(*Named)
imports := make(testImporter)
conf := Config{Importer: imports}
makePkg := func(src string) {
- f := mustParse("", src)
+ f := mustParse(src)
name := f.PkgName.Value
pkg, err := conf.Check(name, []*syntax.File{f}, nil)
if err != nil {
info := &Info{
Defs: make(map[*syntax.Name]Object),
}
- f := mustParse("p.go", src)
+ f := mustParse(src)
conf := Config{}
pkg, err := conf.Check(f.PkgName.Value, []*syntax.File{f}, info)
if err != nil {
type Bad Bad // invalid type
`
- f := mustParse("p.go", src)
+ f := mustParse(src)
conf := Config{Error: func(error) {}}
pkg, _ := conf.Check(f.PkgName.Value, []*syntax.File{f}, nil)
func (V4) M()
`
- pkg := mustTypecheck("p.go", src, nil, nil)
+ pkg := mustTypecheck(src, nil, nil)
T := pkg.Scope().Lookup("T").Type().Underlying().(*Interface)
lookup := func(name string) (*Func, bool) {
// V4 has no method m but has M. Should not report wrongType.
checkMissingMethod("V4", false)
}
+
+func TestErrorURL(t *testing.T) {
+ conf := Config{ErrorURL: " [go.dev/e/%s]"}
+
+ // test case for a one-line error
+ const src1 = `
+package p
+var _ T
+`
+ _, err := typecheck(src1, &conf, nil)
+ if err == nil || !strings.HasSuffix(err.Error(), " [go.dev/e/UndeclaredName]") {
+ t.Errorf("src1: unexpected error: got %v", err)
+ }
+
+ // test case for a multi-line error
+ const src2 = `
+package p
+func f() int { return 0 }
+var _ = f(1, 2)
+`
+ _, err = typecheck(src2, &conf, nil)
+ if err == nil || !strings.Contains(err.Error(), " [go.dev/e/WrongArgCount]\n") {
+ t.Errorf("src1: unexpected error: got %v", err)
+ }
+}
+
+func TestFileVersions(t *testing.T) {
+ for _, test := range []struct {
+ moduleVersion string
+ fileVersion string
+ wantVersion string
+ }{
+ {"", "", ""}, // no versions specified
+ {"go1.19", "", "go1.19"}, // module version specified
+ {"", "go1.20", ""}, // file upgrade ignored
+ {"go1.19", "go1.20", "go1.20"}, // file upgrade permitted
+ {"go1.20", "go1.19", "go1.20"}, // file downgrade not permitted
+ {"go1.21", "go1.19", "go1.19"}, // file downgrade permitted (module version is >= go1.21)
+ } {
+ var src string
+ if test.fileVersion != "" {
+ src = "//go:build " + test.fileVersion + "\n"
+ }
+ src += "package p"
+
+ conf := Config{GoVersion: test.moduleVersion}
+ versions := make(map[*syntax.PosBase]string)
+ var info Info
+ info.FileVersions = versions
+ mustTypecheck(src, &conf, &info)
+
+ n := 0
+ for _, v := range info.FileVersions {
+ want := test.wantVersion
+ if v != want {
+ t.Errorf("%q: unexpected file version: got %v, want %v", src, v, want)
+ }
+ n++
+ }
+ if n != 1 {
+ t.Errorf("%q: incorrect number of map entries: got %d", src, n)
+ }
+ }
+}