func TestCheck(t *testing.T) { testenv.MustHaveGoBuild(t) // Declare builtins for testing. DefPredeclaredTestFuncs() // If explicit test files are specified, only check those. if files := *testFiles; files != "" { checkFiles(t, strings.Split(files, " ")) return } // Otherwise, run all the tests. for _, files := range tests { checkFiles(t, files) } }
func TestTypeString(t *testing.T) { testenv.MustHaveGoBuild(t) var tests []testEntry tests = append(tests, independentTestTypes...) tests = append(tests, dependentTestTypes...) for _, test := range tests { src := `package p; import "io"; type _ io.Writer; type T ` + test.src pkg, err := makePkg(t, src) if err != nil { t.Errorf("%s: %s", src, err) continue } typ := pkg.Scope().Lookup("T").Type().Underlying() if got := typ.String(); got != test.str { t.Errorf("%s: got %s, want %s", test.src, got, test.str) } } }
func TestEvalPos(t *testing.T) { testenv.MustHaveGoBuild(t) // The contents of /*-style comments are of the form // expr => value, type // where value may be the empty string. // Each expr is evaluated at the position of the comment // and the result is compared with the expected value // and type. var sources = []string{ ` package p import "fmt" import m "math" const c = 3.0 type T []int func f(a int, s string) float64 { fmt.Println("calling f") _ = m.Pi // use package math const d int = c + 1 var x int x = a + len(s) return float64(x) /* true => true, untyped bool */ /* fmt.Println => , func(a ...interface{}) (n int, err ?error) */ /* c => 3, untyped float */ /* T => , p.T */ /* a => , int */ /* s => , string */ /* d => 4, int */ /* x => , int */ /* d/c => 1, int */ /* c/2 => 3/2, untyped float */ /* m.Pi < m.E => false, untyped bool */ } `, ` package p /* c => 3, untyped float */ type T1 /* T1 => , p.T1 */ struct {} var v1 /* v1 => , int */ = 42 func /* f1 => , func(v1 float64) */ f1(v1 float64) { /* f1 => , func(v1 float64) */ /* v1 => , float64 */ var c /* c => 3, untyped float */ = "foo" /* c => , string */ { var c struct { c /* c => , string */ int } /* c => , struct{c int} */ _ = c } _ = func(a, b, c int) /* c => , string */ { /* c => , int */ } _ = c type FT /* FT => , p.FT */ interface{} } `, ` package p /* T => , p.T */ `, } fset := token.NewFileSet() var files []*ast.File for i, src := range sources { file, err := parser.ParseFile(fset, "p", src, parser.ParseComments) if err != nil { t.Fatalf("could not parse file %d: %s", i, err) } files = append(files, file) } conf := Config{Importer: importer.Default(files)} pkg, err := conf.Check("p", fset, files, nil) if err != nil { t.Fatal(err) } for _, file := range files { for _, group := range file.Comments { for _, comment := range group.List { s := comment.Text if len(s) >= 4 && s[:2] == "/*" && s[len(s)-2:] == "*/" { str, typ := split(s[2:len(s)-2], ", ") str, val := split(str, "=>") testEval(t, fset, pkg, comment.Pos(), str, nil, typ, val) } } } } }
// This tests that the package associated with the types.Object.Pkg method // is the type's package independent of the order in which the imports are // listed in the sources src1, src2 below. // The actual issue is in go/internal/gcimporter which has a corresponding // test; we leave this test here to verify correct behavior at the go/types // level. func TestIssue13898(t *testing.T) { testenv.MustHaveGoBuild(t) const src0 = ` package main import "github.com/tcard/sgo/sgo/types" func main() { var info types.Info for _, obj := range info.Uses { _ = obj.Pkg() } } ` // like src0, but also imports go/importer const src1 = ` package main import ( "github.com/tcard/sgo/sgo/types" _ "github.com/tcard/sgo/sgo/importer" ) func main() { var info types.Info for _, obj := range info.Uses { _ = obj.Pkg() } } ` // like src1 but with different import order // (used to fail with this issue) const src2 = ` package main import ( _ "github.com/tcard/sgo/sgo/importer" "github.com/tcard/sgo/sgo/types" ) func main() { var info types.Info for _, obj := range info.Uses { _ = obj.Pkg() } } ` f := func(test, src string) { f, err := parser.ParseFile(fset, "", src, 0) if err != nil { t.Fatal(err) } cfg := Config{Importer: importer.Default([]*ast.File{f})} info := Info{Uses: make(map[*ast.Ident]Object)} _, err = cfg.Check("main", fset, []*ast.File{f}, &info) if err != nil { t.Fatal(err) } var pkg *Package count := 0 for id, obj := range info.Uses { if id.Name == "Pkg" { pkg = obj.Pkg() count++ } } if count != 1 { t.Fatalf("%s: got %d entries named Pkg; want 1", test, count) } if pkg.Name() != "types" { t.Fatalf("%s: got %v; want package types", test, pkg) } } f("src0", src0) f("src1", src1) f("src2", src2) }
func TestScopesInfo(t *testing.T) { testenv.MustHaveGoBuild(t) var tests = []struct { src string scopes []string // list of scope descriptors of the form kind:varlist }{ {`package p0`, []string{ "file:", }}, {`package p1; import ( "fmt"; m "math"; _ "os" ); var ( _ = fmt.Println; _ = m.Pi )`, []string{ "file:fmt m", }}, {`package p2; func _() {}`, []string{ "file:", "func:", }}, {`package p3; func _(x, y int) {}`, []string{ "file:", "func:x y", }}, {`package p4; func _(x, y int) { x, z := 1, 2; _ = z }`, []string{ "file:", "func:x y", // redeclaration of x }}, {`package p5; func _(x, y int) (u, _ int) { return }`, []string{ "file:", "func:u x y", }}, {`package p6; func _() { { var x int; _ = x } }`, []string{ "file:", "func:", "block:x", }}, {`package p7; func _() { if true {} }`, []string{ "file:", "func:", "if:", "block:", }}, {`package p8; func _() { if x := 0; x < 0 { y := x; _ = y } }`, []string{ "file:", "func:", "if:x", "block:y", }}, {`package p9; func _() { switch x := 0; x {} }`, []string{ "file:", "func:", "switch:x", }}, {`package p10; func _() { switch x := 0; x { case 1: y := x; _ = y; default: }}`, []string{ "file:", "func:", "switch:x", "case:y", "case:", }}, {`package p11; func _(t interface{}) { switch t.(type) {} }`, []string{ "file:", "func:t", "type switch:", }}, {`package p12; func _(t interface{}) { switch t := t; t.(type) {} }`, []string{ "file:", "func:t", "type switch:t", }}, {`package p13; func _(t interface{}) { switch x := t.(type) { case int: _ = x } }`, []string{ "file:", "func:t", "type switch:", "case:x", // x implicitly declared }}, {`package p14; func _() { select{} }`, []string{ "file:", "func:", }}, {`package p15; func _(c chan int) { select{ case <-c: } }`, []string{ "file:", "func:c", "comm:", }}, {`package p16; func _(c chan int) { select{ case i := <-c: x := i; _ = x} }`, []string{ "file:", "func:c", "comm:i x", }}, {`package p17; func _() { for{} }`, []string{ "file:", "func:", "for:", "block:", }}, {`package p18; func _(n int) { for i := 0; i < n; i++ { _ = i } }`, []string{ "file:", "func:n", "for:i", "block:", }}, {`package p19; func _(a []int) { for i := range a { _ = i} }`, []string{ "file:", "func:a", "range:i", "block:", }}, {`package p20; var s int; func _(a []int) { for i, x := range a { s += x; _ = i } }`, []string{ "file:", "func:a", "range:i x", "block:", }}, } for _, test := range tests { info := Info{Scopes: make(map[ast.Node]*Scope)} name := mustTypecheck(t, "ScopesInfo", test.src, &info) // number of scopes must match if len(info.Scopes) != len(test.scopes) { t.Errorf("package %s: got %d scopes; want %d", name, 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("package %s: no matching scope found for %s", name, desc) } } } }
func TestPredicatesInfo(t *testing.T) { testenv.MustHaveGoBuild(t) var tests = []struct { src string expr string pred string }{ // void {`package n0; func f() { f() }`, `f()`, `void`}, // types {`package t0; type _ int`, `int`, `type`}, {`package t1; type _ []int`, `[]int`, `type`}, {`package t2; type _ func()`, `func()`, `type`}, // built-ins {`package b0; var _ = len("")`, `len`, `builtin`}, {`package b1; var _ = (len)("")`, `(len)`, `builtin`}, // constants {`package c0; var _ = 42`, `42`, `const`}, {`package c1; var _ = "foo" + "bar"`, `"foo" + "bar"`, `const`}, {`package c2; const (i = 1i; _ = i)`, `i`, `const`}, // values {`package v0; var (a, b int; _ = a + b)`, `a + b`, `value`}, {`package v1; var _ = &[]int{1}`, `([]int literal)`, `value`}, {`package v2; var _ = func(){}`, `(func() literal)`, `value`}, {`package v4; func f() { _ = f }`, `f`, `value`}, {`package v3; var _ ?*int = nil`, `nil`, `value, nil`}, {`package v3; var _ ?*int = (nil)`, `(nil)`, `value, nil`}, // addressable (and thus assignable) operands {`package a0; var (x int; _ = x)`, `x`, `value, addressable, assignable`}, {`package a1; var (p *int; _ = *p)`, `*p`, `value, addressable, assignable`}, {`package a2; var (s []int; _ = s[0])`, `s[0]`, `value, addressable, assignable`}, {`package a3; var (s struct{f int}; _ = s.f)`, `s.f`, `value, addressable, assignable`}, {`package a4; var (a [10]int; _ = a[0])`, `a[0]`, `value, addressable, assignable`}, {`package a5; func _(x int) { _ = x }`, `x`, `value, addressable, assignable`}, {`package a6; func _()(x int) { _ = x; return }`, `x`, `value, addressable, assignable`}, {`package a7; type T int; func (x T) _() { _ = x }`, `x`, `value, addressable, assignable`}, // composite literals are not addressable // assignable but not addressable values {`package s0; var (m map[int]int; _ = m[0])`, `m[0]`, `value, assignable, hasOk`}, {`package s1; var (m map[int]int;); func _() { _ \ _ = m[0] }`, `m[0]`, `value, assignable, hasOk`}, // hasOk expressions {`package k0; var (ch chan int; _ = <-ch)`, `<-ch`, `value, hasOk`}, {`package k1; var (ch chan int;); func _() { _, _ = <-ch }`, `<-ch`, `value, hasOk`}, // missing entries // - package names are collected in the Uses map // - identifiers being declared are collected in the Defs map {`package m0; import "os"; func _() { _ = os.Stdout }`, `os`, `<missing>`}, {`package m1; import p "os"; func _() { _ = p.Stdout }`, `p`, `<missing>`}, {`package m2; const c = 0`, `c`, `<missing>`}, {`package m3; type T int`, `T`, `<missing>`}, {`package m4; var v int`, `v`, `<missing>`}, {`package m5; func f() {}`, `f`, `<missing>`}, {`package m6; func _(x int) {}`, `x`, `<missing>`}, {`package m6; func _()(x int) { return }`, `x`, `<missing>`}, {`package m6; type T int; func (x T) _() {}`, `x`, `<missing>`}, } for _, test := range tests { info := Info{Types: make(map[ast.Expr]TypeAndValue)} name := mustTypecheck(t, "PredicatesInfo", test.src, &info) // look for expression predicates got := "<missing>" for e, tv := range info.Types { //println(name, ExprString(e)) if ExprString(e) == test.expr { got = predString(tv) break } } if got != test.pred { t.Errorf("package %s: got %s; want %s", name, got, test.pred) } } }
func TestResolveIdents(t *testing.T) { testenv.MustHaveGoBuild(t) sources := []string{ ` package p import "fmt" import "math" const pi = math.Pi func sin(x float64) float64 { return math.Sin(x) } var Println = fmt.Println `, ` package p import "fmt" type errorStringer struct { fmt.Stringer; error } func f() string { _ = "foo" return fmt.Sprintf("%d", g()) } func g() (x int) { return } `, ` package p import . "github.com/tcard/sgo/sgo/parser" import "sync" func h() Mode { return ImportsOnly } var _, x int = 1, 2 func init() {} type T struct{ *sync.Mutex; a, b, c int} type I interface{ m() } var _ = T{a: 1, b: 2, c: 3} func (_ T) m() {} func (T) _() {} var i I var _ = i.m func _(s []int) { for i, x := range s { _, _ = i, x } } func _(x interface{}) { switch x := x.(type) { case int: _ = x } switch {} // implicit 'true' tag } `, ` package p type S struct{} func (T) _() {} func (T) _() {} `, ` package p func _() { L0: L1: goto L0 for { goto L1 } if true { goto L2 } L2: } `, } pkgnames := []string{ "fmt", "math", } // parse package files fset := token.NewFileSet() var files []*ast.File for i, src := range sources { f, err := parser.ParseFile(fset, fmt.Sprintf("sources[%d]", i), src, parser.DeclarationErrors) if err != nil { t.Fatal(err) } files = append(files, f) } // resolve and type-check package AST importer := new(resolveTestImporter) importer.files = files conf := Config{ Importer: importer, AllowUseUninitializedVars: true, AllowUninitializedExprs: true, } uses := make(map[*ast.Ident]Object) defs := make(map[*ast.Ident]Object) _, err := conf.Check("testResolveIdents", fset, files, &Info{Defs: defs, Uses: uses}) if err != nil { t.Fatal(err) } // check that all packages were imported for _, name := range pkgnames { if !importer.imported[name] { t.Errorf("package %s not imported", name) } } // check that qualified identifiers are resolved for _, f := range files { ast.Inspect(f, func(n ast.Node) bool { if s, ok := n.(*ast.SelectorExpr); ok { if x, ok := s.X.(*ast.Ident); ok { obj := uses[x] if obj == nil { t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name) return false } if _, ok := obj.(*PkgName); ok && uses[s.Sel] == nil { t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name) return false } return false } return false } return true }) } for id, obj := range uses { if obj == nil { t.Errorf("%s: Uses[%s] == nil", fset.Position(id.Pos()), id.Name) } } // check that each identifier in the source is found in uses or defs or both var both []string for _, f := range files { ast.Inspect(f, func(n ast.Node) bool { if x, ok := n.(*ast.Ident); ok { var objects int if _, found := uses[x]; found { objects |= 1 delete(uses, x) } if _, found := defs[x]; found { objects |= 2 delete(defs, x) } if objects == 0 { t.Errorf("%s: unresolved identifier %s", fset.Position(x.Pos()), x.Name) } else if objects == 3 { both = append(both, x.Name) } return false } return true }) } // check the expected set of idents that are simultaneously uses and defs sort.Strings(both) if got, want := fmt.Sprint(both), "[Mutex Stringer error]"; got != want { t.Errorf("simultaneous uses/defs = %s, want %s", got, want) } // any left-over identifiers didn't exist in the source for x := range uses { t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name) } for x := range defs { t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name) } // TODO(gri) add tests to check ImplicitObj callbacks }