--- /dev/null
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package source
+
+import (
+ "context"
+ "go/ast"
+ "go/doc"
+ "go/token"
+ "go/types"
+
+ "golang.org/x/tools/go/ast/astutil"
+ "golang.org/x/tools/internal/event"
+ "golang.org/x/tools/internal/lsp/protocol"
+ errors "golang.org/x/xerrors"
+)
+
+func SignatureHelp(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) (*protocol.SignatureInformation, int, error) {
+ ctx, done := event.Start(ctx, "source.SignatureHelp")
+ defer done()
+
+ pkg, pgf, err := GetParsedFile(ctx, snapshot, fh, NarrowestPackage)
+ if err != nil {
+ return nil, 0, errors.Errorf("getting file for SignatureHelp: %w", err)
+ }
+ spn, err := pgf.Mapper.PointSpan(pos)
+ if err != nil {
+ return nil, 0, err
+ }
+ rng, err := spn.Range(pgf.Mapper.Converter)
+ if err != nil {
+ return nil, 0, err
+ }
+ // Find a call expression surrounding the query position.
+ var callExpr *ast.CallExpr
+ path, _ := astutil.PathEnclosingInterval(pgf.File, rng.Start, rng.Start)
+ if path == nil {
+ return nil, 0, errors.Errorf("cannot find node enclosing position")
+ }
+FindCall:
+ for _, node := range path {
+ switch node := node.(type) {
+ case *ast.CallExpr:
+ if rng.Start >= node.Lparen && rng.Start <= node.Rparen {
+ callExpr = node
+ break FindCall
+ }
+ case *ast.FuncLit, *ast.FuncType:
+ // The user is within an anonymous function,
+ // which may be the parameter to the *ast.CallExpr.
+ // Don't show signature help in this case.
+ return nil, 0, errors.Errorf("no signature help within a function declaration")
+ }
+ }
+ if callExpr == nil || callExpr.Fun == nil {
+ return nil, 0, errors.Errorf("cannot find an enclosing function")
+ }
+
+ qf := Qualifier(pgf.File, pkg.GetTypes(), pkg.GetTypesInfo())
+
+ // Get the object representing the function, if available.
+ // There is no object in certain cases such as calling a function returned by
+ // a function (e.g. "foo()()").
+ var obj types.Object
+ switch t := callExpr.Fun.(type) {
+ case *ast.Ident:
+ obj = pkg.GetTypesInfo().ObjectOf(t)
+ case *ast.SelectorExpr:
+ obj = pkg.GetTypesInfo().ObjectOf(t.Sel)
+ }
+
+ // Handle builtin functions separately.
+ if obj, ok := obj.(*types.Builtin); ok {
+ return builtinSignature(ctx, snapshot, callExpr, obj.Name(), rng.Start)
+ }
+
+ // Get the type information for the function being called.
+ sigType := pkg.GetTypesInfo().TypeOf(callExpr.Fun)
+ if sigType == nil {
+ return nil, 0, errors.Errorf("cannot get type for Fun %[1]T (%[1]v)", callExpr.Fun)
+ }
+
+ sig, _ := sigType.Underlying().(*types.Signature)
+ if sig == nil {
+ return nil, 0, errors.Errorf("cannot find signature for Fun %[1]T (%[1]v)", callExpr.Fun)
+ }
+
+ activeParam := activeParameter(callExpr, sig.Params().Len(), sig.Variadic(), rng.Start)
+
+ var (
+ name string
+ comment *ast.CommentGroup
+ )
+ if obj != nil {
+ node, err := objToDecl(ctx, snapshot, pkg, obj)
+ if err != nil {
+ return nil, 0, err
+ }
+ rng, err := objToMappedRange(snapshot, pkg, obj)
+ if err != nil {
+ return nil, 0, err
+ }
+ decl := Declaration{
+ obj: obj,
+ node: node,
+ }
+ decl.MappedRange = append(decl.MappedRange, rng)
+ d, err := HoverInfo(ctx, pkg, decl.obj, decl.node)
+ if err != nil {
+ return nil, 0, err
+ }
+ name = obj.Name()
+ comment = d.comment
+ } else {
+ name = "func"
+ }
+ s := NewSignature(ctx, snapshot, pkg, sig, comment, qf)
+ paramInfo := make([]protocol.ParameterInformation, 0, len(s.params))
+ for _, p := range s.params {
+ paramInfo = append(paramInfo, protocol.ParameterInformation{Label: p})
+ }
+ return &protocol.SignatureInformation{
+ Label: name + s.Format(),
+ Documentation: doc.Synopsis(s.doc),
+ Parameters: paramInfo,
+ }, activeParam, nil
+}
+
+func builtinSignature(ctx context.Context, snapshot Snapshot, callExpr *ast.CallExpr, name string, pos token.Pos) (*protocol.SignatureInformation, int, error) {
+ sig, err := NewBuiltinSignature(ctx, snapshot, name)
+ if err != nil {
+ return nil, 0, err
+ }
+ paramInfo := make([]protocol.ParameterInformation, 0, len(sig.params))
+ for _, p := range sig.params {
+ paramInfo = append(paramInfo, protocol.ParameterInformation{Label: p})
+ }
+ activeParam := activeParameter(callExpr, len(sig.params), sig.variadic, pos)
+ return &protocol.SignatureInformation{
+ Label: sig.name + sig.Format(),
+ Documentation: doc.Synopsis(sig.doc),
+ Parameters: paramInfo,
+ }, activeParam, nil
+
+}
+
+func activeParameter(callExpr *ast.CallExpr, numParams int, variadic bool, pos token.Pos) (activeParam int) {
+ if len(callExpr.Args) == 0 {
+ return 0
+ }
+ // First, check if the position is even in the range of the arguments.
+ start, end := callExpr.Lparen, callExpr.Rparen
+ if !(start <= pos && pos <= end) {
+ return 0
+ }
+ for _, expr := range callExpr.Args {
+ if start == token.NoPos {
+ start = expr.Pos()
+ }
+ end = expr.End()
+ if start <= pos && pos <= end {
+ break
+ }
+ // Don't advance the active parameter for the last parameter of a variadic function.
+ if !variadic || activeParam < numParams-1 {
+ activeParam++
+ }
+ start = expr.Pos() + 1 // to account for commas
+ }
+ return activeParam
+}