resolver_test.go raw

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