From 2dc213d980bc4ad623aefc805006b0864edeaac4 Mon Sep 17 00:00:00 2001 From: Rebecca Stambler Date: Mon, 16 Sep 2019 18:17:51 -0400 Subject: [PATCH] internal/lsp: remove cachedFileToMapper function This function incorrectly used cached packages to get ASTs and type information that should have been directly found from the origin package. Shift to using pkg.FindFile instead. Change-Id: I9f73209bb1a1343f53b430150e78ffd180e14a44 Reviewed-on: https://go-review.googlesource.com/c/tools/+/195797 Run-TryBot: Rebecca Stambler TryBot-Result: Gobot Gobot Reviewed-by: Ian Cottrell --- internal/lsp/cache/builtin.go | 4 + internal/lsp/cache/pkg.go | 15 +-- internal/lsp/cache/view.go | 11 +++ internal/lsp/source/completion_format.go | 6 +- internal/lsp/source/diagnostics.go | 9 +- internal/lsp/source/highlight.go | 10 +- internal/lsp/source/identifier.go | 35 +++++-- internal/lsp/source/references.go | 4 +- internal/lsp/source/rename.go | 27 ++++-- internal/lsp/source/signature_help.go | 15 ++- internal/lsp/source/suggested_fix.go | 14 ++- internal/lsp/source/symbols.go | 43 +++++---- internal/lsp/source/util.go | 113 +++++++---------------- internal/lsp/source/view.go | 3 +- 14 files changed, 170 insertions(+), 139 deletions(-) diff --git a/internal/lsp/cache/builtin.go b/internal/lsp/cache/builtin.go index e11f39e7d4..f70e4746d6 100644 --- a/internal/lsp/cache/builtin.go +++ b/internal/lsp/cache/builtin.go @@ -21,6 +21,10 @@ func (b *builtinPkg) Lookup(name string) *ast.Object { return b.pkg.Scope.Lookup(name) } +func (b *builtinPkg) Files() []source.ParseGoHandle { + return b.files +} + // buildBuiltinPkg builds the view's builtin package. // It assumes that the view is not active yet, // i.e. it has not been added to the session's list of views. diff --git a/internal/lsp/cache/pkg.go b/internal/lsp/cache/pkg.go index 757dc15f7e..8a0454f87f 100644 --- a/internal/lsp/cache/pkg.go +++ b/internal/lsp/cache/pkg.go @@ -202,7 +202,12 @@ func (pkg *pkg) GetDiagnostics() []source.Diagnostic { return diags } -func (p *pkg) FindFile(ctx context.Context, uri span.URI) (source.ParseGoHandle, *ast.File, source.Package, error) { +func (p *pkg) FindFile(ctx context.Context, uri span.URI) (source.ParseGoHandle, source.Package, error) { + // Special case for ignored files. + if p.view.Ignore(uri) { + return p.view.findIgnoredFile(ctx, uri) + } + queue := []*pkg{p} seen := make(map[string]bool) @@ -213,11 +218,7 @@ func (p *pkg) FindFile(ctx context.Context, uri span.URI) (source.ParseGoHandle, for _, ph := range pkg.files { if ph.File().Identity().URI == uri { - file, _, err := ph.Cached(ctx) - if file == nil { - return nil, nil, nil, err - } - return ph, file, pkg, nil + return ph, pkg, nil } } for _, dep := range pkg.imports { @@ -226,5 +227,5 @@ func (p *pkg) FindFile(ctx context.Context, uri span.URI) (source.ParseGoHandle, } } } - return nil, nil, nil, errors.Errorf("no file for %s", uri) + return nil, nil, errors.Errorf("no file for %s", uri) } diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go index 6ad50c6435..899ce5bb75 100644 --- a/internal/lsp/cache/view.go +++ b/internal/lsp/cache/view.go @@ -23,6 +23,7 @@ import ( "golang.org/x/tools/internal/lsp/telemetry" "golang.org/x/tools/internal/span" "golang.org/x/tools/internal/telemetry/log" + errors "golang.org/x/xerrors" ) type view struct { @@ -282,6 +283,16 @@ func (v *view) Ignore(uri span.URI) bool { return ok } +func (v *view) findIgnoredFile(ctx context.Context, uri span.URI) (source.ParseGoHandle, source.Package, error) { + // Check the builtin package. + for _, h := range v.BuiltinPackage().Files() { + if h.File().Identity().URI == uri { + return h, nil, nil + } + } + return nil, nil, errors.Errorf("no ignored file for %s", uri) +} + func (v *view) BackgroundContext() context.Context { v.mu.Lock() defer v.mu.Unlock() diff --git a/internal/lsp/source/completion_format.go b/internal/lsp/source/completion_format.go index ab8ff9c4f7..162ca2bd76 100644 --- a/internal/lsp/source/completion_format.go +++ b/internal/lsp/source/completion_format.go @@ -125,10 +125,14 @@ func (c *completer) item(cand candidate) (CompletionItem, error) { } uri := span.FileURI(pos.Filename) - _, file, pkg, err := c.pkg.FindFile(c.ctx, uri) + ph, pkg, err := c.pkg.FindFile(c.ctx, uri) if err != nil { return CompletionItem{}, err } + file, _, err := ph.Cached(c.ctx) + if file == nil { + return CompletionItem{}, err + } if !(file.Pos() <= obj.Pos() && obj.Pos() <= file.End()) { return CompletionItem{}, errors.Errorf("no file for %s", obj.Name()) } diff --git a/internal/lsp/source/diagnostics.go b/internal/lsp/source/diagnostics.go index 76f4319ea8..79c2fb58cc 100644 --- a/internal/lsp/source/diagnostics.go +++ b/internal/lsp/source/diagnostics.go @@ -245,10 +245,6 @@ func toDiagnostic(ctx context.Context, view View, diag analysis.Diagnostic, cate if diag.Category != "" { category += "." + category } - ca, err := getCodeActions(ctx, view, diag) - if err != nil { - return Diagnostic{}, err - } f, err := view.GetFile(ctx, spn.URI()) if err != nil { return Diagnostic{}, err @@ -263,6 +259,11 @@ func toDiagnostic(ctx context.Context, view View, diag analysis.Diagnostic, cate if err != nil { return Diagnostic{}, err } + ca, err := getCodeActions(ctx, view, pkg, diag) + if err != nil { + return Diagnostic{}, err + } + rng, err := spanToRange(ctx, view, pkg, spn, false) if err != nil { return Diagnostic{}, err diff --git a/internal/lsp/source/highlight.go b/internal/lsp/source/highlight.go index 8fce4a916d..4193831ea7 100644 --- a/internal/lsp/source/highlight.go +++ b/internal/lsp/source/highlight.go @@ -19,10 +19,16 @@ func Highlight(ctx context.Context, view View, uri span.URI, pos protocol.Positi ctx, done := trace.StartSpan(ctx, "source.Highlight") defer done() - file, _, m, err := fileToMapper(ctx, view, uri) + f, err := view.GetFile(ctx, uri) if err != nil { return nil, err } + fh := f.Handle(ctx) + ph := view.Session().Cache().ParseGoHandle(fh, ParseFull) + file, m, err := ph.Parse(ctx) + if file == nil { + return nil, err + } spn, err := m.PointSpan(pos) if err != nil { return nil, err @@ -43,7 +49,7 @@ func Highlight(ctx context.Context, view View, uri span.URI, pos protocol.Positi if id.Obj != nil { ast.Inspect(path[len(path)-1], func(n ast.Node) bool { if n, ok := n.(*ast.Ident); ok && n.Obj == id.Obj { - rng, err := nodeToProtocolRange(ctx, view, n) + rng, err := nodeToProtocolRange(ctx, view, m, n) if err == nil { result = append(result, rng) } diff --git a/internal/lsp/source/identifier.go b/internal/lsp/source/identifier.go index cb37dbb3fc..7f15a99245 100644 --- a/internal/lsp/source/identifier.go +++ b/internal/lsp/source/identifier.go @@ -48,10 +48,25 @@ type Declaration struct { // Identifier returns identifier information for a position // in a file, accounting for a potentially incomplete selector. func Identifier(ctx context.Context, view View, f GoFile, pos protocol.Position) (*IdentifierInfo, error) { - file, pkgs, m, err := fileToMapper(ctx, view, f.URI()) + pkgs, err := f.GetPackages(ctx) if err != nil { return nil, err } + pkg, err := bestPackage(f.URI(), pkgs) + if err != nil { + return nil, err + } + var ph ParseGoHandle + for _, h := range pkg.GetHandles() { + if h.File().Identity().URI == f.URI() { + ph = h + break + } + } + file, m, err := ph.Cached(ctx) + if file == nil { + return nil, err + } spn, err := m.PointSpan(pos) if err != nil { return nil, err @@ -121,7 +136,7 @@ func identifier(ctx context.Context, view View, pkgs []Package, file *ast.File, } } result.Name = result.ident.Name - if result.mappedRange, err = posToRange(ctx, view, result.ident.Pos(), result.ident.End()); err != nil { + if result.mappedRange, err = posToMappedRange(ctx, view, pkg, result.ident.Pos(), result.ident.End()); err != nil { return nil, err } result.Declaration.obj = pkg.GetTypesInfo().ObjectOf(result.ident) @@ -152,7 +167,7 @@ func identifier(ctx context.Context, view View, pkgs []Package, file *ast.File, return nil, errors.Errorf("no declaration for %s", result.Name) } result.Declaration.node = decl - if result.Declaration.mappedRange, err = nameToMappedRange(ctx, view, decl.Pos(), result.Name); err != nil { + if result.Declaration.mappedRange, err = nameToMappedRange(ctx, view, pkg, decl.Pos(), result.Name); err != nil { return nil, err } return result, nil @@ -177,7 +192,7 @@ func identifier(ctx context.Context, view View, pkgs []Package, file *ast.File, } } - if result.Declaration.mappedRange, err = objToMappedRange(ctx, view, result.Declaration.obj); err != nil { + if result.Declaration.mappedRange, err = objToMappedRange(ctx, view, pkg, result.Declaration.obj); err != nil { return nil, err } if result.Declaration.node, err = objToNode(ctx, view, pkg, result.Declaration.obj); err != nil { @@ -194,7 +209,7 @@ func identifier(ctx context.Context, view View, pkgs []Package, file *ast.File, if hasErrorType(result.Type.Object) { return result, nil } - if result.Type.mappedRange, err = objToMappedRange(ctx, view, result.Type.Object); err != nil { + if result.Type.mappedRange, err = objToMappedRange(ctx, view, pkg, result.Type.Object); err != nil { return nil, err } } @@ -230,7 +245,11 @@ func hasErrorType(obj types.Object) bool { func objToNode(ctx context.Context, view View, pkg Package, obj types.Object) (ast.Decl, error) { uri := span.FileURI(view.Session().Cache().FileSet().Position(obj.Pos()).Filename) - _, declAST, _, err := pkg.FindFile(ctx, uri) + ph, _, err := pkg.FindFile(ctx, uri) + if err != nil { + return nil, err + } + declAST, _, err := ph.Cached(ctx) if declAST == nil { return nil, err } @@ -291,7 +310,7 @@ func importSpec(ctx context.Context, view View, fAST *ast.File, pkgs []Package, Name: importPath, pkgs: pkgs, } - if result.mappedRange, err = posToRange(ctx, view, imp.Path.Pos(), imp.Path.End()); err != nil { + if result.mappedRange, err = posToMappedRange(ctx, view, pkg, imp.Path.Pos(), imp.Path.End()); err != nil { return nil, err } // Consider the "declaration" of an import spec to be the imported package. @@ -312,7 +331,7 @@ func importSpec(ctx context.Context, view View, fAST *ast.File, pkgs []Package, if dest == nil { return nil, errors.Errorf("package %q has no files", importPath) } - if result.Declaration.mappedRange, err = posToRange(ctx, view, dest.Pos(), dest.End()); err != nil { + if result.Declaration.mappedRange, err = posToMappedRange(ctx, view, pkg, dest.Pos(), dest.End()); err != nil { return nil, err } result.Declaration.node = imp diff --git a/internal/lsp/source/references.go b/internal/lsp/source/references.go index 142c3c0789..1d1a993ee4 100644 --- a/internal/lsp/source/references.go +++ b/internal/lsp/source/references.go @@ -56,7 +56,7 @@ func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, erro if obj == nil || !sameObj(obj, i.Declaration.obj) { continue } - rng, err := posToRange(ctx, i.View, ident.Pos(), ident.End()) + rng, err := posToMappedRange(ctx, i.View, pkg, ident.Pos(), ident.End()) if err != nil { return nil, err } @@ -74,7 +74,7 @@ func (i *IdentifierInfo) References(ctx context.Context) ([]*ReferenceInfo, erro if obj == nil || !sameObj(obj, i.Declaration.obj) { continue } - rng, err := posToRange(ctx, i.View, ident.Pos(), ident.End()) + rng, err := posToMappedRange(ctx, i.View, pkg, ident.Pos(), ident.End()) if err != nil { return nil, err } diff --git a/internal/lsp/source/rename.go b/internal/lsp/source/rename.go index 6ba33314f4..6f145479b4 100644 --- a/internal/lsp/source/rename.go +++ b/internal/lsp/source/rename.go @@ -149,13 +149,25 @@ func (i *IdentifierInfo) Rename(ctx context.Context, view View, newName string) } result := make(map[span.URI][]protocol.TextEdit) for uri, edits := range changes { - // Sort the edits first. - diff.SortTextEdits(edits) - - _, m, err := cachedFileToMapper(ctx, view, uri) + // These edits should really be associated with FileHandles for maximal correctness. + // For now, this is good enough. + f, err := view.GetFile(ctx, uri) if err != nil { return nil, err } + fh := f.Handle(ctx) + data, _, err := fh.Read(ctx) + if err != nil { + return nil, err + } + converter := span.NewContentConverter(uri.Filename(), data) + m := &protocol.ColumnMapper{ + URI: uri, + Converter: converter, + Content: data, + } + // Sort the edits first. + diff.SortTextEdits(edits) protocolEdits, err := ToProtocolEdits(m, edits) if err != nil { return nil, err @@ -218,14 +230,13 @@ func getPkgNameIdentifier(ctx context.Context, ident *IdentifierInfo, pkgName *t obj: pkgName, wasImplicit: true, } - var err error - if decl.mappedRange, err = objToMappedRange(ctx, ident.View, decl.obj); err != nil { - return nil, err - } pkg, err := bestPackage(ident.File.File().Identity().URI, ident.pkgs) if err != nil { return nil, err } + if decl.mappedRange, err = objToMappedRange(ctx, ident.View, pkg, decl.obj); err != nil { + return nil, err + } if decl.node, err = objToNode(ctx, ident.View, pkg, decl.obj); err != nil { return nil, err } diff --git a/internal/lsp/source/signature_help.go b/internal/lsp/source/signature_help.go index cc036ca323..6260e50c51 100644 --- a/internal/lsp/source/signature_help.go +++ b/internal/lsp/source/signature_help.go @@ -31,7 +31,7 @@ func SignatureHelp(ctx context.Context, view View, f GoFile, pos protocol.Positi ctx, done := trace.StartSpan(ctx, "source.SignatureHelp") defer done() - file, pkgs, m, err := fileToMapper(ctx, view, f.URI()) + pkgs, err := f.GetPackages(ctx) if err != nil { return nil, err } @@ -39,6 +39,17 @@ func SignatureHelp(ctx context.Context, view View, f GoFile, pos protocol.Positi if err != nil { return nil, err } + var ph ParseGoHandle + for _, h := range pkg.GetHandles() { + if h.File().Identity().URI == f.URI() { + ph = h + break + } + } + file, m, err := ph.Cached(ctx) + if file == nil { + return nil, err + } spn, err := m.PointSpan(pos) if err != nil { return nil, err @@ -113,7 +124,7 @@ FindCall: if err != nil { return nil, err } - rng, err := objToMappedRange(ctx, view, obj) + rng, err := objToMappedRange(ctx, view, pkg, obj) if err != nil { return nil, err } diff --git a/internal/lsp/source/suggested_fix.go b/internal/lsp/source/suggested_fix.go index a9706103a8..d32d47de83 100644 --- a/internal/lsp/source/suggested_fix.go +++ b/internal/lsp/source/suggested_fix.go @@ -5,14 +5,24 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/internal/lsp/protocol" + "golang.org/x/tools/internal/span" ) -func getCodeActions(ctx context.Context, view View, diag analysis.Diagnostic) ([]SuggestedFix, error) { +func getCodeActions(ctx context.Context, view View, pkg Package, diag analysis.Diagnostic) ([]SuggestedFix, error) { var fixes []SuggestedFix for _, fix := range diag.SuggestedFixes { var edits []protocol.TextEdit for _, e := range fix.TextEdits { - mrng, err := posToRange(ctx, view, e.Pos, e.End) + posn := view.Session().Cache().FileSet().Position(e.Pos) + ph, _, err := pkg.FindFile(ctx, span.FileURI(posn.Filename)) + if err != nil { + return nil, err + } + _, m, err := ph.Cached(ctx) + if m == nil { + return nil, err + } + mrng, err := posToRange(ctx, view, m, e.Pos, e.End) if err != nil { return nil, err } diff --git a/internal/lsp/source/symbols.go b/internal/lsp/source/symbols.go index 1360ea1b30..42bfaa396c 100644 --- a/internal/lsp/source/symbols.go +++ b/internal/lsp/source/symbols.go @@ -22,10 +22,13 @@ func DocumentSymbols(ctx context.Context, view View, f GoFile) ([]protocol.Docum if err != nil { return nil, err } - var file *ast.File + var ( + file *ast.File + m *protocol.ColumnMapper + ) for _, ph := range pkg.GetHandles() { if ph.File().Identity().URI == f.URI() { - file, _, err = ph.Cached(ctx) + file, m, err = ph.Cached(ctx) } } if file == nil { @@ -42,7 +45,7 @@ func DocumentSymbols(ctx context.Context, view View, f GoFile) ([]protocol.Docum switch decl := decl.(type) { case *ast.FuncDecl: if obj := info.ObjectOf(decl.Name); obj != nil { - if fs := funcSymbol(ctx, view, decl, obj, q); fs.Kind == protocol.Method { + if fs := funcSymbol(ctx, view, m, decl, obj, q); fs.Kind == protocol.Method { // Store methods separately, as we want them to appear as children // of the corresponding type (which we may not have seen yet). rtype := obj.Type().(*types.Signature).Recv().Type() @@ -56,14 +59,14 @@ func DocumentSymbols(ctx context.Context, view View, f GoFile) ([]protocol.Docum switch spec := spec.(type) { case *ast.TypeSpec: if obj := info.ObjectOf(spec.Name); obj != nil { - ts := typeSymbol(ctx, view, info, spec, obj, q) + ts := typeSymbol(ctx, view, m, info, spec, obj, q) symbols = append(symbols, ts) symbolsToReceiver[obj.Type()] = len(symbols) - 1 } case *ast.ValueSpec: for _, name := range spec.Names { if obj := info.ObjectOf(name); obj != nil { - symbols = append(symbols, varSymbol(ctx, view, decl, name, obj, q)) + symbols = append(symbols, varSymbol(ctx, view, m, decl, name, obj, q)) } } } @@ -87,15 +90,15 @@ func DocumentSymbols(ctx context.Context, view View, f GoFile) ([]protocol.Docum return symbols, nil } -func funcSymbol(ctx context.Context, view View, decl *ast.FuncDecl, obj types.Object, q types.Qualifier) protocol.DocumentSymbol { +func funcSymbol(ctx context.Context, view View, m *protocol.ColumnMapper, decl *ast.FuncDecl, obj types.Object, q types.Qualifier) protocol.DocumentSymbol { s := protocol.DocumentSymbol{ Name: obj.Name(), Kind: protocol.Function, } - if span, err := nodeToProtocolRange(ctx, view, decl); err == nil { + if span, err := nodeToProtocolRange(ctx, view, m, decl); err == nil { s.Range = span } - if span, err := nodeToProtocolRange(ctx, view, decl.Name); err == nil { + if span, err := nodeToProtocolRange(ctx, view, m, decl.Name); err == nil { s.SelectionRange = span } sig, _ := obj.Type().(*types.Signature) @@ -148,17 +151,17 @@ func setKind(s *protocol.DocumentSymbol, typ types.Type, q types.Qualifier) { } } -func typeSymbol(ctx context.Context, view View, info *types.Info, spec *ast.TypeSpec, obj types.Object, q types.Qualifier) protocol.DocumentSymbol { +func typeSymbol(ctx context.Context, view View, m *protocol.ColumnMapper, info *types.Info, spec *ast.TypeSpec, obj types.Object, q types.Qualifier) protocol.DocumentSymbol { s := protocol.DocumentSymbol{ Name: obj.Name(), } s.Detail, _ = formatType(obj.Type(), q) setKind(&s, obj.Type(), q) - if span, err := nodeToProtocolRange(ctx, view, spec); err == nil { + if span, err := nodeToProtocolRange(ctx, view, m, spec); err == nil { s.Range = span } - if span, err := nodeToProtocolRange(ctx, view, spec.Name); err == nil { + if span, err := nodeToProtocolRange(ctx, view, m, spec.Name); err == nil { s.SelectionRange = span } t, objIsStruct := obj.Type().Underlying().(*types.Struct) @@ -173,10 +176,10 @@ func typeSymbol(ctx context.Context, view View, info *types.Info, spec *ast.Type child.Detail, _ = formatType(f.Type(), q) spanNode, selectionNode := nodesForStructField(i, st) - if span, err := nodeToProtocolRange(ctx, view, spanNode); err == nil { + if span, err := nodeToProtocolRange(ctx, view, m, spanNode); err == nil { child.Range = span } - if span, err := nodeToProtocolRange(ctx, view, selectionNode); err == nil { + if span, err := nodeToProtocolRange(ctx, view, m, selectionNode); err == nil { child.SelectionRange = span } s.Children = append(s.Children, child) @@ -203,10 +206,10 @@ func typeSymbol(ctx context.Context, view View, info *types.Info, spec *ast.Type } } } - if span, err := nodeToProtocolRange(ctx, view, spanNode); err == nil { + if span, err := nodeToProtocolRange(ctx, view, m, spanNode); err == nil { child.Range = span } - if span, err := nodeToProtocolRange(ctx, view, selectionNode); err == nil { + if span, err := nodeToProtocolRange(ctx, view, m, selectionNode); err == nil { child.SelectionRange = span } s.Children = append(s.Children, child) @@ -235,10 +238,10 @@ func typeSymbol(ctx context.Context, view View, info *types.Info, spec *ast.Type break Embeddeds } } - if rng, err := nodeToProtocolRange(ctx, view, spanNode); err == nil { + if rng, err := nodeToProtocolRange(ctx, view, m, spanNode); err == nil { child.Range = rng } - if span, err := nodeToProtocolRange(ctx, view, selectionNode); err == nil { + if span, err := nodeToProtocolRange(ctx, view, m, selectionNode); err == nil { child.SelectionRange = span } s.Children = append(s.Children, child) @@ -267,7 +270,7 @@ func nodesForStructField(i int, st *ast.StructType) (span, selection ast.Node) { return nil, nil } -func varSymbol(ctx context.Context, view View, decl ast.Node, name *ast.Ident, obj types.Object, q types.Qualifier) protocol.DocumentSymbol { +func varSymbol(ctx context.Context, view View, m *protocol.ColumnMapper, decl ast.Node, name *ast.Ident, obj types.Object, q types.Qualifier) protocol.DocumentSymbol { s := protocol.DocumentSymbol{ Name: obj.Name(), Kind: protocol.Variable, @@ -275,10 +278,10 @@ func varSymbol(ctx context.Context, view View, decl ast.Node, name *ast.Ident, o if _, ok := obj.(*types.Const); ok { s.Kind = protocol.Constant } - if rng, err := nodeToProtocolRange(ctx, view, decl); err == nil { + if rng, err := nodeToProtocolRange(ctx, view, m, decl); err == nil { s.Range = rng } - if span, err := nodeToProtocolRange(ctx, view, name); err == nil { + if span, err := nodeToProtocolRange(ctx, view, m, name); err == nil { s.SelectionRange = span } s.Detail = types.TypeString(obj.Type(), q) diff --git a/internal/lsp/source/util.go b/internal/lsp/source/util.go index 2ab10190a8..c992be0e07 100644 --- a/internal/lsp/source/util.go +++ b/internal/lsp/source/util.go @@ -69,73 +69,6 @@ func bestPackage(uri span.URI, pkgs []Package) (Package, error) { return result, nil } -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()) - } - pkgs, err := gof.GetPackages(ctx) - if err != nil { - return nil, nil, nil, err - } - pkg, err := bestPackage(f.URI(), pkgs) - if err != nil { - return nil, nil, nil, err - } - file, m, err := pkgToMapper(ctx, view, pkg, f.URI()) - if err != nil { - return nil, nil, nil, err - } - return file, pkgs, 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()) - } - pkg, err := gof.GetCachedPackage(ctx) - if err == nil { - file, m, err := pkgToMapper(ctx, view, pkg, f.URI()) - if err != nil { - return nil, nil, err - } - return file, m, nil - } - // Fallback to just looking for the AST. - ph := view.Session().Cache().ParseGoHandle(gof.Handle(ctx), ParseFull) - file, m, err := ph.Cached(ctx) - if file == 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 - } - } - if ph == nil { - return nil, nil, errors.Errorf("no ParseGoHandle for %s", uri) - } - file, m, err := ph.Cached(ctx) - if file == nil { - return nil, nil, err - } - return file, m, nil -} - func IsGenerated(ctx context.Context, view View, uri span.URI) bool { f, err := view.GetFile(ctx, uri) if err != nil { @@ -163,15 +96,15 @@ func IsGenerated(ctx context.Context, view View, uri span.URI) bool { return false } -func nodeToProtocolRange(ctx context.Context, view View, n ast.Node) (protocol.Range, error) { - mrng, err := nodeToMappedRange(ctx, view, n) +func nodeToProtocolRange(ctx context.Context, view View, m *protocol.ColumnMapper, n ast.Node) (protocol.Range, error) { + mrng, err := nodeToMappedRange(ctx, view, m, n) if err != nil { return protocol.Range{}, err } return mrng.Range() } -func objToMappedRange(ctx context.Context, view View, obj types.Object) (mappedRange, error) { +func objToMappedRange(ctx context.Context, view View, pkg Package, obj types.Object) (mappedRange, error) { if pkgName, ok := obj.(*types.PkgName); ok { // An imported Go package has a package-local, unqualified name. // When the name matches the imported package name, there is no @@ -184,38 +117,54 @@ func objToMappedRange(ctx context.Context, view View, obj types.Object) (mappedR // When the identifier does not appear in the source, have the range // of the object be the point at the beginning of the declaration. if pkgName.Imported().Name() == pkgName.Name() { - return nameToMappedRange(ctx, view, obj.Pos(), "") + return nameToMappedRange(ctx, view, pkg, obj.Pos(), "") } } - return nameToMappedRange(ctx, view, obj.Pos(), obj.Name()) + return nameToMappedRange(ctx, view, pkg, obj.Pos(), obj.Name()) } -func nameToMappedRange(ctx context.Context, view View, pos token.Pos, name string) (mappedRange, error) { - return posToRange(ctx, view, pos, pos+token.Pos(len(name))) +func nameToMappedRange(ctx context.Context, view View, pkg Package, pos token.Pos, name string) (mappedRange, error) { + return posToMappedRange(ctx, view, pkg, pos, pos+token.Pos(len(name))) } -func nodeToMappedRange(ctx context.Context, view View, n ast.Node) (mappedRange, error) { - return posToRange(ctx, view, n.Pos(), n.End()) +func nodeToMappedRange(ctx context.Context, view View, m *protocol.ColumnMapper, n ast.Node) (mappedRange, error) { + return posToRange(ctx, view, m, n.Pos(), n.End()) } -func posToRange(ctx context.Context, view View, pos, end token.Pos) (mappedRange, error) { +func posToMappedRange(ctx context.Context, view View, pkg Package, pos, end token.Pos) (mappedRange, error) { + m, err := posToMapper(ctx, view, pkg, pos) + if err != nil { + return mappedRange{}, err + } + return posToRange(ctx, view, m, pos, end) +} + +func posToRange(ctx context.Context, view View, m *protocol.ColumnMapper, 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 posToMapper(ctx context.Context, view View, pkg Package, pos token.Pos) (*protocol.ColumnMapper, error) { + posn := view.Session().Cache().FileSet().Position(pos) + ph, _, err := pkg.FindFile(ctx, span.FileURI(posn.Filename)) + if err != nil { + return nil, err + } + _, m, err := ph.Cached(ctx) + if m == nil { + return nil, err + } + return m, nil +} + // Matches cgo generated comment as well as the proposed standard: // https://golang.org/s/generatedcode var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`) diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go index a8cd61ec65..6920312cad 100644 --- a/internal/lsp/source/view.go +++ b/internal/lsp/source/view.go @@ -319,9 +319,10 @@ type Package interface { // FindFile returns the AST and type information for a file that may // belong to or be part of a dependency of the given package. - FindFile(ctx context.Context, uri span.URI) (ParseGoHandle, *ast.File, Package, error) + FindFile(ctx context.Context, uri span.URI) (ParseGoHandle, Package, error) } type BuiltinPackage interface { Lookup(name string) *ast.Object + Files() []ParseGoHandle }