blob: 8cb955c2ba04ac8d857e3b274ca3f38701b53e95 [file] [log] [blame]
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// TODO(gri) This file needs to be expanded significantly.
package types
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"strings"
"testing"
)
func pkgFor(path, source string, info *Info) (*Package, error) {
fset = token.NewFileSet()
f, err := parser.ParseFile(fset, path, source, 0)
if err != nil {
return nil, err
}
var conf Config
pkg, err := conf.Check(path, fset, []*ast.File{f}, info)
if err != nil {
return nil, err
}
return pkg, nil
}
func mustTypecheck(t *testing.T, path, source string, info *Info) *Package {
pkg, err := pkgFor(path, source, info)
if err != nil {
t.Fatalf("%s: didn't type-check (%s)", path, err)
}
return pkg
}
func TestCommaOkTypes(t *testing.T) {
var tests = []struct {
src string
expr string // comma-ok expression string
typ string // typestring of comma-ok value
}{
{`package p; var x interface{}; var _, _ = x.(int)`,
`x.(int)`,
`(int, bool)`,
},
{`package p; var x interface{}; func _() { _, _ = x.(int) }`,
`x.(int)`,
`(int, bool)`,
},
{`package p; type mybool bool; var m map[string]complex128; var b mybool; func _() { _, b = m["foo"] }`,
`m["foo"]`,
`(complex128, p.mybool)`,
},
{`package p; var c chan string; var _, _ = <-c`,
`<-c`,
`(string, bool)`,
},
}
for i, test := range tests {
path := fmt.Sprintf("CommaOkTypes%d", i)
info := Info{Types: make(map[ast.Expr]Type)}
mustTypecheck(t, path, test.src, &info)
// look for comma-ok expression type
var typ Type
for e, t := range info.Types {
if exprString(e) == test.expr {
typ = t
break
}
}
if typ == nil {
t.Errorf("%s: no type found for %s", path, test.expr)
continue
}
// check that type is correct
if got := typ.String(); got != test.typ {
t.Errorf("%s: got %s; want %s", path, got, test.typ)
}
}
}
func TestScopesInfo(t *testing.T) {
var tests = []struct {
src string
scopes []string // list of scope descriptors of the form kind:varlist
}{
{`package p`, []string{
"file:",
}},
{`package p; import ( "fmt"; m "math"; _ "os" ); var ( _ = fmt.Println; _ = m.Pi )`, []string{
"file:fmt m",
}},
{`package p; func _() {}`, []string{
"file:", "func:",
}},
{`package p; func _(x, y int) {}`, []string{
"file:", "func:x y",
}},
{`package p; func _(x, y int) { x, z := 1, 2; _ = z }`, []string{
"file:", "func:x y z", // redeclaration of x
}},
{`package p; func _(x, y int) (u, _ int) { return }`, []string{
"file:", "func:u x y",
}},
{`package p; func _() { { var x int; _ = x } }`, []string{
"file:", "func:", "block:x",
}},
{`package p; func _() { if true {} }`, []string{
"file:", "func:", "if:", "block:",
}},
{`package p; func _() { if x := 0; x < 0 { y := x; _ = y } }`, []string{
"file:", "func:", "if:x", "block:y",
}},
{`package p; func _() { switch x := 0; x {} }`, []string{
"file:", "func:", "switch:x",
}},
{`package p; func _() { switch x := 0; x { case 1: y := x; _ = y; default: }}`, []string{
"file:", "func:", "switch:x", "case:y", "case:",
}},
{`package p; func _(t interface{}) { switch t.(type) {} }`, []string{
"file:", "func:t", "type switch:",
}},
{`package p; func _(t interface{}) { switch t := t; t.(type) {} }`, []string{
"file:", "func:t", "type switch:t",
}},
{`package p; func _(t interface{}) { switch x := t.(type) { case int: _ = x } }`, []string{
"file:", "func:t", "type switch:", "case:x", // x implicitly declared
}},
{`package p; func _() { select{} }`, []string{
"file:", "func:",
}},
{`package p; func _(c chan int) { select{ case <-c: } }`, []string{
"file:", "func:c", "comm:",
}},
{`package p; func _(c chan int) { select{ case i := <-c: x := i; _ = x} }`, []string{
"file:", "func:c", "comm:i x",
}},
{`package p; func _() { for{} }`, []string{
"file:", "func:", "for:", "block:",
}},
{`package p; func _(n int) { for i := 0; i < n; i++ { _ = i } }`, []string{
"file:", "func:n", "for:i", "block:",
}},
{`package p; func _(a []int) { for i := range a { _ = i} }`, []string{
"file:", "func:a", "range:i", "block:",
}},
{`package p; var s int; func _(a []int) { for i, x := range a { s += x; _ = i } }`, []string{
"file:", "func:a", "range:i x", "block:",
}},
}
for i, test := range tests {
path := fmt.Sprintf("ScopesInfo%d", i)
info := Info{Scopes: make(map[ast.Node]*Scope)}
mustTypecheck(t, path, test.src, &info)
// number of scopes must match
if len(info.Scopes) != len(test.scopes) {
t.Errorf("%s: got %d scopes; want %d", path, len(info.Scopes), len(test.scopes))
}
// scope descriptions must match
for node, scope := range info.Scopes {
kind := "<unknown node kind>"
switch node.(type) {
case *ast.File:
kind = "file"
case *ast.FuncType:
kind = "func"
case *ast.BlockStmt:
kind = "block"
case *ast.IfStmt:
kind = "if"
case *ast.SwitchStmt:
kind = "switch"
case *ast.TypeSwitchStmt:
kind = "type switch"
case *ast.CaseClause:
kind = "case"
case *ast.CommClause:
kind = "comm"
case *ast.ForStmt:
kind = "for"
case *ast.RangeStmt:
kind = "range"
}
// look for matching scope description
desc := kind + ":" + strings.Join(scope.Names(), " ")
found := false
for _, d := range test.scopes {
if desc == d {
found = true
break
}
}
if !found {
t.Errorf("%s: no matching scope found for %s", path, desc)
}
}
}
}