Black Lives Matter. Support the Equal Justice Initiative.

Source file src/go/types/resolver_test.go

Documentation: go/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  

View as plain text