1 // Copyright 2018 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.
13 "golang.org/x/tools/go/ast/astutil"
14 "golang.org/x/tools/internal/event"
15 "golang.org/x/tools/internal/lsp/protocol"
16 errors "golang.org/x/xerrors"
19 func SignatureHelp(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) (*protocol.SignatureInformation, int, error) {
20 ctx, done := event.Start(ctx, "source.SignatureHelp")
23 pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
25 return nil, 0, errors.Errorf("getting file for SignatureHelp: %w", err)
27 spn, err := pgf.Mapper.PointSpan(pos)
31 rng, err := spn.Range(pgf.Mapper.Converter)
35 // Find a call expression surrounding the query position.
36 var callExpr *ast.CallExpr
37 path, _ := astutil.PathEnclosingInterval(pgf.File, rng.Start, rng.Start)
39 return nil, 0, errors.Errorf("cannot find node enclosing position")
42 for _, node := range path {
43 switch node := node.(type) {
45 if rng.Start >= node.Lparen && rng.Start <= node.Rparen {
49 case *ast.FuncLit, *ast.FuncType:
50 // The user is within an anonymous function,
51 // which may be the parameter to the *ast.CallExpr.
52 // Don't show signature help in this case.
53 return nil, 0, errors.Errorf("no signature help within a function declaration")
56 if callExpr == nil || callExpr.Fun == nil {
57 return nil, 0, errors.Errorf("cannot find an enclosing function")
60 qf := Qualifier(pgf.File, pkg.GetTypes(), pkg.GetTypesInfo())
62 // Get the object representing the function, if available.
63 // There is no object in certain cases such as calling a function returned by
64 // a function (e.g. "foo()()").
66 switch t := callExpr.Fun.(type) {
68 obj = pkg.GetTypesInfo().ObjectOf(t)
69 case *ast.SelectorExpr:
70 obj = pkg.GetTypesInfo().ObjectOf(t.Sel)
73 // Handle builtin functions separately.
74 if obj, ok := obj.(*types.Builtin); ok {
75 return builtinSignature(ctx, snapshot, callExpr, obj.Name(), rng.Start)
78 // Get the type information for the function being called.
79 sigType := pkg.GetTypesInfo().TypeOf(callExpr.Fun)
81 return nil, 0, errors.Errorf("cannot get type for Fun %[1]T (%[1]v)", callExpr.Fun)
84 sig, _ := sigType.Underlying().(*types.Signature)
86 return nil, 0, errors.Errorf("cannot find signature for Fun %[1]T (%[1]v)", callExpr.Fun)
89 activeParam := activeParameter(callExpr, sig.Params().Len(), sig.Variadic(), rng.Start)
93 comment *ast.CommentGroup
96 node, err := objToDecl(ctx, snapshot, pkg, obj)
100 rng, err := objToMappedRange(snapshot, pkg, obj)
108 decl.MappedRange = append(decl.MappedRange, rng)
109 d, err := HoverInfo(ctx, pkg, decl.obj, decl.node)
118 s := NewSignature(ctx, snapshot, pkg, sig, comment, qf)
119 paramInfo := make([]protocol.ParameterInformation, 0, len(s.params))
120 for _, p := range s.params {
121 paramInfo = append(paramInfo, protocol.ParameterInformation{Label: p})
123 return &protocol.SignatureInformation{
124 Label: name + s.Format(),
125 Documentation: s.doc,
126 Parameters: paramInfo,
130 func builtinSignature(ctx context.Context, snapshot Snapshot, callExpr *ast.CallExpr, name string, pos token.Pos) (*protocol.SignatureInformation, int, error) {
131 sig, err := NewBuiltinSignature(ctx, snapshot, name)
135 paramInfo := make([]protocol.ParameterInformation, 0, len(sig.params))
136 for _, p := range sig.params {
137 paramInfo = append(paramInfo, protocol.ParameterInformation{Label: p})
139 activeParam := activeParameter(callExpr, len(sig.params), sig.variadic, pos)
140 return &protocol.SignatureInformation{
141 Label: sig.name + sig.Format(),
142 Documentation: sig.doc,
143 Parameters: paramInfo,
148 func activeParameter(callExpr *ast.CallExpr, numParams int, variadic bool, pos token.Pos) (activeParam int) {
149 if len(callExpr.Args) == 0 {
152 // First, check if the position is even in the range of the arguments.
153 start, end := callExpr.Lparen, callExpr.Rparen
154 if !(start <= pos && pos <= end) {
157 for _, expr := range callExpr.Args {
158 if start == token.NoPos {
162 if start <= pos && pos <= end {
165 // Don't advance the active parameter for the last parameter of a variadic function.
166 if !variadic || activeParam < numParams-1 {
169 start = expr.Pos() + 1 // to account for commas