Home | History | Annotate | Download | only in 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 	"fmt"
     11 	"go/ast"
     12 	"go/importer"
     13 	"go/parser"
     14 	"internal/testenv"
     15 	"sort"
     16 	"strings"
     17 	"testing"
     18 
     19 	. "go/types"
     20 )
     21 
     22 func TestIssue5770(t *testing.T) {
     23 	src := `package p; type S struct{T}`
     24 	f, err := parser.ParseFile(fset, "", src, 0)
     25 	if err != nil {
     26 		t.Fatal(err)
     27 	}
     28 
     29 	conf := Config{Importer: importer.Default()}
     30 	_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash
     31 	want := "undeclared name: T"
     32 	if err == nil || !strings.Contains(err.Error(), want) {
     33 		t.Errorf("got: %v; want: %s", err, want)
     34 	}
     35 }
     36 
     37 func TestIssue5849(t *testing.T) {
     38 	src := `
     39 package p
     40 var (
     41 	s uint
     42 	_ = uint8(8)
     43 	_ = uint16(16) << s
     44 	_ = uint32(32 << s)
     45 	_ = uint64(64 << s + s)
     46 	_ = (interface{})("foo")
     47 	_ = (interface{})(nil)
     48 )`
     49 	f, err := parser.ParseFile(fset, "", src, 0)
     50 	if err != nil {
     51 		t.Fatal(err)
     52 	}
     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, err := parser.ParseFile(fset, "", src, 0)
     98 	if err != nil {
     99 		t.Fatal(err)
    100 	}
    101 
    102 	var conf Config
    103 	types := make(map[ast.Expr]TypeAndValue)
    104 	_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
    105 	if err != nil {
    106 		t.Fatal(err)
    107 	}
    108 
    109 	want := Typ[Int]
    110 	n := 0
    111 	for x, tv := range types {
    112 		if _, ok := x.(*ast.CallExpr); ok {
    113 			if tv.Type != want {
    114 				t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), tv.Type, want)
    115 			}
    116 			n++
    117 		}
    118 	}
    119 
    120 	if n != 2 {
    121 		t.Errorf("got %d CallExprs; want 2", n)
    122 	}
    123 }
    124 
    125 func TestIssue7245(t *testing.T) {
    126 	src := `
    127 package p
    128 func (T) m() (res bool) { return }
    129 type T struct{} // receiver type after method declaration
    130 `
    131 	f, err := parser.ParseFile(fset, "", src, 0)
    132 	if err != nil {
    133 		t.Fatal(err)
    134 	}
    135 
    136 	var conf Config
    137 	defs := make(map[*ast.Ident]Object)
    138 	_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs})
    139 	if err != nil {
    140 		t.Fatal(err)
    141 	}
    142 
    143 	m := f.Decls[0].(*ast.FuncDecl)
    144 	res1 := defs[m.Name].(*Func).Type().(*Signature).Results().At(0)
    145 	res2 := defs[m.Type.Results.List[0].Names[0]].(*Var)
    146 
    147 	if res1 != res2 {
    148 		t.Errorf("got %s (%p) != %s (%p)", res1, res2, res1, res2)
    149 	}
    150 }
    151 
    152 // This tests that uses of existing vars on the LHS of an assignment
    153 // are Uses, not Defs; and also that the (illegal) use of a non-var on
    154 // the LHS of an assignment is a Use nonetheless.
    155 func TestIssue7827(t *testing.T) {
    156 	const src = `
    157 package p
    158 func _() {
    159 	const w = 1        // defs w
    160         x, y := 2, 3       // defs x, y
    161         w, x, z := 4, 5, 6 // uses w, x, defs z; error: cannot assign to w
    162         _, _, _ = x, y, z  // uses x, y, z
    163 }
    164 `
    165 	const want = `L3 defs func p._()
    166 L4 defs const w untyped int
    167 L5 defs var x int
    168 L5 defs var y int
    169 L6 defs var z int
    170 L6 uses const w untyped int
    171 L6 uses var x int
    172 L7 uses var x int
    173 L7 uses var y int
    174 L7 uses var z int`
    175 
    176 	f, err := parser.ParseFile(fset, "", src, 0)
    177 	if err != nil {
    178 		t.Fatal(err)
    179 	}
    180 
    181 	// don't abort at the first error
    182 	conf := Config{Error: func(err error) { t.Log(err) }}
    183 	defs := make(map[*ast.Ident]Object)
    184 	uses := make(map[*ast.Ident]Object)
    185 	_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs, Uses: uses})
    186 	if s := fmt.Sprint(err); !strings.HasSuffix(s, "cannot assign to w") {
    187 		t.Errorf("Check: unexpected error: %s", s)
    188 	}
    189 
    190 	var facts []string
    191 	for id, obj := range defs {
    192 		if obj != nil {
    193 			fact := fmt.Sprintf("L%d defs %s", fset.Position(id.Pos()).Line, obj)
    194 			facts = append(facts, fact)
    195 		}
    196 	}
    197 	for id, obj := range uses {
    198 		fact := fmt.Sprintf("L%d uses %s", fset.Position(id.Pos()).Line, obj)
    199 		facts = append(facts, fact)
    200 	}
    201 	sort.Strings(facts)
    202 
    203 	got := strings.Join(facts, "\n")
    204 	if got != want {
    205 		t.Errorf("Unexpected defs/uses\ngot:\n%s\nwant:\n%s", got, want)
    206 	}
    207 }
    208 
    209 // This tests that the package associated with the types.Object.Pkg method
    210 // is the type's package independent of the order in which the imports are
    211 // listed in the sources src1, src2 below.
    212 // The actual issue is in go/internal/gcimporter which has a corresponding
    213 // test; we leave this test here to verify correct behavior at the go/types
    214 // level.
    215 func TestIssue13898(t *testing.T) {
    216 	testenv.MustHaveGoBuild(t)
    217 
    218 	const src0 = `
    219 package main
    220 
    221 import "go/types"
    222 
    223 func main() {
    224 	var info types.Info
    225 	for _, obj := range info.Uses {
    226 		_ = obj.Pkg()
    227 	}
    228 }
    229 `
    230 	// like src0, but also imports go/importer
    231 	const src1 = `
    232 package main
    233 
    234 import (
    235 	"go/types"
    236 	_ "go/importer"
    237 )
    238 
    239 func main() {
    240 	var info types.Info
    241 	for _, obj := range info.Uses {
    242 		_ = obj.Pkg()
    243 	}
    244 }
    245 `
    246 	// like src1 but with different import order
    247 	// (used to fail with this issue)
    248 	const src2 = `
    249 package main
    250 
    251 import (
    252 	_ "go/importer"
    253 	"go/types"
    254 )
    255 
    256 func main() {
    257 	var info types.Info
    258 	for _, obj := range info.Uses {
    259 		_ = obj.Pkg()
    260 	}
    261 }
    262 `
    263 	f := func(test, src string) {
    264 		f, err := parser.ParseFile(fset, "", src, 0)
    265 		if err != nil {
    266 			t.Fatal(err)
    267 		}
    268 		cfg := Config{Importer: importer.Default()}
    269 		info := Info{Uses: make(map[*ast.Ident]Object)}
    270 		_, err = cfg.Check("main", fset, []*ast.File{f}, &info)
    271 		if err != nil {
    272 			t.Fatal(err)
    273 		}
    274 
    275 		var pkg *Package
    276 		count := 0
    277 		for id, obj := range info.Uses {
    278 			if id.Name == "Pkg" {
    279 				pkg = obj.Pkg()
    280 				count++
    281 			}
    282 		}
    283 		if count != 1 {
    284 			t.Fatalf("%s: got %d entries named Pkg; want 1", test, count)
    285 		}
    286 		if pkg.Name() != "types" {
    287 			t.Fatalf("%s: got %v; want package types", test, pkg)
    288 		}
    289 	}
    290 
    291 	f("src0", src0)
    292 	f("src1", src1)
    293 	f("src2", src2)
    294 }
    295 
    296 func TestIssue22525(t *testing.T) {
    297 	src := `package p; func f() { var a, b, c, d, e int }`
    298 	f, err := parser.ParseFile(fset, "", src, 0)
    299 	if err != nil {
    300 		t.Fatal(err)
    301 	}
    302 
    303 	got := "\n"
    304 	conf := Config{Error: func(err error) { got += err.Error() + "\n" }}
    305 	conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash
    306 	want := `
    307 1:27: a declared but not used
    308 1:30: b declared but not used
    309 1:33: c declared but not used
    310 1:36: d declared but not used
    311 1:39: e declared but not used
    312 `
    313 	if got != want {
    314 		t.Errorf("got: %swant: %s", got, want)
    315 	}
    316 }
    317