blob: a02abce081b8334e3bb57bb0444ff6bced209e79 [file] [log] [blame]
// Copyright 2011 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.
package types2_test
import (
"cmd/compile/internal/syntax"
"fmt"
"internal/testenv"
"sort"
"testing"
. "cmd/compile/internal/types2"
)
type resolveTestImporter struct {
importer ImporterFrom
imported map[string]bool
}
func (imp *resolveTestImporter) Import(string) (*Package, error) {
panic("should not be called")
}
func (imp *resolveTestImporter) ImportFrom(path, srcDir string, mode ImportMode) (*Package, error) {
if mode != 0 {
panic("mode must be 0")
}
if imp.importer == nil {
imp.importer = defaultImporter().(ImporterFrom)
imp.imported = make(map[string]bool)
}
pkg, err := imp.importer.ImportFrom(path, srcDir, mode)
if err != nil {
return nil, err
}
imp.imported[path] = true
return pkg, nil
}
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 . "go/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
var files []*syntax.File
for i, src := range sources {
f, err := parseSrc(fmt.Sprintf("sources[%d]", i), src)
if err != nil {
t.Fatal(err)
}
files = append(files, f)
}
// resolve and type-check package AST
importer := new(resolveTestImporter)
conf := Config{Importer: importer}
uses := make(map[*syntax.Name]Object)
defs := make(map[*syntax.Name]Object)
_, err := conf.Check("testResolveIdents", 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 {
syntax.Crawl(f, func(n syntax.Node) bool {
if s, ok := n.(*syntax.SelectorExpr); ok {
if x, ok := s.X.(*syntax.Name); ok {
obj := uses[x]
if obj == nil {
t.Errorf("%s: unresolved qualified identifier %s", x.Pos(), x.Value)
return true
}
if _, ok := obj.(*PkgName); ok && uses[s.Sel] == nil {
t.Errorf("%s: unresolved selector %s", s.Sel.Pos(), s.Sel.Value)
return true
}
return true
}
return true
}
return false
})
}
for id, obj := range uses {
if obj == nil {
t.Errorf("%s: Uses[%s] == nil", id.Pos(), id.Value)
}
}
// Check that each identifier in the source is found in uses or defs or both.
// We need the foundUses/Defs maps (rather then just deleting the found objects
// from the uses and defs maps) because syntax.Walk traverses shared nodes multiple
// times (e.g. types in field lists such as "a, b, c int").
foundUses := make(map[*syntax.Name]bool)
foundDefs := make(map[*syntax.Name]bool)
var both []string
for _, f := range files {
syntax.Crawl(f, func(n syntax.Node) bool {
if x, ok := n.(*syntax.Name); ok {
var objects int
if _, found := uses[x]; found {
objects |= 1
foundUses[x] = true
}
if _, found := defs[x]; found {
objects |= 2
foundDefs[x] = true
}
switch objects {
case 0:
t.Errorf("%s: unresolved identifier %s", x.Pos(), x.Value)
case 3:
both = append(both, x.Value)
}
return true
}
return false
})
}
// 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 {
if !foundUses[x] {
t.Errorf("%s: identifier %s not present in source", x.Pos(), x.Value)
}
}
for x := range defs {
if !foundDefs[x] {
t.Errorf("%s: identifier %s not present in source", x.Pos(), x.Value)
}
}
// TODO(gri) add tests to check ImplicitObj callbacks
}