Skip to content

Commit 15b8886

Browse files
crazyhulkgopherbot
authored andcommitted
SignatureHelp: report signature of Ident if no enclosing CallExpr
Currently, SignatureHelp reports information only about an enclosing call expression. But it would be useful to show signature information after entering the name of a function such as "f" or "fmt.Println", without call parens. So, if there is no enclosing call, this change reports the signature of the selected identifier if it is callable. Fixes golang/go#68922 Change-Id: Ibb0700e354c5d6e5937fc7f7b5db65e9d96574bb GitHub-Last-Rev: 5e7965e GitHub-Pull-Request: #510 Reviewed-on: https://go-review.googlesource.com/c/tools/+/605983 Reviewed-by: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]> Auto-Submit: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 4e8d5c8 commit 15b8886

File tree

4 files changed

+68
-12
lines changed

4 files changed

+68
-12
lines changed

gopls/doc/features/passive.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ function call.
107107

108108
<img src='../assets/signature-help.png'>
109109

110+
Call parens are not necessary if the cursor is within an identifier
111+
that denotes a function or method. For example, Signature Help at
112+
`once.Do(initialize‸)` will describe `initialize`, not `once.Do`.
113+
110114
Client support:
111115
- **VS Code**: enabled by default.
112116
Also known as "[parameter hints](https://code.visualstudio.com/api/references/vscode-api#SignatureHelpProvider)" in the [IntelliSense settings](https://code.visualstudio.com/docs/editor/intellisense#_settings).

gopls/doc/release/v0.17.0.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,7 @@ constructor of the type of each symbol:
3535
`interface`, `struct`, `signature`, `pointer`, `array`, `map`, `slice`, `chan`, `string`, `number`, `bool`, and `invalid`.
3636
Editors may use this for syntax coloring.
3737

38+
## SignatureHelp for ident and values.
39+
40+
Now, function signature help can be used on any identifier with a function
41+
signature, not just within the parentheses of a function being called.

gopls/internal/golang/signature_help.go

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,32 @@ func SignatureHelp(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle
4545
if path == nil {
4646
return nil, 0, fmt.Errorf("cannot find node enclosing position")
4747
}
48-
FindCall:
49-
for _, node := range path {
48+
info := pkg.TypesInfo()
49+
var fnval ast.Expr
50+
loop:
51+
for i, node := range path {
5052
switch node := node.(type) {
53+
case *ast.Ident:
54+
// If the selected text is a function/method Ident orSelectorExpr,
55+
// even one not in function call position,
56+
// show help for its signature. Example:
57+
// once.Do(initialize⁁)
58+
// should show help for initialize, not once.Do.
59+
if t := info.TypeOf(node); t != nil &&
60+
info.Defs[node] == nil &&
61+
is[*types.Signature](t.Underlying()) {
62+
if sel, ok := path[i+1].(*ast.SelectorExpr); ok && sel.Sel == node {
63+
fnval = sel // e.g. fmt.Println⁁
64+
} else {
65+
fnval = node
66+
}
67+
break loop
68+
}
5169
case *ast.CallExpr:
5270
if pos >= node.Lparen && pos <= node.Rparen {
5371
callExpr = node
54-
break FindCall
72+
fnval = callExpr.Fun
73+
break loop
5574
}
5675
case *ast.FuncLit, *ast.FuncType:
5776
// The user is within an anonymous function,
@@ -70,20 +89,19 @@ FindCall:
7089
}
7190

7291
}
73-
if callExpr == nil || callExpr.Fun == nil {
92+
93+
if fnval == nil {
7494
return nil, 0, nil
7595
}
7696

77-
info := pkg.TypesInfo()
78-
7997
// Get the type information for the function being called.
8098
var sig *types.Signature
81-
if tv, ok := info.Types[callExpr.Fun]; !ok {
82-
return nil, 0, fmt.Errorf("cannot get type for Fun %[1]T (%[1]v)", callExpr.Fun)
99+
if tv, ok := info.Types[fnval]; !ok {
100+
return nil, 0, fmt.Errorf("cannot get type for Fun %[1]T (%[1]v)", fnval)
83101
} else if tv.IsType() {
84102
return nil, 0, nil // a conversion, not a call
85103
} else if sig, ok = tv.Type.Underlying().(*types.Signature); !ok {
86-
return nil, 0, fmt.Errorf("call operand is not a func or type: %[1]T (%[1]v)", callExpr.Fun)
104+
return nil, 0, fmt.Errorf("call operand is not a func or type: %[1]T (%[1]v)", fnval)
87105
}
88106
// Inv: sig != nil
89107

@@ -93,7 +111,7 @@ FindCall:
93111
// There is no object in certain cases such as calling a function returned by
94112
// a function (e.g. "foo()()").
95113
var obj types.Object
96-
switch t := callExpr.Fun.(type) {
114+
switch t := fnval.(type) {
97115
case *ast.Ident:
98116
obj = info.ObjectOf(t)
99117
case *ast.SelectorExpr:
@@ -116,7 +134,12 @@ FindCall:
116134
return nil, 0, bug.Errorf("call to unexpected built-in %v (%T)", obj, obj)
117135
}
118136

119-
activeParam := activeParameter(callExpr, sig.Params().Len(), sig.Variadic(), pos)
137+
activeParam := 0
138+
if callExpr != nil {
139+
// only return activeParam when CallExpr
140+
// because we don't modify arguments when get function signature only
141+
activeParam = activeParameter(callExpr, sig.Params().Len(), sig.Variadic(), pos)
142+
}
120143

121144
var (
122145
name string

gopls/internal/test/marker/testdata/signature/signature.txt

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"bytes"
1717
"encoding/json"
1818
"math/big"
19+
"fmt"
1920
)
2021

2122
func Foo(a string, b int) (c bool) {
@@ -49,6 +50,9 @@ func Qux() {
4950
Foo("foo", 123) //@signature(",", "Foo(a string, b int) (c bool)", 0)
5051
Foo("foo", 123) //@signature(" 1", "Foo(a string, b int) (c bool)", 1)
5152
Foo("foo", 123) //@signature(")", "Foo(a string, b int) (c bool)", 1)
53+
Foo("foo", 123) //@signature("o", "Foo(a string, b int) (c bool)", 0)
54+
_ = Foo //@signature("o", "Foo(a string, b int) (c bool)", 0)
55+
Foo //@signature("o", "Foo(a string, b int) (c bool)", 0)
5256

5357
Bar(13.37, 0x13) //@signature("13.37", "Bar(float64, ...byte)", 0)
5458
Bar(13.37, 0x37) //@signature("0x37", "Bar(float64, ...byte)", 1)
@@ -78,9 +82,28 @@ func Qux() {
7882

7983
_ = make([]int, 1, 2) //@signature("2", "make(t Type, size ...int) Type", 1)
8084

81-
Foo(myFunc(123), 456) //@signature("myFunc", "Foo(a string, b int) (c bool)", 0)
85+
Foo(myFunc(123), 456) //@signature("o(", "Foo(a string, b int) (c bool)", 0)
86+
Foo(myFunc(123), 456) //@signature("(m", "Foo(a string, b int) (c bool)", 0)
87+
Foo( myFunc(123), 456) //@signature(" m", "Foo(a string, b int) (c bool)", 0)
88+
Foo(myFunc(123), 456) //@signature(", ", "Foo(a string, b int) (c bool)", 0)
89+
Foo(myFunc(123), 456) //@signature("456", "Foo(a string, b int) (c bool)", 1)
90+
Foo(myFunc) //@signature(")", "Foo(a string, b int) (c bool)", 0)
91+
Foo(myFunc(123), 456) //@signature("(1", "myFunc(foo int) string", 0)
8292
Foo(myFunc(123), 456) //@signature("123", "myFunc(foo int) string", 0)
8393

94+
fmt.Println //@signature("ln", "Println(a ...any) (n int, err error)", 0)
95+
fmt.Println(myFunc) //@signature("ln", "Println(a ...any) (n int, err error)", 0)
96+
fmt.Println(myFunc) //@signature("Func", "myFunc(foo int) string", 0)
97+
98+
var hi string = "hello"
99+
var wl string = " world: %s"
100+
fmt.Println(fmt.Sprintf(wl, myFunc)) //@signature("Func", "myFunc(foo int) string", 0)
101+
fmt.Println(fmt.Sprintf(wl, myFunc)) //@signature("wl", "Sprintf(format string, a ...any) string", 0)
102+
fmt.Println(fmt.Sprintf(wl, myFunc)) //@signature(" m", "Sprintf(format string, a ...any) string", 1)
103+
fmt.Println(hi, fmt.Sprintf(wl, myFunc)) //@signature("Sprint", "Sprintf(format string, a ...any) string", 0)
104+
fmt.Println(hi, fmt.Sprintf(wl, myFunc)) //@signature(" fmt", "Println(a ...any) (n int, err error)", 0)
105+
fmt.Println(hi, fmt.Sprintf(wl, myFunc)) //@signature("hi", "Println(a ...any) (n int, err error)", 0)
106+
84107
panic("oops!") //@signature(")", "panic(v any)", 0)
85108
println("hello", "world") //@signature(",", "println(args ...Type)", 0)
86109

@@ -100,6 +123,8 @@ package signature
100123

101124
func _() {
102125
Foo(//@signature("//", "Foo(a string, b int) (c bool)", 0)
126+
Foo.//@signature("//", "Foo(a string, b int) (c bool)", 0)
127+
Foo.//@signature("oo", "Foo(a string, b int) (c bool)", 0)
103128
}
104129

105130
-- signature/signature3.go --

0 commit comments

Comments
 (0)