Home | History | Annotate | Download | only in types
      1 // Copyright 2011 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 package types_test
      6 
      7 import (
      8 	"fmt"
      9 	"go/ast"
     10 	"go/importer"
     11 	"go/parser"
     12 	"go/token"
     13 	"internal/testenv"
     14 	"sort"
     15 	"testing"
     16 
     17 	. "go/types"
     18 )
     19 
     20 type resolveTestImporter struct {
     21 	importer ImporterFrom
     22 	imported map[string]bool
     23 }
     24 
     25 func (imp *resolveTestImporter) Import(string) (*Package, error) {
     26 	panic("should not be called")
     27 }
     28 
     29 func (imp *resolveTestImporter) ImportFrom(path, srcDir string, mode ImportMode) (*Package, error) {
     30 	if mode != 0 {
     31 		panic("mode must be 0")
     32 	}
     33 	if imp.importer == nil {
     34 		imp.importer = importer.Default().(ImporterFrom)
     35 		imp.imported = make(map[string]bool)
     36 	}
     37 	pkg, err := imp.importer.ImportFrom(path, srcDir, mode)
     38 	if err != nil {
     39 		return nil, err
     40 	}
     41 	imp.imported[path] = true
     42 	return pkg, nil
     43 }
     44 
     45 func TestResolveIdents(t *testing.T) {
     46 	testenv.MustHaveGoBuild(t)
     47 
     48 	sources := []string{
     49 		`
     50 		package p
     51 		import "fmt"
     52 		import "math"
     53 		const pi = math.Pi
     54 		func sin(x float64) float64 {
     55 			return math.Sin(x)
     56 		}
     57 		var Println = fmt.Println
     58 		`,
     59 		`
     60 		package p
     61 		import "fmt"
     62 		type errorStringer struct { fmt.Stringer; error }
     63 		func f() string {
     64 			_ = "foo"
     65 			return fmt.Sprintf("%d", g())
     66 		}
     67 		func g() (x int) { return }
     68 		`,
     69 		`
     70 		package p
     71 		import . "go/parser"
     72 		import "sync"
     73 		func h() Mode { return ImportsOnly }
     74 		var _, x int = 1, 2
     75 		func init() {}
     76 		type T struct{ *sync.Mutex; a, b, c int}
     77 		type I interface{ m() }
     78 		var _ = T{a: 1, b: 2, c: 3}
     79 		func (_ T) m() {}
     80 		func (T) _() {}
     81 		var i I
     82 		var _ = i.m
     83 		func _(s []int) { for i, x := range s { _, _ = i, x } }
     84 		func _(x interface{}) {
     85 			switch x := x.(type) {
     86 			case int:
     87 				_ = x
     88 			}
     89 			switch {} // implicit 'true' tag
     90 		}
     91 		`,
     92 		`
     93 		package p
     94 		type S struct{}
     95 		func (T) _() {}
     96 		func (T) _() {}
     97 		`,
     98 		`
     99 		package p
    100 		func _() {
    101 		L0:
    102 		L1:
    103 			goto L0
    104 			for {
    105 				goto L1
    106 			}
    107 			if true {
    108 				goto L2
    109 			}
    110 		L2:
    111 		}
    112 		`,
    113 	}
    114 
    115 	pkgnames := []string{
    116 		"fmt",
    117 		"math",
    118 	}
    119 
    120 	// parse package files
    121 	fset := token.NewFileSet()
    122 	var files []*ast.File
    123 	for i, src := range sources {
    124 		f, err := parser.ParseFile(fset, fmt.Sprintf("sources[%d]", i), src, parser.DeclarationErrors)
    125 		if err != nil {
    126 			t.Fatal(err)
    127 		}
    128 		files = append(files, f)
    129 	}
    130 
    131 	// resolve and type-check package AST
    132 	importer := new(resolveTestImporter)
    133 	conf := Config{Importer: importer}
    134 	uses := make(map[*ast.Ident]Object)
    135 	defs := make(map[*ast.Ident]Object)
    136 	_, err := conf.Check("testResolveIdents", fset, files, &Info{Defs: defs, Uses: uses})
    137 	if err != nil {
    138 		t.Fatal(err)
    139 	}
    140 
    141 	// check that all packages were imported
    142 	for _, name := range pkgnames {
    143 		if !importer.imported[name] {
    144 			t.Errorf("package %s not imported", name)
    145 		}
    146 	}
    147 
    148 	// check that qualified identifiers are resolved
    149 	for _, f := range files {
    150 		ast.Inspect(f, func(n ast.Node) bool {
    151 			if s, ok := n.(*ast.SelectorExpr); ok {
    152 				if x, ok := s.X.(*ast.Ident); ok {
    153 					obj := uses[x]
    154 					if obj == nil {
    155 						t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name)
    156 						return false
    157 					}
    158 					if _, ok := obj.(*PkgName); ok && uses[s.Sel] == nil {
    159 						t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name)
    160 						return false
    161 					}
    162 					return false
    163 				}
    164 				return false
    165 			}
    166 			return true
    167 		})
    168 	}
    169 
    170 	for id, obj := range uses {
    171 		if obj == nil {
    172 			t.Errorf("%s: Uses[%s] == nil", fset.Position(id.Pos()), id.Name)
    173 		}
    174 	}
    175 
    176 	// check that each identifier in the source is found in uses or defs or both
    177 	var both []string
    178 	for _, f := range files {
    179 		ast.Inspect(f, func(n ast.Node) bool {
    180 			if x, ok := n.(*ast.Ident); ok {
    181 				var objects int
    182 				if _, found := uses[x]; found {
    183 					objects |= 1
    184 					delete(uses, x)
    185 				}
    186 				if _, found := defs[x]; found {
    187 					objects |= 2
    188 					delete(defs, x)
    189 				}
    190 				if objects == 0 {
    191 					t.Errorf("%s: unresolved identifier %s", fset.Position(x.Pos()), x.Name)
    192 				} else if objects == 3 {
    193 					both = append(both, x.Name)
    194 				}
    195 				return false
    196 			}
    197 			return true
    198 		})
    199 	}
    200 
    201 	// check the expected set of idents that are simultaneously uses and defs
    202 	sort.Strings(both)
    203 	if got, want := fmt.Sprint(both), "[Mutex Stringer error]"; got != want {
    204 		t.Errorf("simultaneous uses/defs = %s, want %s", got, want)
    205 	}
    206 
    207 	// any left-over identifiers didn't exist in the source
    208 	for x := range uses {
    209 		t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name)
    210 	}
    211 	for x := range defs {
    212 		t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name)
    213 	}
    214 
    215 	// TODO(gri) add tests to check ImplicitObj callbacks
    216 }
    217