Source file src/go/types/resolver_test.go

     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/token"
    12  	"internal/testenv"
    13  	"slices"
    14  	"testing"
    15  
    16  	. "go/types"
    17  )
    18  
    19  type resolveTestImporter struct {
    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 = importer.Default().(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 := new(resolveTestImporter)
   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  

View as plain text