Skip to content

Commit 6916077

Browse files
adonovangopherbot
authored andcommitted
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>
1 parent 8fa4173 commit 6916077

File tree

2 files changed

+26
-10
lines changed

2 files changed

+26
-10
lines changed

gopls/internal/golang/semtok.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -259,13 +259,12 @@ func (tv *tokenVisitor) comment(c *ast.Comment, importByName map[string]*types.P
259259

260260
// token emits a token of the specified extent and semantics.
261261
func (tv *tokenVisitor) token(start token.Pos, length int, typ semtok.TokenType, modifiers []string) {
262-
if length <= 0 {
263-
return // vscode doesn't like 0-length Tokens
264-
}
265262
if !start.IsValid() {
266-
// This is not worth reporting. TODO(pjw): does it still happen?
267263
return
268264
}
265+
if length <= 0 {
266+
return // vscode doesn't like 0-length Tokens
267+
}
269268
end := start + token.Pos(length)
270269
if start >= tv.end || end <= tv.start {
271270
return
@@ -849,19 +848,21 @@ func (tv *tokenVisitor) multiline(start, end token.Pos, tok semtok.TokenType) {
849848
}
850849

851850
// findKeyword returns the position of a keyword by searching within
852-
// the specified range, for when its cannot be exactly known from the AST.
851+
// the specified range, for when it cannot be exactly known from the AST.
852+
// It returns NoPos if the keyword was not present in the source due to parse error.
853853
func (tv *tokenVisitor) findKeyword(keyword string, start, end token.Pos) token.Pos {
854854
// TODO(adonovan): use safetoken.Offset.
855855
offset := int(start) - tv.pgf.Tok.Base()
856856
last := int(end) - tv.pgf.Tok.Base()
857857
buf := tv.pgf.Src
858858
idx := bytes.Index(buf[offset:last], []byte(keyword))
859-
if idx != -1 {
860-
return start + token.Pos(idx)
859+
if idx < 0 {
860+
// Ill-formed code may form syntax trees without their usual tokens.
861+
// For example, "type _ <-<-chan int" parses as <-chan (chan int),
862+
// with two nested ChanTypes but only one chan keyword.
863+
return token.NoPos
861864
}
862-
//(in unparsable programs: type _ <-<-chan int)
863-
tv.errorf("not found:%s %v", keyword, safetoken.StartPosition(tv.fset, start))
864-
return token.NoPos
865+
return start + token.Pos(idx)
865866
}
866867

867868
func (tv *tokenVisitor) importSpec(spec *ast.ImportSpec) {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
This test checks semanticTokens on ill-formed code.
2+
(Regression test for #68205.)
3+
4+
-- settings.json --
5+
{
6+
"semanticTokens": true
7+
}
8+
9+
-- flags --
10+
-ignore_extra_diags
11+
12+
-- a.go --
13+
package p
14+
15+
type _ <-<-chan int //@ token("<-", "operator", ""), token("chan", "keyword", "")

0 commit comments

Comments
 (0)