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  }