package types_test
import (
- "bytes"
"fmt"
"go/ast"
"go/importer"
"go/parser"
"go/token"
"internal/testenv"
+ "regexp"
"sort"
"strings"
"testing"
. "go/types"
)
-func mustParse(t *testing.T, src string) *ast.File {
- f, err := parser.ParseFile(fset, "", src, 0)
- if err != nil {
- t.Fatal(err)
- }
- return f
-}
func TestIssue5770(t *testing.T) {
- f := mustParse(t, `package p; type S struct{T}`)
- conf := Config{Importer: importer.Default()}
- _, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash
- want := "undeclared name: T"
+ _, err := typecheck(`package p; type S struct{T}`, nil, nil)
+ const want = "undefined: T"
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("got: %v; want: %s", err, want)
}
_ = (interface{})("foo")
_ = (interface{})(nil)
)`
- f := mustParse(t, src)
-
- var conf Config
types := make(map[ast.Expr]TypeAndValue)
- _, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
- if err != nil {
- t.Fatal(err)
- }
+ mustTypecheck(src, nil, &Info{Types: types})
for x, tv := range types {
var want Type
return 0
}
`
- f := mustParse(t, src)
-
- var conf Config
types := make(map[ast.Expr]TypeAndValue)
- _, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
- if err != nil {
- t.Fatal(err)
- }
+ mustTypecheck(src, nil, &Info{Types: types})
want := Typ[Int]
n := 0
func (T) m() (res bool) { return }
type T struct{} // receiver type after method declaration
`
- f := mustParse(t, src)
+ f := mustParse(fset, src)
var conf Config
defs := make(map[*ast.Ident]Object)
_, _, _ = x, y, z // uses x, y, z
}
`
- f := mustParse(t, src)
+ // We need a specific fileset in this test below for positions.
+ // Cannot use typecheck helper.
+ fset := token.NewFileSet()
+ f := mustParse(fset, src)
const want = `L3 defs func p._()
L4 defs const w untyped int
defs := make(map[*ast.Ident]Object)
uses := make(map[*ast.Ident]Object)
_, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs, Uses: uses})
- if s := fmt.Sprint(err); !strings.HasSuffix(s, "cannot assign to w") {
+ if s := err.Error(); !strings.HasSuffix(s, "cannot assign to w") {
t.Errorf("Check: unexpected error: %s", s)
}
}
`
f := func(test, src string) {
- f := mustParse(t, src)
- cfg := Config{Importer: importer.Default()}
- info := Info{Uses: make(map[*ast.Ident]Object)}
- _, err := cfg.Check("main", fset, []*ast.File{f}, &info)
- if err != nil {
- t.Fatal(err)
- }
+ info := &Info{Uses: make(map[*ast.Ident]Object)}
+ mustTypecheck(src, nil, info)
var pkg *Package
count := 0
}
func TestIssue22525(t *testing.T) {
- f := mustParse(t, `package p; func f() { var a, b, c, d, e int }`)
+ const src = `package p; func f() { var a, b, c, d, e int }`
got := "\n"
conf := Config{Error: func(err error) { got += err.Error() + "\n" }}
- conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash
+ typecheck(src, &conf, nil) // do not crash
want := `
-1:27: a declared but not used
-1:30: b declared but not used
-1:33: c declared but not used
-1:36: d declared but not used
-1:39: e declared but not used
+p:1:27: a declared and not used
+p:1:30: b declared and not used
+p:1:33: c declared and not used
+p:1:36: d declared and not used
+p:1:39: e declared and not used
`
if got != want {
t.Errorf("got: %swant: %s", got, want)
`struct { *I }`,
`struct { a int; b Missing; *Missing }`,
} {
- f := mustParse(t, prefix+src)
+ f := mustParse(fset, prefix+src)
cfg := Config{Importer: importer.Default(), Error: func(err error) {}}
info := &Info{Types: make(map[ast.Expr]TypeAndValue)}
// compute original file ASTs
var orig [len(sources)]*ast.File
for i, src := range sources {
- orig[i] = mustParse(t, src)
+ orig[i] = mustParse(fset, src)
}
// run the test for all order permutations of the incoming files
}
}
if obj == nil {
- t.Fatal("interface not found")
+ t.Fatal("object X not found")
}
- iface := obj.Type().Underlying().(*Interface) // I must be an interface
+ iface := obj.Type().Underlying().(*Interface) // object X must be an interface
// Each iface method m is embedded; and m's receiver base type name
// must match the method's name per the choice in the source file.
}
func TestIssue29029(t *testing.T) {
- f1 := mustParse(t, `package p; type A interface { M() }`)
- f2 := mustParse(t, `package p; var B interface { A }`)
+ f1 := mustParse(fset, `package p; type A interface { M() }`)
+ f2 := mustParse(fset, `package p; var B interface { A }`)
// printInfo prints the *Func definitions recorded in info, one *Func per line.
printInfo := func(info *Info) string {
- var buf bytes.Buffer
+ var buf strings.Builder
for _, obj := range info.Defs {
if fn, ok := obj.(*Func); ok {
fmt.Fprintln(&buf, fn)
const asrc = `package a; type I interface{ M() }; type T struct { F interface { I } }`
const bsrc = `package b; import "a"; type T struct { F interface { a.I } }; var _ = a.T(T{})`
- a, err := pkgFor("a", asrc, nil)
- if err != nil {
- t.Fatalf("package %s failed to typecheck: %v", a.Name(), err)
- }
+ a := mustTypecheck(asrc, nil, nil)
- bast := mustParse(t, bsrc)
- conf := Config{Importer: importHelper{a}}
- b, err := conf.Check(bast.Name.Name, fset, []*ast.File{bast}, nil)
- if err != nil {
- t.Errorf("package %s failed to typecheck: %v", b.Name(), err)
- }
+ conf := Config{Importer: importHelper{pkg: a}}
+ mustTypecheck(bsrc, &conf, nil)
}
type importHelper struct {
- pkg *Package
+ pkg *Package
+ fallback Importer
}
func (h importHelper) Import(path string) (*Package, error) {
- if path != h.pkg.Path() {
+ if path == h.pkg.Path() {
+ return h.pkg, nil
+ }
+ if h.fallback == nil {
return nil, fmt.Errorf("got package path %q; want %q", path, h.pkg.Path())
}
- return h.pkg, nil
+ return h.fallback.Import(path)
}
// TestIssue34921 verifies that we don't update an imported type's underlying
var pkg *Package
for _, src := range sources {
- f := mustParse(t, src)
- conf := Config{Importer: importHelper{pkg}}
- res, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
- if err != nil {
- t.Errorf("%q failed to typecheck: %v", src, err)
- }
- pkg = res // res is imported by the next package in this test
+ conf := Config{Importer: importHelper{pkg: pkg}}
+ pkg = mustTypecheck(src, &conf, nil) // pkg imported by the next package in this test
}
}
// _ T2
// }
// }
- n1 := NewTypeName(token.NoPos, nil, "T1", nil)
+ n1 := NewTypeName(nopos, nil, "T1", nil)
T1 := NewNamed(n1, nil, nil)
- n2 := NewTypeName(token.NoPos, nil, "T2", nil)
+ n2 := NewTypeName(nopos, nil, "T2", nil)
T2 := NewNamed(n2, nil, nil)
- s1 := NewStruct([]*Var{NewField(token.NoPos, nil, "_", T2, false)}, nil)
+ s1 := NewStruct([]*Var{NewField(nopos, nil, "_", T2, false)}, nil)
T1.SetUnderlying(s1)
- s2 := NewStruct([]*Var{NewField(token.NoPos, nil, "_", T2, false)}, nil)
- s3 := NewStruct([]*Var{NewField(token.NoPos, nil, "_", s2, false)}, nil)
+ s2 := NewStruct([]*Var{NewField(nopos, nil, "_", T2, false)}, nil)
+ s3 := NewStruct([]*Var{NewField(nopos, nil, "_", s2, false)}, nil)
T2.SetUnderlying(s3)
// These calls must terminate (no endless recursion).
t.Errorf("got %q; want %q", got, want)
}
}
+
+func TestIssue43124(t *testing.T) {
+ // TODO(rFindley) move this to testdata by enhancing support for importing.
+
+ testenv.MustHaveGoBuild(t) // The go command is needed for the importer to determine the locations of stdlib .a files.
+
+ // All involved packages have the same name (template). Error messages should
+ // disambiguate between text/template and html/template by printing the full
+ // path.
+ const (
+ asrc = `package a; import "text/template"; func F(template.Template) {}; func G(int) {}`
+ bsrc = `
+package b
+
+import (
+ "a"
+ "html/template"
+)
+
+func _() {
+ // Packages should be fully qualified when there is ambiguity within the
+ // error string itself.
+ a.F(template /* ERRORx "cannot use.*html/template.* as .*text/template" */ .Template{})
+}
+`
+ csrc = `
+package c
+
+import (
+ "a"
+ "fmt"
+ "html/template"
+)
+
+// go.dev/issue/46905: make sure template is not the first package qualified.
+var _ fmt.Stringer = 1 // ERRORx "cannot use 1.*as fmt\\.Stringer"
+
+// Packages should be fully qualified when there is ambiguity in reachable
+// packages. In this case both a (and for that matter html/template) import
+// text/template.
+func _() { a.G(template /* ERRORx "cannot use .*html/template.*Template" */ .Template{}) }
+`
+
+ tsrc = `
+package template
+
+import "text/template"
+
+type T int
+
+// Verify that the current package name also causes disambiguation.
+var _ T = template /* ERRORx "cannot use.*text/template.* as T value" */.Template{}
+`
+ )
+
+ a := mustTypecheck(asrc, nil, nil)
+ imp := importHelper{pkg: a, fallback: importer.Default()}
+
+ withImporter := func(cfg *Config) {
+ cfg.Importer = imp
+ }
+
+ testFiles(t, []string{"b.go"}, [][]byte{[]byte(bsrc)}, false, withImporter)
+ testFiles(t, []string{"c.go"}, [][]byte{[]byte(csrc)}, false, withImporter)
+ testFiles(t, []string{"t.go"}, [][]byte{[]byte(tsrc)}, false, withImporter)
+}
+
+func TestIssue50646(t *testing.T) {
+ anyType := Universe.Lookup("any").Type()
+ comparableType := Universe.Lookup("comparable").Type()
+
+ if !Comparable(anyType) {
+ t.Error("any is not a comparable type")
+ }
+ if !Comparable(comparableType) {
+ t.Error("comparable is not a comparable type")
+ }
+
+ if Implements(anyType, comparableType.Underlying().(*Interface)) {
+ t.Error("any implements comparable")
+ }
+ if !Implements(comparableType, anyType.(*Interface)) {
+ t.Error("comparable does not implement any")
+ }
+
+ if AssignableTo(anyType, comparableType) {
+ t.Error("any assignable to comparable")
+ }
+ if !AssignableTo(comparableType, anyType) {
+ t.Error("comparable not assignable to any")
+ }
+}
+
+func TestIssue55030(t *testing.T) {
+ // makeSig makes the signature func(typ...)
+ makeSig := func(typ Type) {
+ par := NewVar(nopos, nil, "", typ)
+ params := NewTuple(par)
+ NewSignatureType(nil, nil, nil, params, nil, true)
+ }
+
+ // makeSig must not panic for the following (example) types:
+ // []int
+ makeSig(NewSlice(Typ[Int]))
+
+ // string
+ makeSig(Typ[String])
+
+ // P where P's core type is string
+ {
+ P := NewTypeName(nopos, nil, "P", nil) // [P string]
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{Typ[String]})))
+ }
+
+ // P where P's core type is an (unnamed) slice
+ {
+ P := NewTypeName(nopos, nil, "P", nil) // [P []int]
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{NewSlice(Typ[Int])})))
+ }
+
+ // P where P's core type is bytestring (i.e., string or []byte)
+ {
+ t1 := NewTerm(true, Typ[String]) // ~string
+ t2 := NewTerm(false, NewSlice(Typ[Byte])) // []byte
+ u := NewUnion([]*Term{t1, t2}) // ~string | []byte
+ P := NewTypeName(nopos, nil, "P", nil) // [P ~string | []byte]
+ makeSig(NewTypeParam(P, NewInterfaceType(nil, []Type{u})))
+ }
+}
+
+func TestIssue51093(t *testing.T) {
+ // Each test stands for a conversion of the form P(val)
+ // where P is a type parameter with typ as constraint.
+ // The test ensures that P(val) has the correct type P
+ // and is not a constant.
+ var tests = []struct {
+ typ string
+ val string
+ }{
+ {"bool", "false"},
+ {"int", "-1"},
+ {"uint", "1.0"},
+ {"rune", "'a'"},
+ {"float64", "3.5"},
+ {"complex64", "1.25"},
+ {"string", "\"foo\""},
+
+ // some more complex constraints
+ {"~byte", "1"},
+ {"~int | ~float64 | complex128", "1"},
+ {"~uint64 | ~rune", "'X'"},
+ }
+
+ for _, test := range tests {
+ src := fmt.Sprintf("package p; func _[P %s]() { _ = P(%s) }", test.typ, test.val)
+ types := make(map[ast.Expr]TypeAndValue)
+ mustTypecheck(src, nil, &Info{Types: types})
+
+ var n int
+ for x, tv := range types {
+ if x, _ := x.(*ast.CallExpr); x != nil {
+ // there must be exactly one CallExpr which is the P(val) conversion
+ n++
+ tpar, _ := tv.Type.(*TypeParam)
+ if tpar == nil {
+ t.Fatalf("%s: got type %s, want type parameter", ExprString(x), tv.Type)
+ }
+ if name := tpar.Obj().Name(); name != "P" {
+ t.Fatalf("%s: got type parameter name %s, want P", ExprString(x), name)
+ }
+ // P(val) must not be constant
+ if tv.Value != nil {
+ t.Errorf("%s: got constant value %s (%s), want no constant", ExprString(x), tv.Value, tv.Value.String())
+ }
+ }
+ }
+
+ if n != 1 {
+ t.Fatalf("%s: got %d CallExpr nodes; want 1", src, 1)
+ }
+ }
+}
+
+func TestIssue54258(t *testing.T) {
+
+ tests := []struct{ main, b, want string }{
+ { //---------------------------------------------------------------
+ `package main
+import "b"
+type I0 interface {
+ M0(w struct{ f string })
+}
+var _ I0 = b.S{}
+`,
+ `package b
+type S struct{}
+func (S) M0(struct{ f string }) {}
+`,
+ `6:12: cannot use b[.]S{} [(]value of type b[.]S[)] as I0 value in variable declaration: b[.]S does not implement I0 [(]wrong type for method M0[)]
+.*have M0[(]struct{f string /[*] package b [*]/ }[)]
+.*want M0[(]struct{f string /[*] package main [*]/ }[)]`},
+
+ { //---------------------------------------------------------------
+ `package main
+import "b"
+type I1 interface {
+ M1(struct{ string })
+}
+var _ I1 = b.S{}
+`,
+ `package b
+type S struct{}
+func (S) M1(struct{ string }) {}
+`,
+ `6:12: cannot use b[.]S{} [(]value of type b[.]S[)] as I1 value in variable declaration: b[.]S does not implement I1 [(]wrong type for method M1[)]
+.*have M1[(]struct{string /[*] package b [*]/ }[)]
+.*want M1[(]struct{string /[*] package main [*]/ }[)]`},
+
+ { //---------------------------------------------------------------
+ `package main
+import "b"
+type I2 interface {
+ M2(y struct{ f struct{ f string } })
+}
+var _ I2 = b.S{}
+`,
+ `package b
+type S struct{}
+func (S) M2(struct{ f struct{ f string } }) {}
+`,
+ `6:12: cannot use b[.]S{} [(]value of type b[.]S[)] as I2 value in variable declaration: b[.]S does not implement I2 [(]wrong type for method M2[)]
+.*have M2[(]struct{f struct{f string} /[*] package b [*]/ }[)]
+.*want M2[(]struct{f struct{f string} /[*] package main [*]/ }[)]`},
+
+ { //---------------------------------------------------------------
+ `package main
+import "b"
+type I3 interface {
+ M3(z struct{ F struct{ f string } })
+}
+var _ I3 = b.S{}
+`,
+ `package b
+type S struct{}
+func (S) M3(struct{ F struct{ f string } }) {}
+`,
+ `6:12: cannot use b[.]S{} [(]value of type b[.]S[)] as I3 value in variable declaration: b[.]S does not implement I3 [(]wrong type for method M3[)]
+.*have M3[(]struct{F struct{f string /[*] package b [*]/ }}[)]
+.*want M3[(]struct{F struct{f string /[*] package main [*]/ }}[)]`},
+
+ { //---------------------------------------------------------------
+ `package main
+import "b"
+type I4 interface {
+ M4(_ struct { *string })
+}
+var _ I4 = b.S{}
+`,
+ `package b
+type S struct{}
+func (S) M4(struct { *string }) {}
+`,
+ `6:12: cannot use b[.]S{} [(]value of type b[.]S[)] as I4 value in variable declaration: b[.]S does not implement I4 [(]wrong type for method M4[)]
+.*have M4[(]struct{[*]string /[*] package b [*]/ }[)]
+.*want M4[(]struct{[*]string /[*] package main [*]/ }[)]`},
+
+ { //---------------------------------------------------------------
+ `package main
+import "b"
+type t struct{ A int }
+type I5 interface {
+ M5(_ struct {b.S;t})
+}
+var _ I5 = b.S{}
+`,
+ `package b
+type S struct{}
+type t struct{ A int }
+func (S) M5(struct {S;t}) {}
+`,
+ `7:12: cannot use b[.]S{} [(]value of type b[.]S[)] as I5 value in variable declaration: b[.]S does not implement I5 [(]wrong type for method M5[)]
+.*have M5[(]struct{b[.]S; b[.]t}[)]
+.*want M5[(]struct{b[.]S; t}[)]`},
+ }
+
+ fset := token.NewFileSet()
+ test := func(main, b, want string) {
+ re := regexp.MustCompile(want)
+ bpkg := mustTypecheck(b, nil, nil)
+ mast := mustParse(fset, main)
+ conf := Config{Importer: importHelper{pkg: bpkg}}
+ _, err := conf.Check(mast.Name.Name, fset, []*ast.File{mast}, nil)
+ if err == nil {
+ t.Error("Expected failure, but it did not")
+ } else if got := err.Error(); !re.MatchString(got) {
+ t.Errorf("Wanted match for\n\t%s\n but got\n\t%s", want, got)
+ } else if testing.Verbose() {
+ t.Logf("Saw expected\n\t%s", err.Error())
+ }
+ }
+ for _, t := range tests {
+ test(t.main, t.b, t.want)
+ }
+}
+
+func TestIssue59944(t *testing.T) {
+ testenv.MustHaveCGO(t)
+
+ // The typechecker should resolve methods declared on aliases of cgo types.
+ const src = `
+package p
+
+/*
+struct layout {
+ int field;
+};
+*/
+import "C"
+
+type Layout = C.struct_layout
+
+func (l *Layout) Binding() {}
+
+func _() {
+ _ = (*Layout).Binding
+}
+`
+
+ // code generated by cmd/cgo for the above source.
+ const cgoTypes = `
+// Code generated by cmd/cgo; DO NOT EDIT.
+
+package p
+
+import "unsafe"
+
+import "syscall"
+
+import _cgopackage "runtime/cgo"
+
+type _ _cgopackage.Incomplete
+var _ syscall.Errno
+func _Cgo_ptr(ptr unsafe.Pointer) unsafe.Pointer { return ptr }
+
+//go:linkname _Cgo_always_false runtime.cgoAlwaysFalse
+var _Cgo_always_false bool
+//go:linkname _Cgo_use runtime.cgoUse
+func _Cgo_use(interface{})
+type _Ctype_int int32
+
+type _Ctype_struct_layout struct {
+ field _Ctype_int
+}
+
+type _Ctype_void [0]byte
+
+//go:linkname _cgo_runtime_cgocall runtime.cgocall
+func _cgo_runtime_cgocall(unsafe.Pointer, uintptr) int32
+
+//go:linkname _cgoCheckPointer runtime.cgoCheckPointer
+func _cgoCheckPointer(interface{}, interface{})
+
+//go:linkname _cgoCheckResult runtime.cgoCheckResult
+func _cgoCheckResult(interface{})
+`
+ testFiles(t, []string{"p.go", "_cgo_gotypes.go"}, [][]byte{[]byte(src), []byte(cgoTypes)}, false, func(cfg *Config) {
+ *boolFieldAddr(cfg, "go115UsesCgo") = true
+ })
+}
+
+func TestIssue61931(t *testing.T) {
+ const src = `
+package p
+
+func A(func(any), ...any) {}
+func B[T any](T) {}
+
+func _() {
+ A(B, nil // syntax error: missing ',' before newline in argument list
+}
+`
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, pkgName(src), src, 0)
+ if err == nil {
+ t.Fatal("expected syntax error")
+ }
+
+ var conf Config
+ conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // must not panic
+}
+
+func TestIssue61938(t *testing.T) {
+ const src = `
+package p
+
+func f[T any]() {}
+func _() { f() }
+`
+ // no error handler provided (this issue)
+ var conf Config
+ typecheck(src, &conf, nil) // must not panic
+
+ // with error handler (sanity check)
+ conf.Error = func(error) {}
+ typecheck(src, &conf, nil) // must not panic
+}
+
+func TestIssue63260(t *testing.T) {
+ const src = `
+package p
+
+func _() {
+ use(f[*string])
+}
+
+func use(func()) {}
+
+func f[I *T, T any]() {
+ var v T
+ _ = v
+}`
+
+ info := Info{
+ Defs: make(map[*ast.Ident]Object),
+ }
+ pkg := mustTypecheck(src, nil, &info)
+
+ // get type parameter T in signature of f
+ T := pkg.Scope().Lookup("f").Type().(*Signature).TypeParams().At(1)
+ if T.Obj().Name() != "T" {
+ t.Fatalf("got type parameter %s, want T", T)
+ }
+
+ // get type of variable v in body of f
+ var v Object
+ for name, obj := range info.Defs {
+ if name.Name == "v" {
+ v = obj
+ break
+ }
+ }
+ if v == nil {
+ t.Fatal("variable v not found")
+ }
+
+ // type of v and T must be pointer-identical
+ if v.Type() != T {
+ t.Fatalf("types of v and T are not pointer-identical: %p != %p", v.Type().(*TypeParam), T)
+ }
+}
+
+func TestIssue44410(t *testing.T) {
+ const src = `
+package p
+
+type A = []int
+type S struct{ A }
+`
+
+ var conf Config
+ *boolFieldAddr(&conf, "_EnableAlias") = true
+ pkg := mustTypecheck(src, &conf, nil)
+
+ S := pkg.Scope().Lookup("S")
+ if S == nil {
+ t.Fatal("object S not found")
+ }
+
+ got := S.String()
+ const want = "type p.S struct{p.A /* = []int */}"
+ if got != want {
+ t.Fatalf("got %q; want %q", got, want)
+ }
+}