internal/lsp/source: return all field funcs from outgoing callhierarchy

Outgoing callhierarchy didn't handle different functions defined as
field in a struct as separate functions since they were declared by the
same AST node.

This change adds the identifier name to the key, so that a function
must share both declaration node and name to be considered "the same".

Fixes golang/go#43456

Change-Id: Ifbced98f2e8fc3a303834f7cefbae66829b68d27
Reviewed-on: https://go-review.googlesource.com/c/tools/+/280618
Trust: Pontus Leitzler <leitzler@gmail.com>
Run-TryBot: Pontus Leitzler <leitzler@gmail.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Pontus Leitzler 2021-01-01 11:12:34 +01:00 committed by Dmitri Shuralyov
parent 6c3993fd31
commit b8e0803c79
2 changed files with 34 additions and 5 deletions

View File

@ -247,8 +247,14 @@ func collectCallExpressions(fset *token.FileSet, mapper *protocol.ColumnMapper,
// toProtocolOutgoingCalls returns an array of protocol.CallHierarchyOutgoingCall for ast call expressions.
// Calls to the same function are assigned to the same declaration.
func toProtocolOutgoingCalls(ctx context.Context, snapshot Snapshot, fh FileHandle, callRanges []protocol.Range) ([]protocol.CallHierarchyOutgoingCall, error) {
// multiple calls could be made to the same function
var outgoingCalls = map[ast.Node]*protocol.CallHierarchyOutgoingCall{}
// Multiple calls could be made to the same function, defined by "same declaration
// AST node & same idenfitier name" to provide a unique identifier key even when
// the func is declared in a struct or interface.
type key struct {
decl ast.Node
name string
}
outgoingCalls := map[key]*protocol.CallHierarchyOutgoingCall{}
for _, callRange := range callRanges {
identifier, err := Identifier(ctx, snapshot, fh, callRange.Start)
if err != nil {
@ -263,7 +269,7 @@ func toProtocolOutgoingCalls(ctx context.Context, snapshot Snapshot, fh FileHand
continue
}
if outgoingCall, ok := outgoingCalls[identifier.Declaration.node]; ok {
if outgoingCall, ok := outgoingCalls[key{identifier.Declaration.node, identifier.Name}]; ok {
outgoingCall.FromRanges = append(outgoingCall.FromRanges, callRange)
continue
}
@ -277,7 +283,7 @@ func toProtocolOutgoingCalls(ctx context.Context, snapshot Snapshot, fh FileHand
return nil, err
}
outgoingCalls[identifier.Declaration.node] = &protocol.CallHierarchyOutgoingCall{
outgoingCalls[key{identifier.Declaration.node, identifier.Name}] = &protocol.CallHierarchyOutgoingCall{
To: protocol.CallHierarchyItem{
Name: identifier.Name,
Kind: protocol.Function,

View File

@ -26,7 +26,7 @@ var x = func() { //@mark(hierarchyLiteral, "func"),mark(hierarchyLiteralOut, "x"
}
// D is exported to test incoming/outgoing calls across packages
func D() { //@mark(hierarchyD, "D"),incomingcalls(hierarchyD, hierarchyA, hierarchyB, hierarchyC, hierarchyLiteral, incomingA),outgoingcalls(hierarchyD, hierarchyE, hierarchyF, hierarchyG, hierarchyLiteralOut, outgoingB, hierarchyFoo)
func D() { //@mark(hierarchyD, "D"),incomingcalls(hierarchyD, hierarchyA, hierarchyB, hierarchyC, hierarchyLiteral, incomingA),outgoingcalls(hierarchyD, hierarchyE, hierarchyF, hierarchyG, hierarchyLiteralOut, outgoingB, hierarchyFoo, hierarchyH, hierarchyI, hierarchyJ, hierarchyK)
e()
x()
F()
@ -34,6 +34,14 @@ func D() { //@mark(hierarchyD, "D"),incomingcalls(hierarchyD, hierarchyA, hierar
outgoing.B()
foo := func() {} //@mark(hierarchyFoo, "foo"),incomingcalls(hierarchyFoo, hierarchyD),outgoingcalls(hierarchyFoo)
foo()
var i Interface = impl{}
i.H()
i.I()
s := Struct{}
s.J()
s.K()
}
func e() {} //@mark(hierarchyE, "e")
@ -42,3 +50,18 @@ func e() {} //@mark(hierarchyE, "e")
func F() {} //@mark(hierarchyF, "F")
func g() {} //@mark(hierarchyG, "g")
type Interface interface {
H() //@mark(hierarchyH, "H")
I() //@mark(hierarchyI, "I")
}
type impl struct{}
func (i impl) H() {}
func (i impl) I() {}
type Struct struct {
J func() //@mark(hierarchyJ, "J")
K func() //@mark(hierarchyK, "K")
}