internal/lsp/semantic: semantic tokens for imports of versions

For
import "a/bar/foo"
the existing code just decides the last component is the package name.
But for
import "a/bar/v2" this is incorrect, as the packge name is 'bar'.
The new code uses the result of parsing to derive the package name
from the import string.

That is, the package name was determined syntactically, it is
now determined semantically.

Fixes https://golang.org/issue/47784

Change-Id: Iccdd25e7e3f591e6514b1e0258e9e1879af9ff2d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/343909
Trust: Peter Weinberger <pjw@google.com>
Run-TryBot: Peter Weinberger <pjw@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
This commit is contained in:
pjw 2021-08-20 08:20:56 -04:00 committed by Peter Weinberger
parent 3e0d083b85
commit 70fb962d80
2 changed files with 33 additions and 16 deletions

View File

@ -88,7 +88,6 @@ func (s *Server) computeSemanticTokens(ctx context.Context, td protocol.TextDocu
if err != nil {
return nil, err
}
info := pkg.GetTypesInfo()
pgf, err := pkg.File(fh.URI())
if err != nil {
return nil, err
@ -103,7 +102,8 @@ func (s *Server) computeSemanticTokens(ctx context.Context, td protocol.TextDocu
ctx: ctx,
pgf: pgf,
rng: rng,
ti: info,
ti: pkg.GetTypesInfo(),
pkg: pkg,
fset: snapshot.FileSet(),
tokTypes: s.session.Options().SemanticTypes,
tokMods: s.session.Options().SemanticMods,
@ -220,6 +220,8 @@ type encoded struct {
pgf *source.ParsedGoFile
rng *protocol.Range
ti *types.Info
types *types.Package
pkg source.Package
fset *token.FileSet
// allowed starting and ending token.Pos, set by init
// used to avoid looking at declarations not in range
@ -238,16 +240,23 @@ func (e *encoded) strStack() string {
loc := e.stack[len(e.stack)-1].Pos()
if !source.InRange(e.pgf.Tok, loc) {
msg = append(msg, fmt.Sprintf("invalid position %v for %s", loc, e.pgf.URI))
} else {
} else if locInRange(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))
} else {
msg = append(msg, fmt.Sprintf("(loc %d out of range)", loc))
}
}
msg = append(msg, "]")
return strings.Join(msg, " ")
}
// avoid panic in token.PostionFor() when typing at the end of the file
func locInRange(f *token.File, loc token.Pos) bool {
return f.Base() <= int(loc) && int(loc) < f.Base()+f.Size()
}
// find the line in the source
func (e *encoded) srcLine(x ast.Node) string {
file := e.pgf.Tok
@ -784,16 +793,10 @@ func (e *encoded) importSpec(d *ast.ImportSpec) {
// a local package name or the last component of the Path
if d.Name != nil {
nm := d.Name.String()
// import . x => x is not a namespace
// import _ x => x is a namespace
if nm != "_" && nm != "." {
e.token(d.Name.Pos(), len(nm), tokNamespace, nil)
return
}
if nm == "." {
return
}
// and fall through for _
return // don't mark anything for . or _
}
val := d.Path.Value
if len(val) < 2 || val[0] != '"' || val[len(val)-1] != '"' {
@ -801,11 +804,25 @@ func (e *encoded) importSpec(d *ast.ImportSpec) {
return
}
nm := val[1 : len(val)-1] // remove surrounding "s
nm = filepath.Base(nm)
// in import "lib/math", 'math' is the package name
start := d.Path.End() - token.Pos(1+len(nm))
e.token(start, len(nm), tokNamespace, nil)
// There may be more cases, as import strings are implementation defined.
// Import strings are implementation defined. Try to match with parse information.
x, err := e.pkg.GetImport(nm)
if err != nil {
// unexpected, but impact is that maybe some import is not colored
return
}
// expect that nm is x.PkgPath and that x.Name() is a component of it
if x.PkgPath() != nm {
// don't know how or what to color (if this can happen at all)
return
}
// this is not a precise test: imagine "github.com/nasty/v/v2"
j := strings.LastIndex(nm, x.Name())
if j == -1 {
// name doesn't show up, for whatever reason, so nothing to report
return
}
start := d.Path.Pos() + 1 + token.Pos(j) // skip the initial quote
e.token(start, len(x.Name()), tokNamespace, nil)
}
// log unexpected state

View File

@ -2,7 +2,7 @@
/*⇒7,keyword,[]*/package /*⇒14,namespace,[]*/semantictokens /*⇒16,comment,[]*///@ semantic("")
/*⇒6,keyword,[]*/import (
_ "encoding/utf8"/*⇐4,namespace,[]*/
_ "encoding/utf8"
/*⇒3,namespace,[]*/utf "encoding/utf8"
"fmt"/*⇐3,namespace,[]*/ /*⇒19,comment,[]*///@ semantic("fmt")
. "fmt"