mirror of https://github.com/golang/go.git
internal/lsp/source: adjust object position when formatting full AST
Thread through an adjusted object position when we format an re-parsed AST that wasn't type-checked to produce the object. Also remove some unnecessary handling of the object type. Fixes golang/go#46158 Change-Id: Ic8bee2bb4cb992d577d085599213fca1deab3b4e Reviewed-on: https://go-review.googlesource.com/c/tools/+/384697 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> Reviewed-by: Heschi Kreinick <heschi@google.com> gopls-CI: kokoro <noreply+kokoro@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
cda4201ef0
commit
fd59bdfe0d
|
|
@ -24,6 +24,7 @@ go 1.12
|
|||
package structs
|
||||
|
||||
type Mixed struct {
|
||||
// Exported comment
|
||||
Exported int
|
||||
unexported string
|
||||
}
|
||||
|
|
@ -40,7 +41,7 @@ go 1.12
|
|||
|
||||
require golang.org/x/structs v1.0.0
|
||||
-- go.sum --
|
||||
golang.org/x/structs v1.0.0 h1:3DlrFfd3OsEen7FnCHfqtnJvjBZ8ZFKmrD/+HjpdJj0=
|
||||
golang.org/x/structs v1.0.0 h1:Ito/a7hBYZaNKShFrZKjfBA/SIPvmBrcPCBWPx5QeKk=
|
||||
golang.org/x/structs v1.0.0/go.mod h1:47gkSIdo5AaQaWJS0upVORsxfEr1LL1MWv9dmYF3iq4=
|
||||
-- main.go --
|
||||
package main
|
||||
|
|
@ -48,9 +49,11 @@ package main
|
|||
import "golang.org/x/structs"
|
||||
|
||||
func main() {
|
||||
var _ structs.Mixed
|
||||
var m structs.Mixed
|
||||
_ = m.Exported
|
||||
}
|
||||
`
|
||||
|
||||
// TODO: use a nested workspace folder here.
|
||||
WithOptions(
|
||||
ProxyFiles(proxy),
|
||||
|
|
@ -61,12 +64,19 @@ func main() {
|
|||
if !strings.Contains(got.Value, "unexported") {
|
||||
t.Errorf("Workspace hover: missing expected field 'unexported'. Got:\n%q", got.Value)
|
||||
}
|
||||
|
||||
cacheFile, _ := env.GoToDefinition("main.go", mixedPos)
|
||||
argPos := env.RegexpSearch(cacheFile, "printMixed.*(Mixed)")
|
||||
got, _ = env.Hover(cacheFile, argPos)
|
||||
if !strings.Contains(got.Value, "unexported") {
|
||||
t.Errorf("Non-workspace hover: missing expected field 'unexported'. Got:\n%q", got.Value)
|
||||
}
|
||||
|
||||
exportedFieldPos := env.RegexpSearch("main.go", "Exported")
|
||||
got, _ = env.Hover("main.go", exportedFieldPos)
|
||||
if !strings.Contains(got.Value, "comment") {
|
||||
t.Errorf("Workspace hover: missing comment for field 'Exported'. Got:\n%q", got.Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -439,9 +439,10 @@ func objectString(obj types.Object, qf types.Qualifier, inferred *types.Signatur
|
|||
return str
|
||||
}
|
||||
|
||||
// HoverInfo returns a HoverInformation struct for an ast node and its type
|
||||
// object. node should be the actual node used in type checking, while fullNode
|
||||
// could be a separate node with more complete syntactic information.
|
||||
// HoverInfo returns a HoverInformation struct for an ast node and its
|
||||
// declaration object. node should be the actual node used in type checking,
|
||||
// while fullNode could be a separate node with more complete syntactic
|
||||
// information.
|
||||
func HoverInfo(ctx context.Context, s Snapshot, pkg Package, obj types.Object, pkgNode ast.Node, fullDecl ast.Decl) (*HoverInformation, error) {
|
||||
var info *HoverInformation
|
||||
|
||||
|
|
@ -497,15 +498,23 @@ func HoverInfo(ctx context.Context, s Snapshot, pkg Package, obj types.Object, p
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tok2 := s.FileSet().File(node.Pos())
|
||||
|
||||
// fullTok and fullPos are the *token.File and object position in for the
|
||||
// full AST.
|
||||
fullTok := s.FileSet().File(node.Pos())
|
||||
fullPos, err := Pos(fullTok, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var spec ast.Spec
|
||||
for _, s := range node.Specs {
|
||||
// Avoid panics by guarding the calls to token.Offset (golang/go#48249).
|
||||
start, err := Offset(tok2, s.Pos())
|
||||
start, err := Offset(fullTok, s.Pos())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
end, err := Offset(tok2, s.End())
|
||||
end, err := Offset(fullTok, s.End())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -514,7 +523,8 @@ func HoverInfo(ctx context.Context, s Snapshot, pkg Package, obj types.Object, p
|
|||
break
|
||||
}
|
||||
}
|
||||
info, err = formatGenDecl(node, spec, obj, obj.Type())
|
||||
|
||||
info, err = formatGenDecl(node, spec, fullPos, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -584,21 +594,19 @@ func isFunctionParam(obj types.Object, node *ast.FuncDecl) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func formatGenDecl(node *ast.GenDecl, spec ast.Spec, obj types.Object, typ types.Type) (*HoverInformation, error) {
|
||||
if _, ok := typ.(*types.Named); ok {
|
||||
switch typ.Underlying().(type) {
|
||||
case *types.Interface, *types.Struct:
|
||||
return formatGenDecl(node, spec, obj, typ.Underlying())
|
||||
}
|
||||
}
|
||||
// formatGenDecl returns hover information an object declared via spec inside
|
||||
// of the GenDecl node. obj is the type-checked object corresponding to the
|
||||
// declaration, but may have been type-checked using a different AST than the
|
||||
// given nodes; fullPos is the position of obj in node's AST.
|
||||
func formatGenDecl(node *ast.GenDecl, spec ast.Spec, fullPos token.Pos, obj types.Object) (*HoverInformation, error) {
|
||||
if spec == nil {
|
||||
return nil, errors.Errorf("no spec for node %v at position %v", node, obj.Pos())
|
||||
return nil, errors.Errorf("no spec for node %v at position %v", node, fullPos)
|
||||
}
|
||||
|
||||
// If we have a field or method.
|
||||
switch obj.(type) {
|
||||
case *types.Var, *types.Const, *types.Func:
|
||||
return formatVar(spec, obj, node), nil
|
||||
return formatVar(spec, fullPos, obj, node), nil
|
||||
}
|
||||
// Handle types.
|
||||
switch spec := spec.(type) {
|
||||
|
|
@ -628,7 +636,7 @@ func formatTypeSpec(spec *ast.TypeSpec, decl *ast.GenDecl) *HoverInformation {
|
|||
}
|
||||
}
|
||||
|
||||
func formatVar(node ast.Spec, obj types.Object, decl *ast.GenDecl) *HoverInformation {
|
||||
func formatVar(node ast.Spec, fullPos token.Pos, obj types.Object, decl *ast.GenDecl) *HoverInformation {
|
||||
var fieldList *ast.FieldList
|
||||
switch spec := node.(type) {
|
||||
case *ast.TypeSpec:
|
||||
|
|
@ -664,7 +672,7 @@ func formatVar(node ast.Spec, obj types.Object, decl *ast.GenDecl) *HoverInforma
|
|||
}
|
||||
|
||||
if fieldList != nil {
|
||||
comment := findFieldComment(obj.Pos(), fieldList)
|
||||
comment := findFieldComment(fullPos, fieldList)
|
||||
return &HoverInformation{source: obj, comment: comment}
|
||||
}
|
||||
return &HoverInformation{source: obj, comment: decl.Doc}
|
||||
|
|
|
|||
|
|
@ -540,7 +540,7 @@ func IsCommandLineArguments(s string) bool {
|
|||
return strings.Contains(s, "command-line-arguments")
|
||||
}
|
||||
|
||||
// Offset returns tok.Offset(pos), but it also checks that the pos is in range
|
||||
// Offset returns tok.Offset(pos), but first checks that the pos is in range
|
||||
// for the given file.
|
||||
func Offset(tok *token.File, pos token.Pos) (int, error) {
|
||||
if !InRange(tok, pos) {
|
||||
|
|
@ -549,6 +549,15 @@ func Offset(tok *token.File, pos token.Pos) (int, error) {
|
|||
return tok.Offset(pos), nil
|
||||
}
|
||||
|
||||
// Pos returns tok.Pos(offset), but first checks that the offset is valid for
|
||||
// the given file.
|
||||
func Pos(tok *token.File, offset int) (token.Pos, error) {
|
||||
if offset < 0 || offset > tok.Size() {
|
||||
return token.NoPos, fmt.Errorf("offset %v is not in range for file of size %v", offset, tok.Size())
|
||||
}
|
||||
return tok.Pos(offset), nil
|
||||
}
|
||||
|
||||
// InRange reports whether the given position is in the given token.File.
|
||||
func InRange(tok *token.File, pos token.Pos) bool {
|
||||
size := tok.Pos(tok.Size())
|
||||
|
|
|
|||
Loading…
Reference in New Issue