gopls/internal/golang: don't panic when findKeyword fails
As the existing comment attests, this can happen in the wild.
Just skip it and move on.
+ a test
Fixes golang/go#68205
Change-Id: I3227b0ce7ffacf3c8b4bbf2180a10e218bf87aa3
Reviewed-on: https://go-review.googlesource.com/c/tools/+/595117
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/gopls/internal/golang/semtok.go b/gopls/internal/golang/semtok.go
index ec628f5..99a0ba3 100644
--- a/gopls/internal/golang/semtok.go
+++ b/gopls/internal/golang/semtok.go
@@ -259,13 +259,12 @@
// token emits a token of the specified extent and semantics.
func (tv *tokenVisitor) token(start token.Pos, length int, typ semtok.TokenType, modifiers []string) {
+ if !start.IsValid() {
+ return
+ }
if length <= 0 {
return // vscode doesn't like 0-length Tokens
}
- if !start.IsValid() {
- // This is not worth reporting. TODO(pjw): does it still happen?
- return
- }
end := start + token.Pos(length)
if start >= tv.end || end <= tv.start {
return
@@ -849,19 +848,21 @@
}
// findKeyword returns the position of a keyword by searching within
-// the specified range, for when its cannot be exactly known from the AST.
+// the specified range, for when it cannot be exactly known from the AST.
+// It returns NoPos if the keyword was not present in the source due to parse error.
func (tv *tokenVisitor) findKeyword(keyword string, start, end token.Pos) token.Pos {
// TODO(adonovan): use safetoken.Offset.
offset := int(start) - tv.pgf.Tok.Base()
last := int(end) - tv.pgf.Tok.Base()
buf := tv.pgf.Src
idx := bytes.Index(buf[offset:last], []byte(keyword))
- if idx != -1 {
- return start + token.Pos(idx)
+ if idx < 0 {
+ // Ill-formed code may form syntax trees without their usual tokens.
+ // For example, "type _ <-<-chan int" parses as <-chan (chan int),
+ // with two nested ChanTypes but only one chan keyword.
+ return token.NoPos
}
- //(in unparsable programs: type _ <-<-chan int)
- tv.errorf("not found:%s %v", keyword, safetoken.StartPosition(tv.fset, start))
- return token.NoPos
+ return start + token.Pos(idx)
}
func (tv *tokenVisitor) importSpec(spec *ast.ImportSpec) {
diff --git a/gopls/internal/test/marker/testdata/token/illformed.txt b/gopls/internal/test/marker/testdata/token/illformed.txt
new file mode 100644
index 0000000..2a3b81e
--- /dev/null
+++ b/gopls/internal/test/marker/testdata/token/illformed.txt
@@ -0,0 +1,15 @@
+This test checks semanticTokens on ill-formed code.
+(Regression test for #68205.)
+
+-- settings.json --
+{
+ "semanticTokens": true
+}
+
+-- flags --
+-ignore_extra_diags
+
+-- a.go --
+package p
+
+type _ <-<-chan int //@ token("<-", "operator", ""), token("chan", "keyword", "")