internal/lsp/mod: fix broken assumptions about file base

Logic in mod.Hover relied upon ColumnMapper.TokFile being the only file
in its FileSet. Fix this to compare offsets with offsets.

Also use safetoken.InRange in semantic.go, to fix an incorrect bounds
check.

Change-Id: Ia18a0799fea1120674e3772efdf0bdb724e554e7
Reviewed-on: https://go-review.googlesource.com/c/tools/+/409934
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Robert Findley 2022-06-01 13:39:54 -04:00
parent 0cdf4b588d
commit 3dd056a8c5
4 changed files with 22 additions and 13 deletions

View File

@ -8,7 +8,6 @@ import (
"bytes"
"context"
"fmt"
"go/token"
"strings"
"golang.org/x/mod/modfile"
@ -39,7 +38,7 @@ func Hover(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle,
if err != nil {
return nil, fmt.Errorf("getting modfile handle: %w", err)
}
pos, err := pm.Mapper.Pos(position)
offset, err := pm.Mapper.Offset(position)
if err != nil {
return nil, fmt.Errorf("computing cursor position: %w", err)
}
@ -57,7 +56,7 @@ func Hover(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle,
// Shift the start position to the location of the
// dependency within the require statement.
startPos, endPos = s+i, s+i+len(dep)
if token.Pos(startPos) <= pos && pos <= token.Pos(endPos) {
if startPos <= offset && offset <= endPos {
req = r
break
}

View File

@ -105,7 +105,7 @@ func (m *ColumnMapper) RangeToSpanRange(r Range) (span.Range, error) {
return spn.Range(m.TokFile)
}
// Pos returns the token.Pos corresponding to the given protocol position.
// Pos returns the token.Pos of p within the mapped file.
func (m *ColumnMapper) Pos(p Position) (token.Pos, error) {
start, err := m.Point(p)
if err != nil {
@ -123,6 +123,17 @@ func (m *ColumnMapper) Pos(p Position) (token.Pos, error) {
return rng.Start, nil
}
// Offset returns the utf-8 byte offset of p within the mapped file.
func (m *ColumnMapper) Offset(p Position) (int, error) {
start, err := m.Point(p)
if err != nil {
return 0, err
}
return start.Offset(), nil
}
// Point returns a span.Point for p within the mapped file. The resulting point
// always has an Offset.
func (m *ColumnMapper) Point(p Position) (span.Point, error) {
line := int(p.Line) + 1
offset, err := span.ToOffset(m.TokFile, line, 1)

View File

@ -248,7 +248,7 @@ func (e *encoded) strStack() string {
loc := e.stack[len(e.stack)-1].Pos()
if !safetoken.InRange(e.pgf.Tok, loc) {
msg = append(msg, fmt.Sprintf("invalid position %v for %s", loc, e.pgf.URI))
} else if locInRange(e.pgf.Tok, loc) {
} else if safetoken.InRange(e.pgf.Tok, loc) {
add := e.pgf.Tok.PositionFor(loc, false)
nm := filepath.Base(add.Filename)
msg = append(msg, fmt.Sprintf("(%s:%d,col:%d)", nm, add.Line, add.Column))
@ -260,14 +260,6 @@ func (e *encoded) strStack() string {
return strings.Join(msg, " ")
}
// avoid panic in token.PositionFor() when typing at the end of the file
//
// TODO: this looks wrong: the second check should be int(loc) <= tf.Base()+tf.Size()?
// Can we just use safetoken.InRange?
func locInRange(tf *token.File, loc token.Pos) bool {
return tf.Base() <= int(loc) && int(loc) < tf.Base()+tf.Size()
}
// find the line in the source
func (e *encoded) srcLine(x ast.Node) string {
file := e.pgf.Tok

View File

@ -54,6 +54,13 @@ func ToUTF16Column(p Point, content []byte) (int, error) {
// supplied line contents.
// This is used to convert from the utf16 counts used by some editors to the
// native (always in bytes) column representation.
//
// The resulting Point always has an offset.
//
// TODO: it looks like this may incorrectly confer a "position" to the
// resulting Point, when it shouldn't. If p.HasPosition() == false, the
// resulting Point will return p.HasPosition() == true, but have the wrong
// position.
func FromUTF16Column(p Point, chr int, content []byte) (Point, error) {
if !p.HasOffset() {
return Point{}, fmt.Errorf("FromUTF16Column: point is missing offset")