mirror of
https://github.com/golang/go
synced 2024-11-19 08:14:40 -07:00
69a2705782
we don't really use them, only generate them in cases where the failure is way more fundamental, and then also fail to remember them for the next call to the same accessor. Better to not have them. Change-Id: I0e8abeda688f5cc2a932ed95a80d89225c399f93 Reviewed-on: https://go-review.googlesource.com/c/162399 Reviewed-by: Rebecca Stambler <rstambler@golang.org>
113 lines
2.9 KiB
Go
113 lines
2.9 KiB
Go
// 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"
|
|
"fmt"
|
|
"go/ast"
|
|
"go/token"
|
|
"go/types"
|
|
|
|
"golang.org/x/tools/go/ast/astutil"
|
|
)
|
|
|
|
type SignatureInformation struct {
|
|
Label string
|
|
Parameters []ParameterInformation
|
|
ActiveParameter int
|
|
}
|
|
|
|
type ParameterInformation struct {
|
|
Label string
|
|
}
|
|
|
|
func SignatureHelp(ctx context.Context, f File, pos token.Pos) (*SignatureInformation, error) {
|
|
fAST := f.GetAST()
|
|
pkg := f.GetPackage()
|
|
|
|
// Find a call expression surrounding the query position.
|
|
var callExpr *ast.CallExpr
|
|
path, _ := astutil.PathEnclosingInterval(fAST, pos, pos)
|
|
if path == nil {
|
|
return nil, fmt.Errorf("cannot find node enclosing position")
|
|
}
|
|
for _, node := range path {
|
|
if c, ok := node.(*ast.CallExpr); ok {
|
|
callExpr = c
|
|
break
|
|
}
|
|
}
|
|
if callExpr == nil || callExpr.Fun == nil {
|
|
return nil, fmt.Errorf("cannot find an enclosing function")
|
|
}
|
|
|
|
// Get the type information for the function corresponding to the call expression.
|
|
var obj types.Object
|
|
switch t := callExpr.Fun.(type) {
|
|
case *ast.Ident:
|
|
obj = pkg.TypesInfo.ObjectOf(t)
|
|
case *ast.SelectorExpr:
|
|
obj = pkg.TypesInfo.ObjectOf(t.Sel)
|
|
default:
|
|
return nil, fmt.Errorf("the enclosing function is malformed")
|
|
}
|
|
if obj == nil {
|
|
return nil, fmt.Errorf("cannot resolve %s", callExpr.Fun)
|
|
}
|
|
// Find the signature corresponding to the object.
|
|
var sig *types.Signature
|
|
switch obj.(type) {
|
|
case *types.Var:
|
|
if underlying, ok := obj.Type().Underlying().(*types.Signature); ok {
|
|
sig = underlying
|
|
}
|
|
case *types.Func:
|
|
sig = obj.Type().(*types.Signature)
|
|
}
|
|
if sig == nil {
|
|
return nil, fmt.Errorf("no function signatures found for %s", obj.Name())
|
|
}
|
|
pkgStringer := qualifier(fAST, pkg.Types, pkg.TypesInfo)
|
|
var paramInfo []ParameterInformation
|
|
for i := 0; i < sig.Params().Len(); i++ {
|
|
param := sig.Params().At(i)
|
|
label := types.TypeString(param.Type(), pkgStringer)
|
|
if param.Name() != "" {
|
|
label = fmt.Sprintf("%s %s", param.Name(), label)
|
|
}
|
|
paramInfo = append(paramInfo, ParameterInformation{
|
|
Label: label,
|
|
})
|
|
}
|
|
// Determine the query position relative to the number of parameters in the function.
|
|
var activeParam int
|
|
var start, end token.Pos
|
|
for i, expr := range callExpr.Args {
|
|
if start == token.NoPos {
|
|
start = expr.Pos()
|
|
}
|
|
end = expr.End()
|
|
if i < len(callExpr.Args)-1 {
|
|
end = callExpr.Args[i+1].Pos() - 1 // comma
|
|
}
|
|
if start <= pos && pos <= end {
|
|
break
|
|
}
|
|
activeParam++
|
|
start = expr.Pos() + 1 // to account for commas
|
|
}
|
|
// Label for function, qualified by package name.
|
|
label := obj.Name()
|
|
if pkg := pkgStringer(obj.Pkg()); pkg != "" {
|
|
label = pkg + "." + label
|
|
}
|
|
return &SignatureInformation{
|
|
Label: label + formatParams(sig.Params(), sig.Variadic(), pkgStringer),
|
|
Parameters: paramInfo,
|
|
ActiveParameter: activeParam,
|
|
}, nil
|
|
}
|