Black Lives Matter. Support the Equal Justice Initiative.

Source file src/go/types/issues_test.go

Documentation: go/types

     1  // Copyright 2013 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  // This file implements tests for various issues.
     6  
     7  package types_test
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"go/ast"
    13  	"go/importer"
    14  	"go/parser"
    15  	"go/token"
    16  	"internal/testenv"
    17  	"sort"
    18  	"strings"
    19  	"testing"
    20  
    21  	. "go/types"
    22  )
    23  
    24  func mustParse(t *testing.T, src string) *ast.File {
    25  	f, err := parser.ParseFile(fset, "", src, 0)
    26  	if err != nil {
    27  		t.Fatal(err)
    28  	}
    29  	return f
    30  }
    31  func TestIssue5770(t *testing.T) {
    32  	f := mustParse(t, `package p; type S struct{T}`)
    33  	conf := Config{Importer: importer.Default()}
    34  	_, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash
    35  	want := "undeclared name: T"
    36  	if err == nil || !strings.Contains(err.Error(), want) {
    37  		t.Errorf("got: %v; want: %s", err, want)
    38  	}
    39  }
    40  
    41  func TestIssue5849(t *testing.T) {
    42  	src := `
    43  package p
    44  var (
    45  	s uint
    46  	_ = uint8(8)
    47  	_ = uint16(16) << s
    48  	_ = uint32(32 << s)
    49  	_ = uint64(64 << s + s)
    50  	_ = (interface{})("foo")
    51  	_ = (interface{})(nil)
    52  )`
    53  	f := mustParse(t, src)
    54  
    55  	var conf Config
    56  	types := make(map[ast.Expr]TypeAndValue)
    57  	_, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  
    62  	for x, tv := range types {
    63  		var want Type
    64  		switch x := x.(type) {
    65  		case *ast.BasicLit:
    66  			switch x.Value {
    67  			case `8`:
    68  				want = Typ[Uint8]
    69  			case `16`:
    70  				want = Typ[Uint16]
    71  			case `32`:
    72  				want = Typ[Uint32]
    73  			case `64`:
    74  				want = Typ[Uint] // because of "+ s", s is of type uint
    75  			case `"foo"`:
    76  				want = Typ[String]
    77  			}
    78  		case *ast.Ident:
    79  			if x.Name == "nil" {
    80  				want = Typ[UntypedNil]
    81  			}
    82  		}
    83  		if want != nil && !Identical(tv.Type, want) {
    84  			t.Errorf("got %s; want %s", tv.Type, want)
    85  		}
    86  	}
    87  }
    88  
    89  func TestIssue6413(t *testing.T) {
    90  	src := `
    91  package p
    92  func f() int {
    93  	defer f()
    94  	go f()
    95  	return 0
    96  }
    97  `
    98  	f := mustParse(t, src)
    99  
   100  	var conf Config
   101  	types := make(map[ast.Expr]TypeAndValue)
   102  	_, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  
   107  	want := Typ[Int]
   108  	n := 0
   109  	for x, tv := range types {
   110  		if _, ok := x.(*ast.CallExpr); ok {
   111  			if tv.Type != want {
   112  				t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), tv.Type, want)
   113  			}
   114  			n++
   115  		}
   116  	}
   117  
   118  	if n != 2 {
   119  		t.Errorf("got %d CallExprs; want 2", n)
   120  	}
   121  }
   122  
   123  func TestIssue7245(t *testing.T) {
   124  	src := `
   125  package p
   126  func (T) m() (res bool) { return }
   127  type T struct{} // receiver type after method declaration
   128  `
   129  	f := mustParse(t, src)
   130  
   131  	var conf Config
   132  	defs := make(map[*ast.Ident]Object)
   133  	_, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs})
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	m := f.Decls[0].(*ast.FuncDecl)
   139  	res1 := defs[m.Name].(*Func).Type().(*Signature).Results().At(0)
   140  	res2 := defs[m.Type.Results.List[0].Names[0]].(*Var)
   141  
   142  	if res1 != res2 {
   143  		t.Errorf("got %s (%p) != %s (%p)", res1, res2, res1, res2)
   144  	}
   145  }
   146  
   147  // This tests that uses of existing vars on the LHS of an assignment
   148  // are Uses, not Defs; and also that the (illegal) use of a non-var on
   149  // the LHS of an assignment is a Use nonetheless.
   150  func TestIssue7827(t *testing.T) {
   151  	const src = `
   152  package p
   153  func _() {
   154  	const w = 1        // defs w
   155          x, y := 2, 3       // defs x, y
   156          w, x, z := 4, 5, 6 // uses w, x, defs z; error: cannot assign to w
   157          _, _, _ = x, y, z  // uses x, y, z
   158  }
   159  `
   160  	f := mustParse(t, src)
   161  
   162  	const want = `L3 defs func p._()
   163  L4 defs const w untyped int
   164  L5 defs var x int
   165  L5 defs var y int
   166  L6 defs var z int
   167  L6 uses const w untyped int
   168  L6 uses var x int
   169  L7 uses var x int
   170  L7 uses var y int
   171  L7 uses var z int`
   172  
   173  	// don't abort at the first error
   174  	conf := Config{Error: func(err error) { t.Log(err) }}
   175  	defs := make(map[*ast.Ident]Object)
   176  	uses := make(map[*ast.Ident]Object)
   177  	_, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs, Uses: uses})
   178  	if s := fmt.Sprint(err); !strings.HasSuffix(s, "cannot assign to w") {
   179  		t.Errorf("Check: unexpected error: %s", s)
   180  	}
   181  
   182  	var facts []string
   183  	for id, obj := range defs {
   184  		if obj != nil {
   185  			fact := fmt.Sprintf("L%d defs %s", fset.Position(id.Pos()).Line, obj)
   186  			facts = append(facts, fact)
   187  		}
   188  	}
   189  	for id, obj := range uses {
   190  		fact := fmt.Sprintf("L%d uses %s", fset.Position(id.Pos()).Line, obj)
   191  		facts = append(facts, fact)
   192  	}
   193  	sort.Strings(facts)
   194  
   195  	got := strings.Join(facts, "\n")
   196  	if got != want {
   197  		t.Errorf("Unexpected defs/uses\ngot:\n%s\nwant:\n%s", got, want)
   198  	}
   199  }
   200  
   201  // This tests that the package associated with the types.Object.Pkg method
   202  // is the type's package independent of the order in which the imports are
   203  // listed in the sources src1, src2 below.
   204  // The actual issue is in go/internal/gcimporter which has a corresponding
   205  // test; we leave this test here to verify correct behavior at the go/types
   206  // level.
   207  func TestIssue13898(t *testing.T) {
   208  	testenv.MustHaveGoBuild(t)
   209  
   210  	const src0 = `
   211  package main
   212  
   213  import "go/types"
   214  
   215  func main() {
   216  	var info types.Info
   217  	for _, obj := range info.Uses {
   218  		_ = obj.Pkg()
   219  	}
   220  }
   221  `
   222  	// like src0, but also imports go/importer
   223  	const src1 = `
   224  package main
   225  
   226  import (
   227  	"go/types"
   228  	_ "go/importer"
   229  )
   230  
   231  func main() {
   232  	var info types.Info
   233  	for _, obj := range info.Uses {
   234  		_ = obj.Pkg()
   235  	}
   236  }
   237  `
   238  	// like src1 but with different import order
   239  	// (used to fail with this issue)
   240  	const src2 = `
   241  package main
   242  
   243  import (
   244  	_ "go/importer"
   245  	"go/types"
   246  )
   247  
   248  func main() {
   249  	var info types.Info
   250  	for _, obj := range info.Uses {
   251  		_ = obj.Pkg()
   252  	}
   253  }
   254  `
   255  	f := func(test, src string) {
   256  		f := mustParse(t, src)
   257  		cfg := Config{Importer: importer.Default()}
   258  		info := Info{Uses: make(map[*ast.Ident]Object)}
   259  		_, err := cfg.Check("main", fset, []*ast.File{f}, &info)
   260  		if err != nil {
   261  			t.Fatal(err)
   262  		}
   263  
   264  		var pkg *Package
   265  		count := 0
   266  		for id, obj := range info.Uses {
   267  			if id.Name == "Pkg" {
   268  				pkg = obj.Pkg()
   269  				count++
   270  			}
   271  		}
   272  		if count != 1 {
   273  			t.Fatalf("%s: got %d entries named Pkg; want 1", test, count)
   274  		}
   275  		if pkg.Name() != "types" {
   276  			t.Fatalf("%s: got %v; want package types", test, pkg)
   277  		}
   278  	}
   279  
   280  	f("src0", src0)
   281  	f("src1", src1)
   282  	f("src2", src2)
   283  }
   284  
   285  func TestIssue22525(t *testing.T) {
   286  	f := mustParse(t, `package p; func f() { var a, b, c, d, e int }`)
   287  
   288  	got := "\n"
   289  	conf := Config{Error: func(err error) { got += err.Error() + "\n" }}
   290  	conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash
   291  	want := `
   292  1:27: a declared but not used
   293  1:30: b declared but not used
   294  1:33: c declared but not used
   295  1:36: d declared but not used
   296  1:39: e declared but not used
   297  `
   298  	if got != want {
   299  		t.Errorf("got: %swant: %s", got, want)
   300  	}
   301  }
   302  
   303  func TestIssue25627(t *testing.T) {
   304  	const prefix = `package p; import "unsafe"; type P *struct{}; type I interface{}; type T `
   305  	// The src strings (without prefix) are constructed such that the number of semicolons
   306  	// plus one corresponds to the number of fields expected in the respective struct.
   307  	for _, src := range []string{
   308  		`struct { x Missing }`,
   309  		`struct { Missing }`,
   310  		`struct { *Missing }`,
   311  		`struct { unsafe.Pointer }`,
   312  		`struct { P }`,
   313  		`struct { *I }`,
   314  		`struct { a int; b Missing; *Missing }`,
   315  	} {
   316  		f := mustParse(t, prefix+src)
   317  
   318  		cfg := Config{Importer: importer.Default(), Error: func(err error) {}}
   319  		info := &Info{Types: make(map[ast.Expr]TypeAndValue)}
   320  		_, err := cfg.Check(f.Name.Name, fset, []*ast.File{f}, info)
   321  		if err != nil {
   322  			if _, ok := err.(Error); !ok {
   323  				t.Fatal(err)
   324  			}
   325  		}
   326  
   327  		ast.Inspect(f, func(n ast.Node) bool {
   328  			if spec, _ := n.(*ast.TypeSpec); spec != nil {
   329  				if tv, ok := info.Types[spec.Type]; ok && spec.Name.Name == "T" {
   330  					want := strings.Count(src, ";") + 1
   331  					if got := tv.Type.(*Struct).NumFields(); got != want {
   332  						t.Errorf("%s: got %d fields; want %d", src, got, want)
   333  					}
   334  				}
   335  			}
   336  			return true
   337  		})
   338  	}
   339  }
   340  
   341  func TestIssue28005(t *testing.T) {
   342  	// method names must match defining interface name for this test
   343  	// (see last comment in this function)
   344  	sources := [...]string{
   345  		"package p; type A interface{ A() }",
   346  		"package p; type B interface{ B() }",
   347  		"package p; type X interface{ A; B }",
   348  	}
   349  
   350  	// compute original file ASTs
   351  	var orig [len(sources)]*ast.File
   352  	for i, src := range sources {
   353  		orig[i] = mustParse(t, src)
   354  	}
   355  
   356  	// run the test for all order permutations of the incoming files
   357  	for _, perm := range [][len(sources)]int{
   358  		{0, 1, 2},
   359  		{0, 2, 1},
   360  		{1, 0, 2},
   361  		{1, 2, 0},
   362  		{2, 0, 1},
   363  		{2, 1, 0},
   364  	} {
   365  		// create file order permutation
   366  		files := make([]*ast.File, len(sources))
   367  		for i := range perm {
   368  			files[i] = orig[perm[i]]
   369  		}
   370  
   371  		// type-check package with given file order permutation
   372  		var conf Config
   373  		info := &Info{Defs: make(map[*ast.Ident]Object)}
   374  		_, err := conf.Check("", fset, files, info)
   375  		if err != nil {
   376  			t.Fatal(err)
   377  		}
   378  
   379  		// look for interface object X
   380  		var obj Object
   381  		for name, def := range info.Defs {
   382  			if name.Name == "X" {
   383  				obj = def
   384  				break
   385  			}
   386  		}
   387  		if obj == nil {
   388  			t.Fatal("object X not found")
   389  		}
   390  		iface := obj.Type().Underlying().(*Interface) // object X must be an interface
   391  
   392  		// Each iface method m is embedded; and m's receiver base type name
   393  		// must match the method's name per the choice in the source file.
   394  		for i := 0; i < iface.NumMethods(); i++ {
   395  			m := iface.Method(i)
   396  			recvName := m.Type().(*Signature).Recv().Type().(*Named).Obj().Name()
   397  			if recvName != m.Name() {
   398  				t.Errorf("perm %v: got recv %s; want %s", perm, recvName, m.Name())
   399  			}
   400  		}
   401  	}
   402  }
   403  
   404  func TestIssue28282(t *testing.T) {
   405  	// create type interface { error }
   406  	et := Universe.Lookup("error").Type()
   407  	it := NewInterfaceType(nil, []Type{et})
   408  	it.Complete()
   409  	// verify that after completing the interface, the embedded method remains unchanged
   410  	want := et.Underlying().(*Interface).Method(0)
   411  	got := it.Method(0)
   412  	if got != want {
   413  		t.Fatalf("%s.Method(0): got %q (%p); want %q (%p)", it, got, got, want, want)
   414  	}
   415  	// verify that lookup finds the same method in both interfaces (redundant check)
   416  	obj, _, _ := LookupFieldOrMethod(et, false, nil, "Error")
   417  	if obj != want {
   418  		t.Fatalf("%s.Lookup: got %q (%p); want %q (%p)", et, obj, obj, want, want)
   419  	}
   420  	obj, _, _ = LookupFieldOrMethod(it, false, nil, "Error")
   421  	if obj != want {
   422  		t.Fatalf("%s.Lookup: got %q (%p); want %q (%p)", it, obj, obj, want, want)
   423  	}
   424  }
   425  
   426  func TestIssue29029(t *testing.T) {
   427  	f1 := mustParse(t, `package p; type A interface { M() }`)
   428  	f2 := mustParse(t, `package p; var B interface { A }`)
   429  
   430  	// printInfo prints the *Func definitions recorded in info, one *Func per line.
   431  	printInfo := func(info *Info) string {
   432  		var buf bytes.Buffer
   433  		for _, obj := range info.Defs {
   434  			if fn, ok := obj.(*Func); ok {
   435  				fmt.Fprintln(&buf, fn)
   436  			}
   437  		}
   438  		return buf.String()
   439  	}
   440  
   441  	// The *Func (method) definitions for package p must be the same
   442  	// independent on whether f1 and f2 are type-checked together, or
   443  	// incrementally.
   444  
   445  	// type-check together
   446  	var conf Config
   447  	info := &Info{Defs: make(map[*ast.Ident]Object)}
   448  	check := NewChecker(&conf, fset, NewPackage("", "p"), info)
   449  	if err := check.Files([]*ast.File{f1, f2}); err != nil {
   450  		t.Fatal(err)
   451  	}
   452  	want := printInfo(info)
   453  
   454  	// type-check incrementally
   455  	info = &Info{Defs: make(map[*ast.Ident]Object)}
   456  	check = NewChecker(&conf, fset, NewPackage("", "p"), info)
   457  	if err := check.Files([]*ast.File{f1}); err != nil {
   458  		t.Fatal(err)
   459  	}
   460  	if err := check.Files([]*ast.File{f2}); err != nil {
   461  		t.Fatal(err)
   462  	}
   463  	got := printInfo(info)
   464  
   465  	if got != want {
   466  		t.Errorf("\ngot : %swant: %s", got, want)
   467  	}
   468  }
   469  
   470  func TestIssue34151(t *testing.T) {
   471  	const asrc = `package a; type I interface{ M() }; type T struct { F interface { I } }`
   472  	const bsrc = `package b; import "a"; type T struct { F interface { a.I } }; var _ = a.T(T{})`
   473  
   474  	a, err := pkgFor("a", asrc, nil)
   475  	if err != nil {
   476  		t.Fatalf("package %s failed to typecheck: %v", a.Name(), err)
   477  	}
   478  
   479  	bast := mustParse(t, bsrc)
   480  	conf := Config{Importer: importHelper{pkg: a}}
   481  	b, err := conf.Check(bast.Name.Name, fset, []*ast.File{bast}, nil)
   482  	if err != nil {
   483  		t.Errorf("package %s failed to typecheck: %v", b.Name(), err)
   484  	}
   485  }
   486  
   487  type importHelper struct {
   488  	pkg      *Package
   489  	fallback Importer
   490  }
   491  
   492  func (h importHelper) Import(path string) (*Package, error) {
   493  	if path == h.pkg.Path() {
   494  		return h.pkg, nil
   495  	}
   496  	if h.fallback == nil {
   497  		return nil, fmt.Errorf("got package path %q; want %q", path, h.pkg.Path())
   498  	}
   499  	return h.fallback.Import(path)
   500  }
   501  
   502  // TestIssue34921 verifies that we don't update an imported type's underlying
   503  // type when resolving an underlying type. Specifically, when determining the
   504  // underlying type of b.T (which is the underlying type of a.T, which is int)
   505  // we must not set the underlying type of a.T again since that would lead to
   506  // a race condition if package b is imported elsewhere, in a package that is
   507  // concurrently type-checked.
   508  func TestIssue34921(t *testing.T) {
   509  	defer func() {
   510  		if r := recover(); r != nil {
   511  			t.Error(r)
   512  		}
   513  	}()
   514  
   515  	var sources = []string{
   516  		`package a; type T int`,
   517  		`package b; import "a"; type T a.T`,
   518  	}
   519  
   520  	var pkg *Package
   521  	for _, src := range sources {
   522  		f := mustParse(t, src)
   523  		conf := Config{Importer: importHelper{pkg: pkg}}
   524  		res, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
   525  		if err != nil {
   526  			t.Errorf("%q failed to typecheck: %v", src, err)
   527  		}
   528  		pkg = res // res is imported by the next package in this test
   529  	}
   530  }
   531  
   532  func TestIssue43088(t *testing.T) {
   533  	// type T1 struct {
   534  	//         _ T2
   535  	// }
   536  	//
   537  	// type T2 struct {
   538  	//         _ struct {
   539  	//                 _ T2
   540  	//         }
   541  	// }
   542  	n1 := NewTypeName(token.NoPos, nil, "T1", nil)
   543  	T1 := NewNamed(n1, nil, nil)
   544  	n2 := NewTypeName(token.NoPos, nil, "T2", nil)
   545  	T2 := NewNamed(n2, nil, nil)
   546  	s1 := NewStruct([]*Var{NewField(token.NoPos, nil, "_", T2, false)}, nil)
   547  	T1.SetUnderlying(s1)
   548  	s2 := NewStruct([]*Var{NewField(token.NoPos, nil, "_", T2, false)}, nil)
   549  	s3 := NewStruct([]*Var{NewField(token.NoPos, nil, "_", s2, false)}, nil)
   550  	T2.SetUnderlying(s3)
   551  
   552  	// These calls must terminate (no endless recursion).
   553  	Comparable(T1)
   554  	Comparable(T2)
   555  }
   556  
   557  func TestIssue44515(t *testing.T) {
   558  	typ := Unsafe.Scope().Lookup("Pointer").Type()
   559  
   560  	got := TypeString(typ, nil)
   561  	want := "unsafe.Pointer"
   562  	if got != want {
   563  		t.Errorf("got %q; want %q", got, want)
   564  	}
   565  
   566  	qf := func(pkg *Package) string {
   567  		if pkg == Unsafe {
   568  			return "foo"
   569  		}
   570  		return ""
   571  	}
   572  	got = TypeString(typ, qf)
   573  	want = "foo.Pointer"
   574  	if got != want {
   575  		t.Errorf("got %q; want %q", got, want)
   576  	}
   577  }
   578  
   579  func TestIssue43124(t *testing.T) {
   580  	// TODO(rFindley) move this to testdata by enhancing support for importing.
   581  
   582  	// All involved packages have the same name (template). Error messages should
   583  	// disambiguate between text/template and html/template by printing the full
   584  	// path.
   585  	const (
   586  		asrc = `package a; import "text/template"; func F(template.Template) {}; func G(int) {}`
   587  		bsrc = `
   588  package b
   589  
   590  import (
   591  	"a"
   592  	"html/template"
   593  )
   594  
   595  func _() {
   596  	// Packages should be fully qualified when there is ambiguity within the
   597  	// error string itself.
   598  	a.F(template /* ERROR cannot use.*html/template.* as .*text/template */ .Template{})
   599  }
   600  `
   601  		csrc = `
   602  package c
   603  
   604  import (
   605  	"a"
   606  	"fmt"
   607  	"html/template"
   608  )
   609  
   610  // Issue #46905: make sure template is not the first package qualified.
   611  var _ fmt.Stringer = 1 // ERROR cannot use 1.*as fmt\.Stringer
   612  
   613  // Packages should be fully qualified when there is ambiguity in reachable
   614  // packages. In this case both a (and for that matter html/template) import
   615  // text/template.
   616  func _() { a.G(template /* ERROR cannot use .*html/template.*Template */ .Template{}) }
   617  `
   618  
   619  		tsrc = `
   620  package template
   621  
   622  import "text/template"
   623  
   624  type T int
   625  
   626  // Verify that the current package name also causes disambiguation.
   627  var _ T = template /* ERROR cannot use.*text/template.* as T value */.Template{}
   628  `
   629  	)
   630  
   631  	a, err := pkgFor("a", asrc, nil)
   632  	if err != nil {
   633  		t.Fatalf("package a failed to typecheck: %v", err)
   634  	}
   635  	imp := importHelper{pkg: a, fallback: importer.Default()}
   636  
   637  	checkFiles(t, nil, "", []string{"b.go"}, [][]byte{[]byte(bsrc)}, false, imp)
   638  	checkFiles(t, nil, "", []string{"c.go"}, [][]byte{[]byte(csrc)}, false, imp)
   639  	checkFiles(t, nil, "", []string{"t.go"}, [][]byte{[]byte(tsrc)}, false, imp)
   640  }
   641  

View as plain text