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  	"internal/testenv"
    16  	"sort"
    17  	"strings"
    18  	"testing"
    19  
    20  	. "go/types"
    21  )
    22  
    23  func mustParse(t *testing.T, src string) *ast.File {
    24  	f, err := parser.ParseFile(fset, "", src, 0)
    25  	if err != nil {
    26  		t.Fatal(err)
    27  	}
    28  	return f
    29  }
    30  func TestIssue5770(t *testing.T) {
    31  	f := mustParse(t, `package p; type S struct{T}`)
    32  	conf := Config{Importer: importer.Default()}
    33  	_, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash
    34  	want := "undeclared name: T"
    35  	if err == nil || !strings.Contains(err.Error(), want) {
    36  		t.Errorf("got: %v; want: %s", err, want)
    37  	}
    38  }
    39  
    40  func TestIssue5849(t *testing.T) {
    41  	src := `
    42  package p
    43  var (
    44  	s uint
    45  	_ = uint8(8)
    46  	_ = uint16(16) << s
    47  	_ = uint32(32 << s)
    48  	_ = uint64(64 << s + s)
    49  	_ = (interface{})("foo")
    50  	_ = (interface{})(nil)
    51  )`
    52  	f := mustParse(t, src)
    53  
    54  	var conf Config
    55  	types := make(map[ast.Expr]TypeAndValue)
    56  	_, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
    57  	if err != nil {
    58  		t.Fatal(err)
    59  	}
    60  
    61  	for x, tv := range types {
    62  		var want Type
    63  		switch x := x.(type) {
    64  		case *ast.BasicLit:
    65  			switch x.Value {
    66  			case `8`:
    67  				want = Typ[Uint8]
    68  			case `16`:
    69  				want = Typ[Uint16]
    70  			case `32`:
    71  				want = Typ[Uint32]
    72  			case `64`:
    73  				want = Typ[Uint] // because of "+ s", s is of type uint
    74  			case `"foo"`:
    75  				want = Typ[String]
    76  			}
    77  		case *ast.Ident:
    78  			if x.Name == "nil" {
    79  				want = Typ[UntypedNil]
    80  			}
    81  		}
    82  		if want != nil && !Identical(tv.Type, want) {
    83  			t.Errorf("got %s; want %s", tv.Type, want)
    84  		}
    85  	}
    86  }
    87  
    88  func TestIssue6413(t *testing.T) {
    89  	src := `
    90  package p
    91  func f() int {
    92  	defer f()
    93  	go f()
    94  	return 0
    95  }
    96  `
    97  	f := mustParse(t, src)
    98  
    99  	var conf Config
   100  	types := make(map[ast.Expr]TypeAndValue)
   101  	_, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
   102  	if err != nil {
   103  		t.Fatal(err)
   104  	}
   105  
   106  	want := Typ[Int]
   107  	n := 0
   108  	for x, tv := range types {
   109  		if _, ok := x.(*ast.CallExpr); ok {
   110  			if tv.Type != want {
   111  				t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), tv.Type, want)
   112  			}
   113  			n++
   114  		}
   115  	}
   116  
   117  	if n != 2 {
   118  		t.Errorf("got %d CallExprs; want 2", n)
   119  	}
   120  }
   121  
   122  func TestIssue7245(t *testing.T) {
   123  	src := `
   124  package p
   125  func (T) m() (res bool) { return }
   126  type T struct{} // receiver type after method declaration
   127  `
   128  	f := mustParse(t, src)
   129  
   130  	var conf Config
   131  	defs := make(map[*ast.Ident]Object)
   132  	_, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs})
   133  	if err != nil {
   134  		t.Fatal(err)
   135  	}
   136  
   137  	m := f.Decls[0].(*ast.FuncDecl)
   138  	res1 := defs[m.Name].(*Func).Type().(*Signature).Results().At(0)
   139  	res2 := defs[m.Type.Results.List[0].Names[0]].(*Var)
   140  
   141  	if res1 != res2 {
   142  		t.Errorf("got %s (%p) != %s (%p)", res1, res2, res1, res2)
   143  	}
   144  }
   145  
   146  // This tests that uses of existing vars on the LHS of an assignment
   147  // are Uses, not Defs; and also that the (illegal) use of a non-var on
   148  // the LHS of an assignment is a Use nonetheless.
   149  func TestIssue7827(t *testing.T) {
   150  	const src = `
   151  package p
   152  func _() {
   153  	const w = 1        // defs w
   154          x, y := 2, 3       // defs x, y
   155          w, x, z := 4, 5, 6 // uses w, x, defs z; error: cannot assign to w
   156          _, _, _ = x, y, z  // uses x, y, z
   157  }
   158  `
   159  	f := mustParse(t, src)
   160  
   161  	const want = `L3 defs func p._()
   162  L4 defs const w untyped int
   163  L5 defs var x int
   164  L5 defs var y int
   165  L6 defs var z int
   166  L6 uses const w untyped int
   167  L6 uses var x int
   168  L7 uses var x int
   169  L7 uses var y int
   170  L7 uses var z int`
   171  
   172  	// don't abort at the first error
   173  	conf := Config{Error: func(err error) { t.Log(err) }}
   174  	defs := make(map[*ast.Ident]Object)
   175  	uses := make(map[*ast.Ident]Object)
   176  	_, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs, Uses: uses})
   177  	if s := fmt.Sprint(err); !strings.HasSuffix(s, "cannot assign to w") {
   178  		t.Errorf("Check: unexpected error: %s", s)
   179  	}
   180  
   181  	var facts []string
   182  	for id, obj := range defs {
   183  		if obj != nil {
   184  			fact := fmt.Sprintf("L%d defs %s", fset.Position(id.Pos()).Line, obj)
   185  			facts = append(facts, fact)
   186  		}
   187  	}
   188  	for id, obj := range uses {
   189  		fact := fmt.Sprintf("L%d uses %s", fset.Position(id.Pos()).Line, obj)
   190  		facts = append(facts, fact)
   191  	}
   192  	sort.Strings(facts)
   193  
   194  	got := strings.Join(facts, "\n")
   195  	if got != want {
   196  		t.Errorf("Unexpected defs/uses\ngot:\n%s\nwant:\n%s", got, want)
   197  	}
   198  }
   199  
   200  // This tests that the package associated with the types.Object.Pkg method
   201  // is the type's package independent of the order in which the imports are
   202  // listed in the sources src1, src2 below.
   203  // The actual issue is in go/internal/gcimporter which has a corresponding
   204  // test; we leave this test here to verify correct behavior at the go/types
   205  // level.
   206  func TestIssue13898(t *testing.T) {
   207  	testenv.MustHaveGoBuild(t)
   208  
   209  	const src0 = `
   210  package main
   211  
   212  import "go/types"
   213  
   214  func main() {
   215  	var info types.Info
   216  	for _, obj := range info.Uses {
   217  		_ = obj.Pkg()
   218  	}
   219  }
   220  `
   221  	// like src0, but also imports go/importer
   222  	const src1 = `
   223  package main
   224  
   225  import (
   226  	"go/types"
   227  	_ "go/importer"
   228  )
   229  
   230  func main() {
   231  	var info types.Info
   232  	for _, obj := range info.Uses {
   233  		_ = obj.Pkg()
   234  	}
   235  }
   236  `
   237  	// like src1 but with different import order
   238  	// (used to fail with this issue)
   239  	const src2 = `
   240  package main
   241  
   242  import (
   243  	_ "go/importer"
   244  	"go/types"
   245  )
   246  
   247  func main() {
   248  	var info types.Info
   249  	for _, obj := range info.Uses {
   250  		_ = obj.Pkg()
   251  	}
   252  }
   253  `
   254  	f := func(test, src string) {
   255  		f := mustParse(t, src)
   256  		cfg := Config{Importer: importer.Default()}
   257  		info := Info{Uses: make(map[*ast.Ident]Object)}
   258  		_, err := cfg.Check("main", fset, []*ast.File{f}, &info)
   259  		if err != nil {
   260  			t.Fatal(err)
   261  		}
   262  
   263  		var pkg *Package
   264  		count := 0
   265  		for id, obj := range info.Uses {
   266  			if id.Name == "Pkg" {
   267  				pkg = obj.Pkg()
   268  				count++
   269  			}
   270  		}
   271  		if count != 1 {
   272  			t.Fatalf("%s: got %d entries named Pkg; want 1", test, count)
   273  		}
   274  		if pkg.Name() != "types" {
   275  			t.Fatalf("%s: got %v; want package types", test, pkg)
   276  		}
   277  	}
   278  
   279  	f("src0", src0)
   280  	f("src1", src1)
   281  	f("src2", src2)
   282  }
   283  
   284  func TestIssue22525(t *testing.T) {
   285  	f := mustParse(t, `package p; func f() { var a, b, c, d, e int }`)
   286  
   287  	got := "\n"
   288  	conf := Config{Error: func(err error) { got += err.Error() + "\n" }}
   289  	conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash
   290  	want := `
   291  1:27: a declared but not used
   292  1:30: b declared but not used
   293  1:33: c declared but not used
   294  1:36: d declared but not used
   295  1:39: e declared but not used
   296  `
   297  	if got != want {
   298  		t.Errorf("got: %swant: %s", got, want)
   299  	}
   300  }
   301  
   302  func TestIssue25627(t *testing.T) {
   303  	const prefix = `package p; import "unsafe"; type P *struct{}; type I interface{}; type T `
   304  	// The src strings (without prefix) are constructed such that the number of semicolons
   305  	// plus one corresponds to the number of fields expected in the respective struct.
   306  	for _, src := range []string{
   307  		`struct { x Missing }`,
   308  		`struct { Missing }`,
   309  		`struct { *Missing }`,
   310  		`struct { unsafe.Pointer }`,
   311  		`struct { P }`,
   312  		`struct { *I }`,
   313  		`struct { a int; b Missing; *Missing }`,
   314  	} {
   315  		f := mustParse(t, prefix+src)
   316  
   317  		cfg := Config{Importer: importer.Default(), Error: func(err error) {}}
   318  		info := &Info{Types: make(map[ast.Expr]TypeAndValue)}
   319  		_, err := cfg.Check(f.Name.Name, fset, []*ast.File{f}, info)
   320  		if err != nil {
   321  			if _, ok := err.(Error); !ok {
   322  				t.Fatal(err)
   323  			}
   324  		}
   325  
   326  		ast.Inspect(f, func(n ast.Node) bool {
   327  			if spec, _ := n.(*ast.TypeSpec); spec != nil {
   328  				if tv, ok := info.Types[spec.Type]; ok && spec.Name.Name == "T" {
   329  					want := strings.Count(src, ";") + 1
   330  					if got := tv.Type.(*Struct).NumFields(); got != want {
   331  						t.Errorf("%s: got %d fields; want %d", src, got, want)
   332  					}
   333  				}
   334  			}
   335  			return true
   336  		})
   337  	}
   338  }
   339  
   340  func TestIssue28005(t *testing.T) {
   341  	// method names must match defining interface name for this test
   342  	// (see last comment in this function)
   343  	sources := [...]string{
   344  		"package p; type A interface{ A() }",
   345  		"package p; type B interface{ B() }",
   346  		"package p; type X interface{ A; B }",
   347  	}
   348  
   349  	// compute original file ASTs
   350  	var orig [len(sources)]*ast.File
   351  	for i, src := range sources {
   352  		orig[i] = mustParse(t, src)
   353  	}
   354  
   355  	// run the test for all order permutations of the incoming files
   356  	for _, perm := range [][len(sources)]int{
   357  		{0, 1, 2},
   358  		{0, 2, 1},
   359  		{1, 0, 2},
   360  		{1, 2, 0},
   361  		{2, 0, 1},
   362  		{2, 1, 0},
   363  	} {
   364  		// create file order permutation
   365  		files := make([]*ast.File, len(sources))
   366  		for i := range perm {
   367  			files[i] = orig[perm[i]]
   368  		}
   369  
   370  		// type-check package with given file order permutation
   371  		var conf Config
   372  		info := &Info{Defs: make(map[*ast.Ident]Object)}
   373  		_, err := conf.Check("", fset, files, info)
   374  		if err != nil {
   375  			t.Fatal(err)
   376  		}
   377  
   378  		// look for interface object X
   379  		var obj Object
   380  		for name, def := range info.Defs {
   381  			if name.Name == "X" {
   382  				obj = def
   383  				break
   384  			}
   385  		}
   386  		if obj == nil {
   387  			t.Fatal("interface not found")
   388  		}
   389  		iface := obj.Type().Underlying().(*Interface) // I must be an interface
   390  
   391  		// Each iface method m is embedded; and m's receiver base type name
   392  		// must match the method's name per the choice in the source file.
   393  		for i := 0; i < iface.NumMethods(); i++ {
   394  			m := iface.Method(i)
   395  			recvName := m.Type().(*Signature).Recv().Type().(*Named).Obj().Name()
   396  			if recvName != m.Name() {
   397  				t.Errorf("perm %v: got recv %s; want %s", perm, recvName, m.Name())
   398  			}
   399  		}
   400  	}
   401  }
   402  
   403  func TestIssue28282(t *testing.T) {
   404  	// create type interface { error }
   405  	et := Universe.Lookup("error").Type()
   406  	it := NewInterfaceType(nil, []Type{et})
   407  	it.Complete()
   408  	// verify that after completing the interface, the embedded method remains unchanged
   409  	want := et.Underlying().(*Interface).Method(0)
   410  	got := it.Method(0)
   411  	if got != want {
   412  		t.Fatalf("%s.Method(0): got %q (%p); want %q (%p)", it, got, got, want, want)
   413  	}
   414  	// verify that lookup finds the same method in both interfaces (redundant check)
   415  	obj, _, _ := LookupFieldOrMethod(et, false, nil, "Error")
   416  	if obj != want {
   417  		t.Fatalf("%s.Lookup: got %q (%p); want %q (%p)", et, obj, obj, want, want)
   418  	}
   419  	obj, _, _ = LookupFieldOrMethod(it, false, nil, "Error")
   420  	if obj != want {
   421  		t.Fatalf("%s.Lookup: got %q (%p); want %q (%p)", it, obj, obj, want, want)
   422  	}
   423  }
   424  
   425  func TestIssue29029(t *testing.T) {
   426  	f1 := mustParse(t, `package p; type A interface { M() }`)
   427  	f2 := mustParse(t, `package p; var B interface { A }`)
   428  
   429  	// printInfo prints the *Func definitions recorded in info, one *Func per line.
   430  	printInfo := func(info *Info) string {
   431  		var buf bytes.Buffer
   432  		for _, obj := range info.Defs {
   433  			if fn, ok := obj.(*Func); ok {
   434  				fmt.Fprintln(&buf, fn)
   435  			}
   436  		}
   437  		return buf.String()
   438  	}
   439  
   440  	// The *Func (method) definitions for package p must be the same
   441  	// independent on whether f1 and f2 are type-checked together, or
   442  	// incrementally.
   443  
   444  	// type-check together
   445  	var conf Config
   446  	info := &Info{Defs: make(map[*ast.Ident]Object)}
   447  	check := NewChecker(&conf, fset, NewPackage("", "p"), info)
   448  	if err := check.Files([]*ast.File{f1, f2}); err != nil {
   449  		t.Fatal(err)
   450  	}
   451  	want := printInfo(info)
   452  
   453  	// type-check incrementally
   454  	info = &Info{Defs: make(map[*ast.Ident]Object)}
   455  	check = NewChecker(&conf, fset, NewPackage("", "p"), info)
   456  	if err := check.Files([]*ast.File{f1}); err != nil {
   457  		t.Fatal(err)
   458  	}
   459  	if err := check.Files([]*ast.File{f2}); err != nil {
   460  		t.Fatal(err)
   461  	}
   462  	got := printInfo(info)
   463  
   464  	if got != want {
   465  		t.Errorf("\ngot : %swant: %s", got, want)
   466  	}
   467  }
   468  

View as plain text