Skip to content

Commit 5b540d3

Browse files
committed
internal/lsp/semantic.go: repress useless messages and tighten logic
Error messages associated with bad parses and when working at the end of the file are not useful to users, so they are no longer generated. The logic around recognizing function calls has been improved, and a panic sometimes caused by bad imports has been fixed. The logic is imprecise/incorrect in some cases; there will be another CL. Change-Id: I72be3a0a003569fe06d458989e3dbbb46b7a22c0 Reviewed-on: https://go-review.googlesource.com/c/tools/+/332909 Run-TryBot: Peter Weinberger <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Trust: Peter Weinberger <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent e33c0f2 commit 5b540d3

File tree

3 files changed

+54
-11
lines changed

3 files changed

+54
-11
lines changed

internal/lsp/semantic.go

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"go/token"
1313
"go/types"
1414
"log"
15+
"path/filepath"
1516
"sort"
1617
"strings"
1718
"time"
@@ -93,7 +94,7 @@ func (s *Server) computeSemanticTokens(ctx context.Context, td protocol.TextDocu
9394
if err != nil {
9495
return nil, err
9596
}
96-
// don't return errors on pgf.ParseErr. Do what we can.
97+
// ignore pgf.ParseErr. Do what we can.
9798
if rng == nil && len(pgf.Src) > maxFullFileSize {
9899
err := fmt.Errorf("semantic tokens: file %s too large for full (%d>%d)",
99100
fh.URI().Filename(), len(pgf.Src), maxFullFileSize)
@@ -122,6 +123,7 @@ func (s *Server) computeSemanticTokens(ctx context.Context, td protocol.TextDocu
122123

123124
func (e *encoded) semantics() {
124125
f := e.pgf.File
126+
// may not be in range, but harmless
125127
e.token(f.Package, len("package"), tokKeyword, nil)
126128
e.token(f.Name.NamePos, len(f.Name.Name), tokNamespace, nil)
127129
inspect := func(n ast.Node) bool {
@@ -166,8 +168,11 @@ const (
166168
)
167169

168170
func (e *encoded) token(start token.Pos, leng int, typ tokenType, mods []string) {
169-
if start == 0 {
170-
e.unexpected("token at token.NoPos")
171+
172+
if start == token.NoPos {
173+
// This is not worth reporting
174+
//e.unexpected("token at token.NoPos")
175+
return
171176
}
172177
if start >= e.end || start+token.Pos(leng) <= e.start {
173178
return
@@ -186,10 +191,7 @@ func (e *encoded) token(start token.Pos, leng int, typ tokenType, mods []string)
186191
return
187192
}
188193
if lspRange.End.Line != lspRange.Start.Line {
189-
// abrupt end of file, without \n. TODO(pjw): fix?
190-
pos := e.fset.PositionFor(start, false)
191-
msg := fmt.Sprintf("token at %s:%d.%d overflows", pos.Filename, pos.Line, pos.Column)
192-
event.Log(e.ctx, msg)
194+
// this happens if users are typing at the end of the file, but report nothing
193195
return
194196
}
195197
// token is all on one line
@@ -236,12 +238,22 @@ func (e *encoded) strStack() string {
236238
if len(e.stack) > 0 {
237239
loc := e.stack[len(e.stack)-1].Pos()
238240
add := e.pgf.Tok.PositionFor(loc, false)
239-
msg = append(msg, fmt.Sprintf("(line:%d,col:%d)", add.Line, add.Column))
241+
nm := filepath.Base(add.Filename)
242+
msg = append(msg, fmt.Sprintf("(%s:%d,col:%d)", nm, add.Line, add.Column))
240243
}
241244
msg = append(msg, "]")
242245
return strings.Join(msg, " ")
243246
}
244247

248+
func (e *encoded) srcLine(x ast.Node) string {
249+
file := e.pgf.Tok
250+
line := file.Line(x.Pos())
251+
start := file.Offset(file.LineStart(line))
252+
end := file.Offset(file.LineStart(line + 1)) // and hope it's not the last line of the file
253+
ans := e.pgf.Src[start : end-1]
254+
return string(ans)
255+
}
256+
245257
func (e *encoded) inspector(n ast.Node) bool {
246258
pop := func() {
247259
e.stack = e.stack[:len(e.stack)-1]
@@ -409,6 +421,26 @@ func (e *encoded) ident(x *ast.Ident) {
409421
use := e.ti.Uses[x]
410422
switch y := use.(type) {
411423
case nil:
424+
// In this position we think the identifier is either a function or a variable
425+
// and it is possible that it is being defined. The decision has to be made based
426+
// on where we are in the parse tree (and all that's known is the parse stack).
427+
// The present logic is inadequate, and will be fixed in the next CL:
428+
// ExprStmt CallExpr Ident: var [x in a(x)]
429+
// ExprStmt CallExpr Ident: function [f()]
430+
// CallExpr TypeAssertExpr Ident: type [so not variable nor function]
431+
log.SetFlags(log.Lshortfile)
432+
log.Printf("%s %s%q", x.Name, e.strStack(), e.srcLine(x))
433+
if len(e.stack) >= 3 {
434+
n := len(e.stack) - 1
435+
if _, ok := e.stack[n-1].(*ast.SelectorExpr); ok {
436+
if _, ok = e.stack[n-2].(*ast.CallExpr); ok {
437+
log.Print("function")
438+
e.token(x.NamePos, len(x.Name), tokFunction, nil)
439+
break
440+
}
441+
}
442+
}
443+
log.Print("var def")
412444
e.token(x.NamePos, len(x.Name), tokVariable, []string{"definition"})
413445
case *types.Builtin:
414446
e.token(x.NamePos, len(x.Name), tokFunction, []string{"defaultLibrary"})
@@ -642,16 +674,21 @@ func (e *encoded) importSpec(d *ast.ImportSpec) {
642674
}
643675
// and fall through for _
644676
}
645-
if d.Path.Value == "" {
677+
val := d.Path.Value
678+
if len(val) < 2 || val[0] != '"' || val[len(val)-1] != '"' {
679+
// avoid panics on imports without a properly quoted string
646680
return
647681
}
648-
nm := d.Path.Value[1 : len(d.Path.Value)-1] // trailing "
682+
nm := val[1 : len(val)-1] // remove surrounding "s
649683
v := strings.LastIndex(nm, "/")
650684
if v != -1 {
685+
// in import "lib/math", 'math' is the package name
651686
nm = nm[v+1:]
652687
}
653688
start := d.Path.End() - token.Pos(1+len(nm))
654689
e.token(start, len(nm), tokNamespace, nil)
690+
// There may be more cases, as import strings are implementation defined.
691+
// (E.g., module a.b.c (without a /), the 'a' should be tokNamespace, if we cared.)
655692
}
656693

657694
// log unexpected state

internal/lsp/testdata/semantic/a.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ func (a *A) f() bool {
5555
w := b[4:]
5656
j := len(x)
5757
j--
58+
q := []interface{}{j, 23i, &y}
59+
g(q...)
5860
return true
5961
}
6062

@@ -74,5 +76,6 @@ Never:
7476
if !ok {
7577
switch x := vv[0].(type) {
7678
}
79+
goto Never
7780
}
7881
}

internal/lsp/testdata/semantic/a.go.golden

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,15 @@
5656
/*⇒1,variable,[definition]*/w /*⇒2,operator,[]*/:= /*⇒1,variable,[]*/b[/*⇒1,number,[]*/4:]
5757
/*⇒1,variable,[definition]*/j /*⇒2,operator,[]*/:= /*⇒3,function,[defaultLibrary]*/len(/*⇒1,variable,[]*/x)
5858
/*⇒1,variable,[]*/j/*⇒2,operator,[]*/--
59+
/*⇒1,variable,[definition]*/q /*⇒2,operator,[]*/:= []/*⇒9,keyword,[]*/interface{}{/*⇒1,variable,[]*/j, /*⇒3,number,[]*/23i, /*⇒1,operator,[]*/&/*⇒1,variable,[]*/y}
60+
/*⇒1,function,[]*/g(/*⇒1,variable,[]*/q/*⇒3,operator,[]*/...)
5961
/*⇒6,keyword,[]*/return /*⇒4,variable,[readonly]*/true
6062
}
6163

6264
/*⇒4,keyword,[]*/func /*⇒1,function,[definition]*/g(/*⇒2,parameter,[definition]*/vv /*⇒3,operator,[]*/.../*⇒9,keyword,[]*/interface{}) {
6365
/*⇒2,variable,[definition]*/ff /*⇒2,operator,[]*/:= /*⇒4,keyword,[]*/func() {}
6466
/*⇒5,keyword,[]*/defer /*⇒2,variable,[]*/ff()
65-
/*⇒2,keyword,[]*/go /*⇒3,namespace,[]*/utf./*⇒9,variable,[definition]*/RuneCount(/*⇒2,string,[]*/"")
67+
/*⇒2,keyword,[]*/go /*⇒3,namespace,[]*/utf./*⇒9,function,[]*/RuneCount(/*⇒2,string,[]*/"")
6668
/*⇒2,keyword,[]*/go /*⇒4,namespace,[]*/utf8./*⇒9,function,[]*/RuneCount(/*⇒2,variable,[]*/vv.(/*⇒6,variable,[definition]*/string))
6769
/*⇒2,keyword,[]*/if /*⇒4,variable,[readonly]*/true {
6870
} /*⇒4,keyword,[]*/else {
@@ -75,6 +77,7 @@
7577
/*⇒2,keyword,[]*/if /*⇒1,operator,[]*/!/*⇒2,variable,[]*/ok {
7678
/*⇒6,keyword,[]*/switch /*⇒1,variable,[definition]*/x /*⇒2,operator,[]*/:= /*⇒2,variable,[]*/vv[/*⇒1,number,[]*/0].(/*⇒4,keyword,[]*/type) {
7779
}
80+
/*⇒4,keyword,[]*/goto Never
7881
}
7982
}
8083

0 commit comments

Comments
 (0)