golang.org/x/tools/gopls@v0.15.3/internal/golang/identifier_test.go (about) 1 // Copyright 2020 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 golang 6 7 import ( 8 "bytes" 9 "go/ast" 10 "go/parser" 11 "go/token" 12 "go/types" 13 "testing" 14 15 "golang.org/x/tools/internal/versions" 16 ) 17 18 func TestSearchForEnclosing(t *testing.T) { 19 tests := []struct { 20 desc string 21 // For convenience, consider the first occurrence of the identifier "X" in 22 // src. 23 src string 24 // By convention, "" means no type found. 25 wantTypeName string 26 }{ 27 { 28 // TODO(rFindley): is this correct, or do we want to resolve I2 here? 29 desc: "embedded interface in interface", 30 src: `package a; var y = i1.X; type i1 interface {I2}; type I2 interface{X()}`, 31 wantTypeName: "", 32 }, 33 { 34 desc: "embedded interface in struct", 35 src: `package a; var y = t.X; type t struct {I}; type I interface{X()}`, 36 wantTypeName: "I", 37 }, 38 { 39 desc: "double embedding", 40 src: `package a; var y = t1.X; type t1 struct {t2}; type t2 struct {I}; type I interface{X()}`, 41 wantTypeName: "I", 42 }, 43 } 44 45 for _, test := range tests { 46 test := test 47 t.Run(test.desc, func(t *testing.T) { 48 fset := token.NewFileSet() 49 file, err := parser.ParseFile(fset, "a.go", test.src, parser.AllErrors) 50 if err != nil { 51 t.Fatal(err) 52 } 53 column := 1 + bytes.IndexRune([]byte(test.src), 'X') 54 pos := posAt(1, column, fset, "a.go") 55 path := pathEnclosingObjNode(file, pos) 56 if path == nil { 57 t.Fatalf("no ident found at (1, %d)", column) 58 } 59 info := newInfo() 60 if _, err = (*types.Config)(nil).Check("p", fset, []*ast.File{file}, info); err != nil { 61 t.Fatal(err) 62 } 63 obj := searchForEnclosing(info, path) 64 if obj == nil { 65 if test.wantTypeName != "" { 66 t.Errorf("searchForEnclosing(...) = <nil>, want %q", test.wantTypeName) 67 } 68 return 69 } 70 if got := obj.Name(); got != test.wantTypeName { 71 t.Errorf("searchForEnclosing(...) = %q, want %q", got, test.wantTypeName) 72 } 73 }) 74 } 75 } 76 77 // posAt returns the token.Pos corresponding to the 1-based (line, column) 78 // coordinates in the file fname of fset. 79 func posAt(line, column int, fset *token.FileSet, fname string) token.Pos { 80 var tok *token.File 81 fset.Iterate(func(tf *token.File) bool { 82 if tf.Name() == fname { 83 tok = tf 84 return false 85 } 86 return true 87 }) 88 if tok == nil { 89 return token.NoPos 90 } 91 start := tok.LineStart(line) 92 return start + token.Pos(column-1) 93 } 94 95 // newInfo returns a types.Info with all maps populated. 96 func newInfo() *types.Info { 97 info := &types.Info{ 98 Types: make(map[ast.Expr]types.TypeAndValue), 99 Defs: make(map[*ast.Ident]types.Object), 100 Uses: make(map[*ast.Ident]types.Object), 101 Implicits: make(map[ast.Node]types.Object), 102 Selections: make(map[*ast.SelectorExpr]*types.Selection), 103 Scopes: make(map[ast.Node]*types.Scope), 104 } 105 versions.InitFileVersions(info) 106 return info 107 }