]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/go/types/api_test.go
go/types, types2: remove local version processing in favor of go/version
[gostls13.git] / src / go / types / api_test.go
index 825f30585b1597a991b59ecc2cf6c85a4272d219..594b92bb237f9217c42bd85b2561baddab6af017 100644 (file)
@@ -11,11 +11,13 @@ import (
        "go/importer"
        "go/parser"
        "go/token"
+       "internal/goversion"
        "internal/testenv"
        "reflect"
        "regexp"
        "sort"
        "strings"
+       "sync"
        "testing"
 
        . "go/types"
@@ -24,12 +26,8 @@ import (
 // nopos indicates an unknown position
 var nopos token.Pos
 
-func parse(fset *token.FileSet, src string) (*ast.File, error) {
-       return parser.ParseFile(fset, pkgName(src), src, 0)
-}
-
 func mustParse(fset *token.FileSet, src string) *ast.File {
-       f, err := parse(fset, src)
+       f, err := parser.ParseFile(fset, pkgName(src), src, 0)
        if err != nil {
                panic(err) // so we don't need to pass *testing.T
        }
@@ -38,10 +36,7 @@ func mustParse(fset *token.FileSet, src string) *ast.File {
 
 func typecheck(src string, conf *Config, info *Info) (*Package, error) {
        fset := token.NewFileSet()
-       f, err := parse(fset, src)
-       if f == nil { // ignore errors unless f is nil
-               return nil, err
-       }
+       f := mustParse(fset, src)
        if conf == nil {
                conf = &Config{
                        Error:    func(err error) {}, // collect all errors
@@ -52,14 +47,7 @@ func typecheck(src string, conf *Config, info *Info) (*Package, error) {
 }
 
 func mustTypecheck(src string, conf *Config, info *Info) *Package {
-       fset := token.NewFileSet()
-       f := mustParse(fset, src)
-       if conf == nil {
-               conf = &Config{
-                       Importer: importer.Default(),
-               }
-       }
-       pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
+       pkg, err := typecheck(src, conf, info)
        if err != nil {
                panic(err) // so we don't need to pass *testing.T
        }
@@ -339,10 +327,10 @@ func TestTypesInfo(t *testing.T) {
                {`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
                {broken + `x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`},
                {broken + `x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`},
-               {broken + `x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a; f: b;}}`, `b`, `string`},
+               {broken + `x2; func _() { var a, b string; type x struct {f string}; z := &x{f: a, f: b,}}`, `b`, `string`},
                {broken + `x3; var x = panic("");`, `panic`, `func(interface{})`},
                {`package x4; func _() { panic("") }`, `panic`, `func(interface{})`},
                {broken + `x5; func _() { var x map[string][...]int; x = map[string][...]int{"": {1,2,3}} }`, `x`, `map[string]invalid type`},
@@ -395,6 +383,26 @@ func TestTypesInfo(t *testing.T) {
                {`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 {
@@ -428,7 +436,7 @@ func TestTypesInfo(t *testing.T) {
 
                // 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)
                }
        }
 }
@@ -564,18 +572,62 @@ type T[P any] []P
                {`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[*ast.Ident]Instance)
                useMap := make(map[*ast.Ident]Object)
                makePkg := func(src string) *Package {
-                       pkg, _ := typecheck(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
                }
@@ -909,6 +961,81 @@ func TestImplicitsInfo(t *testing.T) {
        }
 }
 
+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"},
+       }
+
+       fset := token.NewFileSet()
+       f := mustParse(fset, src)
+       info := Info{
+               Defs:      make(map[*ast.Ident]Object),
+               Implicits: make(map[ast.Node]Object),
+       }
+       var conf Config
+       conf.Importer = importer.Default()
+       _, err := conf.Check("p", fset, []*ast.File{f}, &info)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       // map import paths to importDecl
+       imports := make(map[string]*ast.ImportSpec)
+       for _, s := range f.Decls[0].(*ast.GenDecl).Specs {
+               if imp, _ := s.(*ast.ImportSpec); 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(ast.ImportSpec)); 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) {
@@ -2021,6 +2148,29 @@ func TestIdenticalUnions(t *testing.T) {
        }
 }
 
+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.Complete()
+}
+
 func TestIssue15305(t *testing.T) {
        const src = "package p; func f() int16; var _ = f(undef)"
        fset := token.NewFileSet()
@@ -2250,6 +2400,60 @@ func TestInstantiate(t *testing.T) {
        }
 }
 
+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
@@ -2619,3 +2823,95 @@ func (V4) M()
        // V4 has no method m but has M. Should not report wrongType.
        checkMissingMethod("V4", false)
 }
+
+func TestErrorURL(t *testing.T) {
+       var conf Config
+       *stringFieldAddr(&conf, "_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 TestModuleVersion(t *testing.T) {
+       // version go1.dd must be able to typecheck go1.dd.0, go1.dd.1, etc.
+       goversion := fmt.Sprintf("go1.%d", goversion.Version)
+       for _, v := range []string{
+               goversion,
+               goversion + ".0",
+               goversion + ".1",
+               goversion + ".rc",
+       } {
+               conf := Config{GoVersion: v}
+               pkg := mustTypecheck("package p", &conf, nil)
+               if pkg.GoVersion() != conf.GoVersion {
+                       t.Errorf("got %s; want %s", pkg.GoVersion(), conf.GoVersion)
+               }
+       }
+}
+
+func TestFileVersions(t *testing.T) {
+       for _, test := range []struct {
+               goVersion   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)
+
+               // versions containing release numbers
+               // (file versions containing release numbers are considered invalid)
+               {"go1.19.0", "", "go1.19.0"},         // no file version specified
+               {"go1.20", "go1.20.1", "go1.20"},     // file upgrade ignored
+               {"go1.20.1", "go1.20", "go1.20.1"},   // file upgrade ignored
+               {"go1.20.1", "go1.21", "go1.21"},     // file upgrade permitted
+               {"go1.20.1", "go1.19", "go1.20.1"},   // file downgrade not permitted
+               {"go1.21.1", "go1.19.1", "go1.21.1"}, // file downgrade not permitted (invalid file version)
+               {"go1.21.1", "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.goVersion}
+               versions := make(map[*ast.File]string)
+               var info Info
+               info.FileVersions = versions
+               mustTypecheck(src, &conf, &info)
+
+               n := 0
+               for _, v := range versions {
+                       want := test.wantVersion
+                       if v != want {
+                               t.Errorf("%q: unexpected file version: got %q, want %q", src, v, want)
+                       }
+                       n++
+               }
+               if n != 1 {
+                       t.Errorf("%q: incorrect number of map entries: got %d", src, n)
+               }
+       }
+}