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.
14 "golang.org/x/tools/go/ast/astutil"
15 "golang.org/x/tools/internal/event"
16 "golang.org/x/tools/internal/lsp/protocol"
17 errors "golang.org/x/xerrors"
20 func SignatureHelp(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) (*protocol.SignatureInformation, int, error) {
21 ctx, done := event.Start(ctx, "source.SignatureHelp")
24 pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
26 return nil, 0, errors.Errorf("getting file for SignatureHelp: %w", err)
28 spn, err := pgf.Mapper.PointSpan(pos)
32 rng, err := spn.Range(pgf.Mapper.Converter)
36 // Find a call expression surrounding the query position.
37 var callExpr *ast.CallExpr
38 path, _ := astutil.PathEnclosingInterval(pgf.File, rng.Start, rng.Start)
40 return nil, 0, errors.Errorf("cannot find node enclosing position")
43 for _, node := range path {
44 switch node := node.(type) {
46 if rng.Start >= node.Lparen && rng.Start <= node.Rparen {
50 case *ast.FuncLit, *ast.FuncType:
51 // The user is within an anonymous function,
52 // which may be the parameter to the *ast.CallExpr.
53 // Don't show signature help in this case.
54 return nil, 0, errors.Errorf("no signature help within a function declaration")
57 if callExpr == nil || callExpr.Fun == nil {
58 return nil, 0, errors.Errorf("cannot find an enclosing function")
61 qf := Qualifier(pgf.File, pkg.GetTypes(), pkg.GetTypesInfo())
63 // Get the object representing the function, if available.
64 // There is no object in certain cases such as calling a function returned by
65 // a function (e.g. "foo()()").
67 switch t := callExpr.Fun.(type) {
69 obj = pkg.GetTypesInfo().ObjectOf(t)
70 case *ast.SelectorExpr:
71 obj = pkg.GetTypesInfo().ObjectOf(t.Sel)
74 // Handle builtin functions separately.
75 if obj, ok := obj.(*types.Builtin); ok {
76 return builtinSignature(ctx, snapshot, callExpr, obj.Name(), rng.Start)
79 // Get the type information for the function being called.
80 sigType := pkg.GetTypesInfo().TypeOf(callExpr.Fun)
82 return nil, 0, errors.Errorf("cannot get type for Fun %[1]T (%[1]v)", callExpr.Fun)
85 sig, _ := sigType.Underlying().(*types.Signature)
87 return nil, 0, errors.Errorf("cannot find signature for Fun %[1]T (%[1]v)", callExpr.Fun)
90 activeParam := activeParameter(callExpr, sig.Params().Len(), sig.Variadic(), rng.Start)
94 comment *ast.CommentGroup
97 node, err := objToDecl(ctx, snapshot, pkg, obj)
101 rng, err := objToMappedRange(snapshot, pkg, obj)
109 decl.MappedRange = append(decl.MappedRange, rng)
110 d, err := HoverInfo(ctx, pkg, decl.obj, decl.node)
119 s := NewSignature(ctx, snapshot, pkg, sig, comment, qf)
120 paramInfo := make([]protocol.ParameterInformation, 0, len(s.params))
121 for _, p := range s.params {
122 paramInfo = append(paramInfo, protocol.ParameterInformation{Label: p})
124 return &protocol.SignatureInformation{
125 Label: name + s.Format(),
126 Documentation: doc.Synopsis(s.doc),
127 Parameters: paramInfo,
131 func builtinSignature(ctx context.Context, snapshot Snapshot, callExpr *ast.CallExpr, name string, pos token.Pos) (*protocol.SignatureInformation, int, error) {
132 sig, err := NewBuiltinSignature(ctx, snapshot, name)
136 paramInfo := make([]protocol.ParameterInformation, 0, len(sig.params))
137 for _, p := range sig.params {
138 paramInfo = append(paramInfo, protocol.ParameterInformation{Label: p})
140 activeParam := activeParameter(callExpr, len(sig.params), sig.variadic, pos)
141 return &protocol.SignatureInformation{
142 Label: sig.name + sig.Format(),
143 Documentation: doc.Synopsis(sig.doc),
144 Parameters: paramInfo,
149 func activeParameter(callExpr *ast.CallExpr, numParams int, variadic bool, pos token.Pos) (activeParam int) {
150 if len(callExpr.Args) == 0 {
153 // First, check if the position is even in the range of the arguments.
154 start, end := callExpr.Lparen, callExpr.Rparen
155 if !(start <= pos && pos <= end) {
158 for _, expr := range callExpr.Args {
159 if start == token.NoPos {
163 if start <= pos && pos <= end {
166 // Don't advance the active parameter for the last parameter of a variadic function.
167 if !variadic || activeParam < numParams-1 {
170 start = expr.Pos() + 1 // to account for commas