internal/lsp: use protocol.Ranges for source.Identifier

Change-Id: I42cb957e3c1676e2ec7e3f50dd5e3613f3dd9555
Reviewed-on: https://go-review.googlesource.com/c/tools/+/191880
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Rebecca Stambler 2019-08-27 00:26:45 -04:00
parent aed303cbaa
commit 42f498d34c
18 changed files with 339 additions and 268 deletions

1
go.sum
View File

@ -3,6 +3,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=

View File

@ -162,6 +162,10 @@ func (f *goFile) GetCachedPackage(ctx context.Context) (source.Package, error) {
} }
f.mu.Unlock() f.mu.Unlock()
if len(cphs) == 0 {
return nil, errors.Errorf("no CheckPackageHandles for %s", f.URI())
}
cph, err := bestCheckPackageHandle(f.URI(), cphs) cph, err := bestCheckPackageHandle(f.URI(), cphs)
if err != nil { if err != nil {
return nil, err return nil, err
@ -202,6 +206,16 @@ func (f *goFile) wrongParseMode(ctx context.Context, mode source.ParseMode) bool
return true return true
} }
func (f *goFile) Builtin() (*ast.File, bool) {
builtinPkg := f.View().BuiltinPackage()
for filename, file := range builtinPkg.Files {
if filename == f.URI().Filename() {
return file, true
}
}
return nil, false
}
// isDirty is true if the file needs to be type-checked. // isDirty is true if the file needs to be type-checked.
// It assumes that the file's view's mutex is held by the caller. // It assumes that the file's view's mutex is held by the caller.
func (f *goFile) isDirty(ctx context.Context) bool { func (f *goFile) isDirty(ctx context.Context) bool {

View File

@ -19,39 +19,20 @@ func (s *Server) definition(ctx context.Context, params *protocol.TextDocumentPo
if err != nil { if err != nil {
return nil, err return nil, err
} }
m, err := getMapper(ctx, f) ident, err := source.Identifier(ctx, view, f, params.Position)
if err != nil { if err != nil {
return nil, err return nil, err
} }
spn, err := m.PointSpan(params.Position) decRange, err := ident.Declaration.Range()
if err != nil { if err != nil {
return nil, err return nil, err
} }
rng, err := spn.Range(m.Converter) return []protocol.Location{
if err != nil { {
return nil, err URI: protocol.NewURI(ident.Declaration.URI()),
} Range: decRange,
ident, err := source.Identifier(ctx, f, rng.Start) },
if err != nil { }, nil
return nil, err
}
decSpan, err := ident.DeclarationRange().Span()
if err != nil {
return nil, err
}
decFile, err := getGoFile(ctx, view, decSpan.URI())
if err != nil {
return nil, err
}
decM, err := getMapper(ctx, decFile)
if err != nil {
return nil, err
}
loc, err := decM.Location(decSpan)
if err != nil {
return nil, err
}
return []protocol.Location{loc}, nil
} }
func (s *Server) typeDefinition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) { func (s *Server) typeDefinition(ctx context.Context, params *protocol.TextDocumentPositionParams) ([]protocol.Location, error) {
@ -61,37 +42,18 @@ func (s *Server) typeDefinition(ctx context.Context, params *protocol.TextDocume
if err != nil { if err != nil {
return nil, err return nil, err
} }
m, err := getMapper(ctx, f) ident, err := source.Identifier(ctx, view, f, params.Position)
if err != nil { if err != nil {
return nil, err return nil, err
} }
spn, err := m.PointSpan(params.Position) identRange, err := ident.Type.Range()
if err != nil { if err != nil {
return nil, err return nil, err
} }
rng, err := spn.Range(m.Converter) return []protocol.Location{
if err != nil { {
return nil, err URI: protocol.NewURI(ident.Type.URI()),
} Range: identRange,
ident, err := source.Identifier(ctx, f, rng.Start) },
if err != nil { }, nil
return nil, err
}
identSpan, err := ident.Type.Range.Span()
if err != nil {
return nil, err
}
identFile, err := getGoFile(ctx, view, identSpan.URI())
if err != nil {
return nil, err
}
identM, err := getMapper(ctx, identFile)
if err != nil {
return nil, err
}
loc, err := identM.Location(identSpan)
if err != nil {
return nil, err
}
return []protocol.Location{loc}, nil
} }

View File

@ -38,19 +38,7 @@ func (s *Server) hover(ctx context.Context, params *protocol.TextDocumentPositio
if err != nil { if err != nil {
return nil, err return nil, err
} }
m, err := getMapper(ctx, f) ident, err := source.Identifier(ctx, view, f, params.Position)
if err != nil {
return nil, err
}
spn, err := m.PointSpan(params.Position)
if err != nil {
return nil, err
}
identRange, err := spn.Range(m.Converter)
if err != nil {
return nil, err
}
ident, err := source.Identifier(ctx, f, identRange.Start)
if err != nil { if err != nil {
return nil, nil return nil, nil
} }
@ -58,11 +46,7 @@ func (s *Server) hover(ctx context.Context, params *protocol.TextDocumentPositio
if err != nil { if err != nil {
return nil, err return nil, err
} }
identSpan, err := ident.Range.Span() rng, err := ident.Range()
if err != nil {
return nil, err
}
rng, err := m.Range(identSpan)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -361,7 +361,7 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) {
} else { } else {
locs, err = r.server.Definition(r.ctx, params) locs, err = r.server.Definition(r.ctx, params)
if err != nil { if err != nil {
t.Fatalf("failed for %v: %v", d.Src, err) t.Fatalf("failed for %v: %+v", d.Src, err)
} }
hover, err = r.server.Hover(r.ctx, params) hover, err = r.server.Hover(r.ctx, params)
} }

View File

@ -21,44 +21,21 @@ func (s *Server) references(ctx context.Context, params *protocol.ReferenceParam
if err != nil { if err != nil {
return nil, err return nil, err
} }
m, err := getMapper(ctx, f)
if err != nil {
return nil, err
}
spn, err := m.PointSpan(params.Position)
if err != nil {
return nil, err
}
rng, err := spn.Range(m.Converter)
if err != nil {
return nil, err
}
// Find all references to the identifier at the position. // Find all references to the identifier at the position.
ident, err := source.Identifier(ctx, f, rng.Start) ident, err := source.Identifier(ctx, view, f, params.Position)
if err != nil { if err != nil {
return nil, err return nil, err
} }
references, err := ident.References(ctx) references, err := ident.References(ctx, view)
if err != nil { if err != nil {
log.Error(ctx, "no references", err, tag.Of("Identifier", ident.Name)) log.Error(ctx, "no references", err, tag.Of("Identifier", ident.Name))
} }
if params.Context.IncludeDeclaration {
// The declaration of this identifier may not be in the
// scope that we search for references, so make sure
// it is added to the beginning of the list if IncludeDeclaration
// was specified.
references = append([]*source.ReferenceInfo{
&source.ReferenceInfo{
Range: ident.DeclarationRange(),
},
}, references...)
}
// Get the location of each reference to return as the result. // Get the location of each reference to return as the result.
locations := make([]protocol.Location, 0, len(references)) locations := make([]protocol.Location, 0, len(references))
seen := make(map[span.Span]bool) seen := make(map[span.Span]bool)
for _, ref := range references { for _, ref := range references {
refSpan, err := ref.Range.Span() refSpan, err := ref.Span()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -66,20 +43,31 @@ func (s *Server) references(ctx context.Context, params *protocol.ReferenceParam
continue // already added this location continue // already added this location
} }
seen[refSpan] = true seen[refSpan] = true
refRange, err := ref.Range()
if err != nil {
return nil, err
}
locations = append(locations, protocol.Location{
URI: protocol.NewURI(ref.URI()),
Range: refRange,
})
}
// The declaration of this identifier may not be in the
// scope that we search for references, so make sure
// it is added to the beginning of the list if IncludeDeclaration
// was specified.
if params.Context.IncludeDeclaration {
rng, err := ident.Declaration.Range()
if err != nil {
return nil, err
}
locations = append([]protocol.Location{
{
URI: protocol.NewURI(ident.Declaration.URI()),
Range: rng,
},
}, locations...)
refFile, err := getGoFile(ctx, view, refSpan.URI())
if err != nil {
return nil, err
}
refM, err := getMapper(ctx, refFile)
if err != nil {
return nil, err
}
loc, err := refM.Location(refSpan)
if err != nil {
return nil, err
}
locations = append(locations, loc)
} }
return locations, nil return locations, nil
} }

View File

@ -19,23 +19,11 @@ func (s *Server) rename(ctx context.Context, params *protocol.RenameParams) (*pr
if err != nil { if err != nil {
return nil, err return nil, err
} }
m, err := getMapper(ctx, f) ident, err := source.Identifier(ctx, view, f, params.Position)
if err != nil { if err != nil {
return nil, err return nil, err
} }
spn, err := m.PointSpan(params.Position) edits, err := ident.Rename(ctx, view, params.NewName)
if err != nil {
return nil, err
}
rng, err := spn.Range(m.Converter)
if err != nil {
return nil, err
}
ident, err := source.Identifier(ctx, f, rng.Start)
if err != nil {
return nil, err
}
edits, err := ident.Rename(ctx, params.NewName)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -33,7 +33,7 @@ func (s *Server) signatureHelp(ctx context.Context, params *protocol.TextDocumen
if err != nil { if err != nil {
return nil, err return nil, err
} }
info, err := source.SignatureHelp(ctx, f, rng.Start) info, err := source.SignatureHelp(ctx, view, f, params.Position)
if err != nil { if err != nil {
log.Print(ctx, "no signature help", tag.Of("At", rng), tag.Of("Failure", err)) log.Print(ctx, "no signature help", tag.Of("At", rng), tag.Of("Failure", err))
return nil, nil return nil, nil

View File

@ -115,11 +115,11 @@ func (c *completer) item(cand candidate) (CompletionItem, error) {
} }
// TODO(rstambler): Log errors when this feature is enabled. // TODO(rstambler): Log errors when this feature is enabled.
if c.opts.WantDocumentaton { if c.opts.WantDocumentaton {
declRange, err := objToRange(c.ctx, c.view.Session().Cache().FileSet(), obj) declRange, err := objToRange(c.ctx, c.view, obj)
if err != nil { if err != nil {
goto Return goto Return
} }
pos := declRange.FileSet.Position(declRange.Start) pos := c.view.Session().Cache().FileSet().Position(declRange.spanRange.Start)
if !pos.IsValid() { if !pos.IsValid() {
goto Return goto Return
} }
@ -136,16 +136,20 @@ func (c *completer) item(cand candidate) (CompletionItem, error) {
if err != nil { if err != nil {
goto Return goto Return
} }
var file *ast.File var ph ParseGoHandle
for _, ph := range pkg.GetHandles() { for _, h := range pkg.GetHandles() {
if ph.File().Identity().URI == gof.URI() { if h.File().Identity().URI == gof.URI() {
file, _ = ph.Cached(c.ctx) ph = h
} }
} }
if ph == nil {
goto Return
}
file, _ := ph.Cached(c.ctx)
if file == nil { if file == nil {
goto Return goto Return
} }
ident, err := findIdentifier(c.ctx, gof, pkg, file, declRange.Start) ident, err := findIdentifier(c.ctx, c.view, gof, pkg, file, declRange.spanRange.Start)
if err != nil { if err != nil {
goto Return goto Return
} }

View File

@ -38,7 +38,7 @@ func (i *IdentifierInfo) Hover(ctx context.Context) (*HoverInformation, error) {
ctx, done := trace.StartSpan(ctx, "source.Hover") ctx, done := trace.StartSpan(ctx, "source.Hover")
defer done() defer done()
h, err := i.decl.hover(ctx) h, err := i.Declaration.hover(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -55,8 +55,8 @@ func (i *IdentifierInfo) Hover(ctx context.Context) (*HoverInformation, error) {
} }
// Set the documentation. // Set the documentation.
if i.decl.obj != nil { if i.Declaration.obj != nil {
h.SingleLine = types.ObjectString(i.decl.obj, i.qf) h.SingleLine = types.ObjectString(i.Declaration.obj, i.qf)
} }
if h.comment != nil { if h.comment != nil {
h.FullDocumentation = h.comment.Text() h.FullDocumentation = h.comment.Text()
@ -65,7 +65,7 @@ func (i *IdentifierInfo) Hover(ctx context.Context) (*HoverInformation, error) {
return h, nil return h, nil
} }
func (d declaration) hover(ctx context.Context) (*HoverInformation, error) { func (d Declaration) hover(ctx context.Context) (*HoverInformation, error) {
ctx, done := trace.StartSpan(ctx, "source.hover") ctx, done := trace.StartSpan(ctx, "source.hover")
defer done() defer done()
obj := d.obj obj := d.obj

View File

@ -12,6 +12,7 @@ import (
"strconv" "strconv"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/span" "golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/telemetry/trace" "golang.org/x/tools/internal/telemetry/trace"
errors "golang.org/x/xerrors" errors "golang.org/x/xerrors"
@ -20,13 +21,15 @@ import (
// IdentifierInfo holds information about an identifier in Go source. // IdentifierInfo holds information about an identifier in Go source.
type IdentifierInfo struct { type IdentifierInfo struct {
Name string Name string
Range span.Range mappedRange
File GoFile File GoFile
Type struct { Type struct {
Range span.Range mappedRange
Object types.Object Object types.Object
} }
decl declaration
Declaration Declaration
pkg Package pkg Package
ident *ast.Ident ident *ast.Ident
@ -34,44 +37,39 @@ type IdentifierInfo struct {
qf types.Qualifier qf types.Qualifier
} }
type declaration struct { type Declaration struct {
rng span.Range mappedRange
node ast.Node node ast.Node
obj types.Object obj types.Object
wasImplicit bool wasImplicit bool
} }
func (i *IdentifierInfo) DeclarationRange() span.Range {
return i.decl.rng
}
// Identifier returns identifier information for a position // Identifier returns identifier information for a position
// in a file, accounting for a potentially incomplete selector. // in a file, accounting for a potentially incomplete selector.
func Identifier(ctx context.Context, f GoFile, pos token.Pos) (*IdentifierInfo, error) { func Identifier(ctx context.Context, view View, f GoFile, pos protocol.Position) (*IdentifierInfo, error) {
pkg, err := f.GetPackage(ctx) file, pkg, m, err := fileToMapper(ctx, view, f.URI())
if err != nil { if err != nil {
return nil, err return nil, err
} }
var file *ast.File spn, err := m.PointSpan(pos)
for _, ph := range pkg.GetHandles() { if err != nil {
if ph.File().Identity().URI == f.URI() {
file, err = ph.Cached(ctx)
}
}
if file == nil {
return nil, err return nil, err
} }
return findIdentifier(ctx, f, pkg, file, pos) rng, err := spn.Range(m.Converter)
if err != nil {
return nil, err
}
return findIdentifier(ctx, view, f, pkg, file, rng.Start)
} }
func findIdentifier(ctx context.Context, f GoFile, pkg Package, file *ast.File, pos token.Pos) (*IdentifierInfo, error) { func findIdentifier(ctx context.Context, view View, f GoFile, pkg Package, file *ast.File, pos token.Pos) (*IdentifierInfo, error) {
if result, err := identifier(ctx, f, pkg, file, pos); err != nil || result != nil { if result, err := identifier(ctx, view, f, pkg, file, pos); err != nil || result != nil {
return result, err return result, err
} }
// If the position is not an identifier but immediately follows // If the position is not an identifier but immediately follows
// an identifier or selector period (as is common when // an identifier or selector period (as is common when
// requesting a completion), use the path to the preceding node. // requesting a completion), use the path to the preceding node.
result, err := identifier(ctx, f, pkg, file, pos-1) result, err := identifier(ctx, view, f, pkg, file, pos-1)
if result == nil && err == nil { if result == nil && err == nil {
err = errors.Errorf("no identifier found for %s", f.FileSet().Position(pos)) err = errors.Errorf("no identifier found for %s", f.FileSet().Position(pos))
} }
@ -79,14 +77,14 @@ func findIdentifier(ctx context.Context, f GoFile, pkg Package, file *ast.File,
} }
// identifier checks a single position for a potential identifier. // identifier checks a single position for a potential identifier.
func identifier(ctx context.Context, f GoFile, pkg Package, file *ast.File, pos token.Pos) (*IdentifierInfo, error) { func identifier(ctx context.Context, view View, f GoFile, pkg Package, file *ast.File, pos token.Pos) (*IdentifierInfo, error) {
ctx, done := trace.StartSpan(ctx, "source.identifier") ctx, done := trace.StartSpan(ctx, "source.identifier")
defer done() defer done()
var err error var err error
// Handle import specs separately, as there is no formal position for a package declaration. // Handle import specs separately, as there is no formal position for a package declaration.
if result, err := importSpec(ctx, f, file, pkg, pos); result != nil || err != nil { if result, err := importSpec(ctx, view, f, file, pkg, pos); result != nil || err != nil {
return result, err return result, err
} }
path, _ := astutil.PathEnclosingInterval(file, pos, pos) path, _ := astutil.PathEnclosingInterval(file, pos, pos)
@ -115,9 +113,11 @@ func identifier(ctx context.Context, f GoFile, pkg Package, file *ast.File, pos
} }
} }
result.Name = result.ident.Name result.Name = result.ident.Name
result.Range = span.NewRange(f.FileSet(), result.ident.Pos(), result.ident.End()) if result.mappedRange, err = posToRange(ctx, view, result.ident.Pos(), result.ident.End()); err != nil {
result.decl.obj = pkg.GetTypesInfo().ObjectOf(result.ident) return nil, err
if result.decl.obj == nil { }
result.Declaration.obj = pkg.GetTypesInfo().ObjectOf(result.ident)
if result.Declaration.obj == nil {
// If there was no types.Object for the declaration, there might be an implicit local variable // If there was no types.Object for the declaration, there might be an implicit local variable
// declaration in a type switch. // declaration in a type switch.
if objs := typeSwitchVar(pkg.GetTypesInfo(), path); len(objs) > 0 { if objs := typeSwitchVar(pkg.GetTypesInfo(), path); len(objs) > 0 {
@ -125,8 +125,8 @@ func identifier(ctx context.Context, f GoFile, pkg Package, file *ast.File, pos
// but all of the types.Objects associated with the usages of this variable can be // but all of the types.Objects associated with the usages of this variable can be
// used to connect it back to the declaration. // used to connect it back to the declaration.
// Preserve the first of these objects and treat it as if it were the declaring object. // Preserve the first of these objects and treat it as if it were the declaring object.
result.decl.obj = objs[0] result.Declaration.obj = objs[0]
result.decl.wasImplicit = true result.Declaration.wasImplicit = true
} else { } else {
// Probably a type error. // Probably a type error.
return nil, errors.Errorf("no object for ident %v", result.Name) return nil, errors.Errorf("no object for ident %v", result.Name)
@ -134,13 +134,13 @@ func identifier(ctx context.Context, f GoFile, pkg Package, file *ast.File, pos
} }
// Handle builtins separately. // Handle builtins separately.
if result.decl.obj.Parent() == types.Universe { if result.Declaration.obj.Parent() == types.Universe {
decl, ok := lookupBuiltinDecl(f.View(), result.Name).(ast.Node) decl, ok := lookupBuiltinDecl(f.View(), result.Name).(ast.Node)
if !ok { if !ok {
return nil, errors.Errorf("no declaration for %s", result.Name) return nil, errors.Errorf("no declaration for %s", result.Name)
} }
result.decl.node = decl result.Declaration.node = decl
if result.decl.rng, err = posToRange(ctx, f.FileSet(), result.Name, decl.Pos()); err != nil { if result.Declaration.mappedRange, err = nameToRange(ctx, view, decl.Pos(), result.Name); err != nil {
return nil, err return nil, err
} }
return result, nil return result, nil
@ -149,26 +149,26 @@ func identifier(ctx context.Context, f GoFile, pkg Package, file *ast.File, pos
if result.wasEmbeddedField { if result.wasEmbeddedField {
// The original position was on the embedded field declaration, so we // The original position was on the embedded field declaration, so we
// try to dig out the type and jump to that instead. // try to dig out the type and jump to that instead.
if v, ok := result.decl.obj.(*types.Var); ok { if v, ok := result.Declaration.obj.(*types.Var); ok {
if typObj := typeToObject(v.Type()); typObj != nil { if typObj := typeToObject(v.Type()); typObj != nil {
result.decl.obj = typObj result.Declaration.obj = typObj
} }
} }
} }
for _, obj := range pkg.GetTypesInfo().Implicits { for _, obj := range pkg.GetTypesInfo().Implicits {
if obj.Pos() == result.decl.obj.Pos() { if obj.Pos() == result.Declaration.obj.Pos() {
// Mark this declaration as implicit, since it will not // Mark this declaration as implicit, since it will not
// appear in a (*types.Info).Defs map. // appear in a (*types.Info).Defs map.
result.decl.wasImplicit = true result.Declaration.wasImplicit = true
break break
} }
} }
if result.decl.rng, err = objToRange(ctx, f.FileSet(), result.decl.obj); err != nil { if result.Declaration.mappedRange, err = objToRange(ctx, view, result.Declaration.obj); err != nil {
return nil, err return nil, err
} }
if result.decl.node, err = objToNode(ctx, f.View(), pkg.GetTypes(), result.decl.obj, result.decl.rng); err != nil { if result.Declaration.node, err = objToNode(ctx, f.View(), pkg.GetTypes(), result.Declaration.obj, result.Declaration.mappedRange.spanRange); err != nil {
return nil, err return nil, err
} }
typ := pkg.GetTypesInfo().TypeOf(result.ident) typ := pkg.GetTypesInfo().TypeOf(result.ident)
@ -182,7 +182,7 @@ func identifier(ctx context.Context, f GoFile, pkg Package, file *ast.File, pos
if hasErrorType(result.Type.Object) { if hasErrorType(result.Type.Object) {
return result, nil return result, nil
} }
if result.Type.Range, err = objToRange(ctx, f.FileSet(), result.Type.Object); err != nil { if result.Type.mappedRange, err = objToRange(ctx, view, result.Type.Object); err != nil {
return nil, err return nil, err
} }
} }
@ -204,7 +204,7 @@ func hasErrorType(obj types.Object) bool {
return types.IsInterface(obj.Type()) && obj.Pkg() == nil && obj.Name() == "error" return types.IsInterface(obj.Type()) && obj.Pkg() == nil && obj.Name() == "error"
} }
func objToRange(ctx context.Context, fset *token.FileSet, obj types.Object) (span.Range, error) { func objToRange(ctx context.Context, view View, obj types.Object) (mappedRange, error) {
if pkgName, ok := obj.(*types.PkgName); ok { if pkgName, ok := obj.(*types.PkgName); ok {
// An imported Go package has a package-local, unqualified name. // An imported Go package has a package-local, unqualified name.
// When the name matches the imported package name, there is no // When the name matches the imported package name, there is no
@ -217,17 +217,32 @@ func objToRange(ctx context.Context, fset *token.FileSet, obj types.Object) (spa
// When the identifier does not appear in the source, have the range // When the identifier does not appear in the source, have the range
// of the object be the point at the beginning of the declaration. // of the object be the point at the beginning of the declaration.
if pkgName.Imported().Name() == pkgName.Name() { if pkgName.Imported().Name() == pkgName.Name() {
return posToRange(ctx, fset, "", obj.Pos()) return nameToRange(ctx, view, obj.Pos(), "")
} }
} }
return posToRange(ctx, fset, obj.Name(), obj.Pos()) return nameToRange(ctx, view, obj.Pos(), obj.Name())
} }
func posToRange(ctx context.Context, fset *token.FileSet, name string, pos token.Pos) (span.Range, error) { func nameToRange(ctx context.Context, view View, pos token.Pos, name string) (mappedRange, error) {
if !pos.IsValid() { return posToRange(ctx, view, pos, pos+token.Pos(len(name)))
return span.Range{}, errors.Errorf("invalid position for %v", name)
} }
return span.NewRange(fset, pos, pos+token.Pos(len(name))), nil
func posToRange(ctx context.Context, view View, pos, end token.Pos) (mappedRange, error) {
if !pos.IsValid() {
return mappedRange{}, errors.Errorf("invalid position for %v", pos)
}
if !end.IsValid() {
return mappedRange{}, errors.Errorf("invalid position for %v", end)
}
posn := view.Session().Cache().FileSet().Position(pos)
_, m, err := cachedFileToMapper(ctx, view, span.FileURI(posn.Filename))
if err != nil {
return mappedRange{}, err
}
return mappedRange{
m: m,
spanRange: span.NewRange(view.Session().Cache().FileSet(), pos, end),
}, nil
} }
func objToNode(ctx context.Context, view View, originPkg *types.Package, obj types.Object, rng span.Range) (ast.Decl, error) { func objToNode(ctx context.Context, view View, originPkg *types.Package, obj types.Object, rng span.Range) (ast.Decl, error) {
@ -279,7 +294,7 @@ func objToNode(ctx context.Context, view View, originPkg *types.Package, obj typ
} }
// importSpec handles positions inside of an *ast.ImportSpec. // importSpec handles positions inside of an *ast.ImportSpec.
func importSpec(ctx context.Context, f GoFile, fAST *ast.File, pkg Package, pos token.Pos) (*IdentifierInfo, error) { func importSpec(ctx context.Context, view View, f GoFile, fAST *ast.File, pkg Package, pos token.Pos) (*IdentifierInfo, error) {
var imp *ast.ImportSpec var imp *ast.ImportSpec
for _, spec := range fAST.Imports { for _, spec := range fAST.Imports {
if spec.Path.Pos() <= pos && pos < spec.Path.End() { if spec.Path.Pos() <= pos && pos < spec.Path.End() {
@ -296,9 +311,11 @@ func importSpec(ctx context.Context, f GoFile, fAST *ast.File, pkg Package, pos
result := &IdentifierInfo{ result := &IdentifierInfo{
File: f, File: f,
Name: importPath, Name: importPath,
Range: span.NewRange(f.FileSet(), imp.Pos(), imp.End()),
pkg: pkg, pkg: pkg,
} }
if result.mappedRange, err = posToRange(ctx, view, imp.Pos(), imp.End()); err != nil {
return nil, err
}
// Consider the "declaration" of an import spec to be the imported package. // Consider the "declaration" of an import spec to be the imported package.
importedPkg, err := pkg.GetImport(ctx, importPath) importedPkg, err := pkg.GetImport(ctx, importPath)
if err != nil { if err != nil {
@ -317,8 +334,10 @@ func importSpec(ctx context.Context, f GoFile, fAST *ast.File, pkg Package, pos
if dest == nil { if dest == nil {
return nil, errors.Errorf("package %q has no files", importPath) return nil, errors.Errorf("package %q has no files", importPath)
} }
result.decl.rng = span.NewRange(f.FileSet(), dest.Name.Pos(), dest.Name.End()) if result.Declaration.mappedRange, err = posToRange(ctx, view, dest.Pos(), dest.End()); err != nil {
result.decl.node = imp return nil, err
}
result.Declaration.node = imp
return result, nil return result, nil
} }

View File

@ -9,7 +9,6 @@ import (
"go/ast" "go/ast"
"go/types" "go/types"
"golang.org/x/tools/internal/span"
"golang.org/x/tools/internal/telemetry/trace" "golang.org/x/tools/internal/telemetry/trace"
errors "golang.org/x/xerrors" errors "golang.org/x/xerrors"
) )
@ -17,7 +16,7 @@ import (
// ReferenceInfo holds information about reference to an identifier in Go source. // ReferenceInfo holds information about reference to an identifier in Go source.
type ReferenceInfo struct { type ReferenceInfo struct {
Name string Name string
Range span.Range mappedRange
ident *ast.Ident ident *ast.Ident
obj types.Object obj types.Object
pkg Package pkg Package
@ -26,13 +25,13 @@ type ReferenceInfo struct {
// References returns a list of references for a given identifier within the packages // References returns a list of references for a given identifier within the packages
// containing i.File. Declarations appear first in the result. // containing i.File. Declarations appear first in the result.
func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, error) { func (i *IdentifierInfo) References(ctx context.Context, view View) ([]*ReferenceInfo, error) {
ctx, done := trace.StartSpan(ctx, "source.References") ctx, done := trace.StartSpan(ctx, "source.References")
defer done() defer done()
var references []*ReferenceInfo var references []*ReferenceInfo
// If the object declaration is nil, assume it is an import spec and do not look for references. // If the object declaration is nil, assume it is an import spec and do not look for references.
if i.decl.obj == nil { if i.Declaration.obj == nil {
return nil, errors.Errorf("no references for an import spec") return nil, errors.Errorf("no references for an import spec")
} }
@ -46,43 +45,49 @@ func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, erro
return nil, errors.Errorf("package %s has no types info", pkg.PkgPath()) return nil, errors.Errorf("package %s has no types info", pkg.PkgPath())
} }
if i.decl.wasImplicit { if i.Declaration.wasImplicit {
// The definition is implicit, so we must add it separately. // The definition is implicit, so we must add it separately.
// This occurs when the variable is declared in a type switch statement // This occurs when the variable is declared in a type switch statement
// or is an implicit package name. Both implicits are local to a file. // or is an implicit package name. Both implicits are local to a file.
references = append(references, &ReferenceInfo{ references = append(references, &ReferenceInfo{
Name: i.decl.obj.Name(), Name: i.Declaration.obj.Name(),
Range: i.decl.rng, mappedRange: i.Declaration.mappedRange,
obj: i.decl.obj, obj: i.Declaration.obj,
pkg: pkg, pkg: pkg,
isDeclaration: true, isDeclaration: true,
}) })
} }
for ident, obj := range info.Defs { for ident, obj := range info.Defs {
if obj == nil || !sameObj(obj, i.decl.obj) { if obj == nil || !sameObj(obj, i.Declaration.obj) {
continue continue
} }
// Add the declarations at the beginning of the references list. reference := &ReferenceInfo{
references = append([]*ReferenceInfo{&ReferenceInfo{
Name: ident.Name, Name: ident.Name,
Range: span.NewRange(i.File.FileSet(), ident.Pos(), ident.End()),
ident: ident, ident: ident,
obj: obj, obj: obj,
pkg: pkg, pkg: pkg,
isDeclaration: true, isDeclaration: true,
}}, references...) }
if reference.mappedRange, err = posToRange(ctx, view, ident.Pos(), ident.End()); err != nil {
return nil, err
}
// Add the declarations at the beginning of the references list.
references = append([]*ReferenceInfo{reference}, references...)
} }
for ident, obj := range info.Uses { for ident, obj := range info.Uses {
if obj == nil || !sameObj(obj, i.decl.obj) { if obj == nil || !sameObj(obj, i.Declaration.obj) {
continue continue
} }
references = append(references, &ReferenceInfo{ reference := &ReferenceInfo{
Name: ident.Name, Name: ident.Name,
Range: span.NewRange(i.File.FileSet(), ident.Pos(), ident.End()),
ident: ident, ident: ident,
pkg: pkg, pkg: pkg,
obj: obj, obj: obj,
}) }
if reference.mappedRange, err = posToRange(ctx, view, ident.Pos(), ident.End()); err != nil {
return nil, err
}
references = append(references, reference)
} }
} }

View File

@ -36,19 +36,19 @@ type renamer struct {
} }
// Rename returns a map of TextEdits for each file modified when renaming a given identifier within a package. // Rename returns a map of TextEdits for each file modified when renaming a given identifier within a package.
func (i *IdentifierInfo) Rename(ctx context.Context, newName string) (map[span.URI][]diff.TextEdit, error) { func (i *IdentifierInfo) Rename(ctx context.Context, view View, newName string) (map[span.URI][]diff.TextEdit, error) {
ctx, done := trace.StartSpan(ctx, "source.Rename") ctx, done := trace.StartSpan(ctx, "source.Rename")
defer done() defer done()
// If the object declaration is nil, assume it is an import spec. // If the object declaration is nil, assume it is an import spec.
if i.decl.obj == nil { if i.Declaration.obj == nil {
// Find the corresponding package name for this import spec // Find the corresponding package name for this import spec
// and rename that instead. // and rename that instead.
ident, err := i.getPkgName(ctx) ident, err := i.getPkgName(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return ident.Rename(ctx, newName) return ident.Rename(ctx, view, newName)
} }
if i.Name == newName { if i.Name == newName {
return nil, errors.Errorf("old and new names are the same: %s", newName) return nil, errors.Errorf("old and new names are the same: %s", newName)
@ -57,18 +57,18 @@ func (i *IdentifierInfo) Rename(ctx context.Context, newName string) (map[span.U
return nil, errors.Errorf("invalid identifier to rename: %q", i.Name) return nil, errors.Errorf("invalid identifier to rename: %q", i.Name)
} }
// Do not rename builtin identifiers. // Do not rename builtin identifiers.
if i.decl.obj.Parent() == types.Universe { if i.Declaration.obj.Parent() == types.Universe {
return nil, errors.Errorf("cannot rename builtin %q", i.Name) return nil, errors.Errorf("cannot rename builtin %q", i.Name)
} }
if i.pkg == nil || i.pkg.IsIllTyped() { if i.pkg == nil || i.pkg.IsIllTyped() {
return nil, errors.Errorf("package for %s is ill typed", i.File.URI()) return nil, errors.Errorf("package for %s is ill typed", i.File.URI())
} }
// Do not rename identifiers declared in another package. // Do not rename identifiers declared in another package.
if i.pkg.GetTypes() != i.decl.obj.Pkg() { if i.pkg.GetTypes() != i.Declaration.obj.Pkg() {
return nil, errors.Errorf("failed to rename because %q is declared in package %q", i.Name, i.decl.obj.Pkg().Name()) return nil, errors.Errorf("failed to rename because %q is declared in package %q", i.Name, i.Declaration.obj.Pkg().Name())
} }
refs, err := i.References(ctx) refs, err := i.References(ctx, view)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -112,8 +112,8 @@ func (i *IdentifierInfo) Rename(ctx context.Context, newName string) (map[span.U
// getPkgName gets the pkg name associated with an identifer representing // getPkgName gets the pkg name associated with an identifer representing
// the import path in an import spec. // the import path in an import spec.
func (i *IdentifierInfo) getPkgName(ctx context.Context) (*IdentifierInfo, error) { func (i *IdentifierInfo) getPkgName(ctx context.Context) (*IdentifierInfo, error) {
file := i.File.FileSet().File(i.Range.Start) file := i.File.FileSet().File(i.mappedRange.spanRange.Start)
pkgLine := file.Line(i.Range.Start) pkgLine := file.Line(i.mappedRange.spanRange.Start)
for _, obj := range i.pkg.GetTypesInfo().Defs { for _, obj := range i.pkg.GetTypesInfo().Defs {
pkgName, ok := obj.(*types.PkgName) pkgName, ok := obj.(*types.PkgName)
@ -133,22 +133,22 @@ func (i *IdentifierInfo) getPkgName(ctx context.Context) (*IdentifierInfo, error
// getPkgNameIdentifier returns an IdentifierInfo representing pkgName. // getPkgNameIdentifier returns an IdentifierInfo representing pkgName.
// pkgName must be in the same package and file as ident. // pkgName must be in the same package and file as ident.
func getPkgNameIdentifier(ctx context.Context, ident *IdentifierInfo, pkgName *types.PkgName) (*IdentifierInfo, error) { func getPkgNameIdentifier(ctx context.Context, ident *IdentifierInfo, pkgName *types.PkgName) (*IdentifierInfo, error) {
decl := declaration{ decl := Declaration{
obj: pkgName, obj: pkgName,
wasImplicit: true, wasImplicit: true,
} }
var err error var err error
if decl.rng, err = objToRange(ctx, ident.File.FileSet(), decl.obj); err != nil { if decl.mappedRange, err = objToRange(ctx, ident.File.View(), decl.obj); err != nil {
return nil, err return nil, err
} }
if decl.node, err = objToNode(ctx, ident.File.View(), ident.pkg.GetTypes(), decl.obj, decl.rng); err != nil { if decl.node, err = objToNode(ctx, ident.File.View(), ident.pkg.GetTypes(), decl.obj, decl.mappedRange.spanRange); err != nil {
return nil, err return nil, err
} }
return &IdentifierInfo{ return &IdentifierInfo{
Name: pkgName.Name(), Name: pkgName.Name(),
Range: decl.rng, mappedRange: decl.mappedRange,
File: ident.File, File: ident.File,
decl: decl, Declaration: decl,
pkg: ident.pkg, pkg: ident.pkg,
wasEmbeddedField: false, wasEmbeddedField: false,
qf: ident.qf, qf: ident.qf,
@ -165,7 +165,7 @@ func (r *renamer) update() (map[span.URI][]diff.TextEdit, error) {
return nil, err return nil, err
} }
for _, ref := range r.refs { for _, ref := range r.refs {
refSpan, err := ref.Range.Span() refSpan, err := ref.spanRange.Span()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -12,6 +12,7 @@ import (
"go/types" "go/types"
"golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/telemetry/trace" "golang.org/x/tools/internal/telemetry/trace"
errors "golang.org/x/xerrors" errors "golang.org/x/xerrors"
) )
@ -26,21 +27,25 @@ type ParameterInformation struct {
Label string Label string
} }
func SignatureHelp(ctx context.Context, f GoFile, pos token.Pos) (*SignatureInformation, error) { func SignatureHelp(ctx context.Context, view View, f GoFile, pos protocol.Position) (*SignatureInformation, error) {
ctx, done := trace.StartSpan(ctx, "source.SignatureHelp") ctx, done := trace.StartSpan(ctx, "source.SignatureHelp")
defer done() defer done()
file, err := f.GetAST(ctx, ParseFull) file, pkg, m, err := fileToMapper(ctx, view, f.URI())
if file == nil { if err != nil {
return nil, err return nil, err
} }
pkg, err := f.GetPackage(ctx) spn, err := m.PointSpan(pos)
if err != nil {
return nil, err
}
rng, err := spn.Range(m.Converter)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Find a call expression surrounding the query position. // Find a call expression surrounding the query position.
var callExpr *ast.CallExpr var callExpr *ast.CallExpr
path, _ := astutil.PathEnclosingInterval(file, pos, pos) path, _ := astutil.PathEnclosingInterval(file, rng.Start, rng.Start)
if path == nil { if path == nil {
return nil, errors.Errorf("cannot find node enclosing position") return nil, errors.Errorf("cannot find node enclosing position")
} }
@ -48,7 +53,7 @@ FindCall:
for _, node := range path { for _, node := range path {
switch node := node.(type) { switch node := node.(type) {
case *ast.CallExpr: case *ast.CallExpr:
if pos >= node.Lparen && pos <= node.Rparen { if rng.Start >= node.Lparen && rng.Start <= node.Rparen {
callExpr = node callExpr = node
break FindCall break FindCall
} }
@ -76,7 +81,7 @@ FindCall:
// Handle builtin functions separately. // Handle builtin functions separately.
if obj, ok := obj.(*types.Builtin); ok { if obj, ok := obj.(*types.Builtin); ok {
return builtinSignature(ctx, f.View(), callExpr, obj.Name(), pos) return builtinSignature(ctx, f.View(), callExpr, obj.Name(), rng.Start)
} }
// Get the type information for the function being called. // Get the type information for the function being called.
@ -93,24 +98,24 @@ FindCall:
qf := qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo()) qf := qualifier(file, pkg.GetTypes(), pkg.GetTypesInfo())
params := formatParams(sig.Params(), sig.Variadic(), qf) params := formatParams(sig.Params(), sig.Variadic(), qf)
results, writeResultParens := formatResults(sig.Results(), qf) results, writeResultParens := formatResults(sig.Results(), qf)
activeParam := activeParameter(callExpr, sig.Params().Len(), sig.Variadic(), pos) activeParam := activeParameter(callExpr, sig.Params().Len(), sig.Variadic(), rng.Start)
var ( var (
name string name string
comment *ast.CommentGroup comment *ast.CommentGroup
) )
if obj != nil { if obj != nil {
rng, err := objToRange(ctx, f.FileSet(), obj) rng, err := objToRange(ctx, view, obj)
if err != nil { if err != nil {
return nil, err return nil, err
} }
node, err := objToNode(ctx, f.View(), pkg.GetTypes(), obj, rng) node, err := objToNode(ctx, f.View(), pkg.GetTypes(), obj, rng.spanRange)
if err != nil { if err != nil {
return nil, err return nil, err
} }
decl := &declaration{ decl := &Declaration{
obj: obj, obj: obj,
rng: rng, mappedRange: rng,
node: node, node: node,
} }
d, err := decl.hover(ctx) d, err := decl.hover(ctx)

View File

@ -346,12 +346,11 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) {
if err != nil { if err != nil {
t.Fatalf("failed for %v: %v", d.Src, err) t.Fatalf("failed for %v: %v", d.Src, err)
} }
tok, err := f.(source.GoFile).GetToken(ctx) srcRng, err := spanToRange(r.data, d.Src)
if err != nil { if err != nil {
t.Fatalf("failed to get token for %s: %v", d.Src.URI(), err) t.Fatal(err)
} }
pos := tok.Pos(d.Src.Start().Offset()) ident, err := source.Identifier(ctx, r.view, f.(source.GoFile), srcRng.Start)
ident, err := source.Identifier(ctx, f.(source.GoFile), pos)
if err != nil { if err != nil {
t.Fatalf("failed for %v: %v", d.Src, err) t.Fatalf("failed for %v: %v", d.Src, err)
} }
@ -364,9 +363,15 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) {
hover += h.Synopsis + "\n" hover += h.Synopsis + "\n"
} }
hover += h.Signature hover += h.Signature
rng := ident.DeclarationRange() rng, err := ident.Range()
if err != nil {
t.Fatal(err)
}
if d.IsType { if d.IsType {
rng = ident.Type.Range rng, err = ident.Type.Range()
if err != nil {
t.Fatal(err)
}
hover = "" hover = ""
} }
if hover != "" { if hover != "" {
@ -378,10 +383,10 @@ func (r *runner) Definition(t *testing.T, data tests.Definitions) {
t.Errorf("for %v got %q want %q", d.Src, hover, expectHover) t.Errorf("for %v got %q want %q", d.Src, hover, expectHover)
} }
} else if !d.OnlyHover { } else if !d.OnlyHover {
if def, err := rng.Span(); err != nil { if defRng, err := spanToRange(r.data, d.Def); err != nil {
t.Fatalf("failed for %v: %v", rng, err) t.Fatal(err)
} else if def != d.Def { } else if rng != defRng {
t.Errorf("for %v got %v want %v", d.Src, def, d.Def) t.Errorf("for %v got %v want %v", d.Src, rng, d.Def)
} }
} else { } else {
t.Errorf("no tests ran for %s", d.Src.URI()) t.Errorf("no tests ran for %s", d.Src.URI())
@ -424,12 +429,11 @@ func (r *runner) Reference(t *testing.T, data tests.References) {
if err != nil { if err != nil {
t.Fatalf("failed for %v: %v", src, err) t.Fatalf("failed for %v: %v", src, err)
} }
tok, err := f.(source.GoFile).GetToken(ctx) srcRng, err := spanToRange(r.data, src)
if err != nil { if err != nil {
t.Fatalf("failed to get token for %s: %v", src.URI(), err) t.Fatal(err)
} }
pos := tok.Pos(src.Start().Offset()) ident, err := source.Identifier(ctx, r.view, f.(source.GoFile), srcRng.Start)
ident, err := source.Identifier(ctx, f.(source.GoFile), pos)
if err != nil { if err != nil {
t.Fatalf("failed for %v: %v", src, err) t.Fatalf("failed for %v: %v", src, err)
} }
@ -439,16 +443,16 @@ func (r *runner) Reference(t *testing.T, data tests.References) {
want[pos] = true want[pos] = true
} }
refs, err := ident.References(ctx) refs, err := ident.References(ctx, r.view)
if err != nil { if err != nil {
t.Fatalf("failed for %v: %v", src, err) t.Fatalf("failed for %v: %v", src, err)
} }
got := make(map[span.Span]bool) got := make(map[span.Span]bool)
for _, refInfo := range refs { for _, refInfo := range refs {
refSpan, err := refInfo.Range.Span() refSpan, err := refInfo.Span()
if err != nil { if err != nil {
t.Errorf("failed for %v item %v: %v", src, refInfo.Name, err) t.Fatal(err)
} }
got[refSpan] = true got[refSpan] = true
} }
@ -474,17 +478,16 @@ func (r *runner) Rename(t *testing.T, data tests.Renames) {
if err != nil { if err != nil {
t.Fatalf("failed for %v: %v", spn, err) t.Fatalf("failed for %v: %v", spn, err)
} }
tok, err := f.(source.GoFile).GetToken(ctx) srcRng, err := spanToRange(r.data, spn)
if err != nil { if err != nil {
t.Fatalf("failed to get token for %s: %v", spn.URI(), err) t.Fatal(err)
} }
pos := tok.Pos(spn.Start().Offset()) ident, err := source.Identifier(r.ctx, r.view, f.(source.GoFile), srcRng.Start)
ident, err := source.Identifier(r.ctx, f.(source.GoFile), pos)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
continue continue
} }
changes, err := ident.Rename(r.ctx, newText) changes, err := ident.Rename(r.ctx, r.view, newText)
if err != nil { if err != nil {
renamed := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) { renamed := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) {
return []byte(err.Error()), nil return []byte(err.Error()), nil
@ -619,12 +622,11 @@ func (r *runner) SignatureHelp(t *testing.T, data tests.Signatures) {
if err != nil { if err != nil {
t.Fatalf("failed for %v: %v", spn, err) t.Fatalf("failed for %v: %v", spn, err)
} }
tok, err := f.(source.GoFile).GetToken(ctx) rng, err := spanToRange(r.data, spn)
if err != nil { if err != nil {
t.Fatalf("failed to get token for %s: %v", spn.URI(), err) t.Fatal(err)
} }
pos := tok.Pos(spn.Start().Offset()) gotSignature, err := source.SignatureHelp(ctx, r.view, f.(source.GoFile), rng.Start)
gotSignature, err := source.SignatureHelp(ctx, f.(source.GoFile), pos)
if err != nil { if err != nil {
// Only fail if we got an error we did not expect. // Only fail if we got an error we did not expect.
if expectedSignature != nil { if expectedSignature != nil {
@ -667,3 +669,16 @@ func diffSignatures(spn span.Span, want *source.SignatureInformation, got *sourc
func (r *runner) Link(t *testing.T, data tests.Links) { func (r *runner) Link(t *testing.T, data tests.Links) {
// This is a pure LSP feature, no source level functionality to be tested. // This is a pure LSP feature, no source level functionality to be tested.
} }
func spanToRange(data *tests.Data, span span.Span) (protocol.Range, error) {
contents, err := data.Exported.FileContents(span.URI().Filename())
if err != nil {
return protocol.Range{}, err
}
m := protocol.NewColumnMapper(span.URI(), span.URI().Filename(), data.Exported.ExpectFileSet, nil, contents)
srcRng, err := m.Range(span)
if err != nil {
return protocol.Range{}, err
}
return srcRng, nil
}

View File

@ -16,6 +16,7 @@ import (
"golang.org/x/tools/internal/lsp/protocol" "golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/span" "golang.org/x/tools/internal/span"
errors "golang.org/x/xerrors"
) )
type mappedRange struct { type mappedRange struct {
@ -42,10 +43,94 @@ func (s mappedRange) Range() (protocol.Range, error) {
return *s.protocolRange, nil return *s.protocolRange, nil
} }
func (s mappedRange) Span() (span.Span, error) {
return s.spanRange.Span()
}
func (s mappedRange) URI() span.URI { func (s mappedRange) URI() span.URI {
return s.m.URI return s.m.URI
} }
func fileToMapper(ctx context.Context, view View, uri span.URI) (*ast.File, Package, *protocol.ColumnMapper, error) {
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, nil, nil, err
}
gof, ok := f.(GoFile)
if !ok {
return nil, nil, nil, errors.Errorf("%s is not a Go file", f.URI())
}
pkg, err := gof.GetPackage(ctx)
if err != nil {
return nil, nil, nil, err
}
file, m, err := pkgToMapper(ctx, view, pkg, uri)
if err != nil {
return nil, nil, nil, err
}
return file, pkg, m, nil
}
func cachedFileToMapper(ctx context.Context, view View, uri span.URI) (*ast.File, *protocol.ColumnMapper, error) {
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, nil, err
}
gof, ok := f.(GoFile)
if !ok {
return nil, nil, errors.Errorf("%s is not a Go file", f.URI())
}
if file, ok := gof.Builtin(); ok {
return builtinFileToMapper(ctx, view, gof, file)
}
pkg, err := gof.GetCachedPackage(ctx)
if err != nil {
return nil, nil, err
}
file, m, err := pkgToMapper(ctx, view, pkg, uri)
if err != nil {
return nil, nil, err
}
return file, m, nil
}
func pkgToMapper(ctx context.Context, view View, pkg Package, uri span.URI) (*ast.File, *protocol.ColumnMapper, error) {
var ph ParseGoHandle
for _, h := range pkg.GetHandles() {
if h.File().Identity().URI == uri {
ph = h
}
}
file, err := ph.Cached(ctx)
if file == nil {
return nil, nil, err
}
data, _, err := ph.File().Read(ctx)
if err != nil {
return nil, nil, err
}
fset := view.Session().Cache().FileSet()
tok := fset.File(file.Pos())
if tok == nil {
return nil, nil, errors.Errorf("no token.File for %s", uri)
}
return file, protocol.NewColumnMapper(uri, uri.Filename(), fset, tok, data), nil
}
func builtinFileToMapper(ctx context.Context, view View, f GoFile, file *ast.File) (*ast.File, *protocol.ColumnMapper, error) {
fh := f.Handle(ctx)
data, _, err := fh.Read(ctx)
if err != nil {
return nil, nil, err
}
fset := view.Session().Cache().FileSet()
tok := fset.File(file.Pos())
if tok == nil {
return nil, nil, errors.Errorf("no token.File for %s", f.URI())
}
return nil, protocol.NewColumnMapper(f.URI(), f.URI().Filename(), fset, tok, data), nil
}
func IsGenerated(ctx context.Context, view View, uri span.URI) bool { func IsGenerated(ctx context.Context, view View, uri span.URI) bool {
f, err := view.GetFile(ctx, uri) f, err := view.GetFile(ctx, uri)
if err != nil { if err != nil {

View File

@ -258,6 +258,8 @@ type File interface {
type GoFile interface { type GoFile interface {
File File
Builtin() (*ast.File, bool)
// GetAST returns the AST for the file, at or above the given mode. // GetAST returns the AST for the file, at or above the given mode.
GetAST(ctx context.Context, mode ParseMode) (*ast.File, error) GetAST(ctx context.Context, mode ParseMode) (*ast.File, error)

View File

@ -108,10 +108,9 @@ type Tests interface {
type Definition struct { type Definition struct {
Name string Name string
Src span.Span
IsType bool IsType bool
OnlyHover bool OnlyHover bool
Def span.Span Src, Def span.Span
} }
type CompletionSnippet struct { type CompletionSnippet struct {