Skip to content

Commit 32d3139

Browse files
committed
gopls/internal/golang: add semantic tokens for control labels
Fixes golang/go#65494 Change-Id: Id044cd12a5c48bb3d5dc8f91657febdc431811b4 Reviewed-on: https://go-review.googlesource.com/c/tools/+/562244 Reviewed-by: Peter Weinberger <[email protected]> Reviewed-by: Robert Findley <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 0d17194 commit 32d3139

File tree

5 files changed

+44
-6
lines changed

5 files changed

+44
-6
lines changed

gopls/internal/golang/semtok.go

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,9 @@ func (tv *tokenVisitor) inspect(n ast.Node) (descend bool) {
231231
case *ast.BlockStmt:
232232
case *ast.BranchStmt:
233233
tv.token(n.TokPos, len(n.Tok.String()), semtok.TokKeyword, nil)
234-
// There's no semantic encoding for labels
234+
if n.Label != nil {
235+
tv.token(n.Label.Pos(), len(n.Label.Name), semtok.TokLabel, nil)
236+
}
235237
case *ast.CallExpr:
236238
if n.Ellipsis.IsValid() {
237239
tv.token(n.Ellipsis, len("..."), semtok.TokOperator, nil)
@@ -303,6 +305,7 @@ func (tv *tokenVisitor) inspect(n ast.Node) (descend bool) {
303305
tv.token(n.Interface, len("interface"), semtok.TokKeyword, nil)
304306
case *ast.KeyValueExpr:
305307
case *ast.LabeledStmt:
308+
tv.token(n.Label.Pos(), len(n.Label.Name), semtok.TokLabel, []string{"definition"})
306309
case *ast.MapType:
307310
tv.token(n.Map, len("map"), semtok.TokKeyword, nil)
308311
case *ast.ParenExpr:
@@ -411,7 +414,7 @@ func (tv *tokenVisitor) ident(id *ast.Ident) {
411414
case *types.Func:
412415
emit(semtok.TokFunction)
413416
case *types.Label:
414-
// nothing to map it to
417+
// Labels are reliably covered by the syntax traversal.
415418
case *types.Nil:
416419
// nil is a predeclared identifier
417420
emit(semtok.TokVariable, "readonly", "defaultLibrary")
@@ -571,8 +574,14 @@ func (tv *tokenVisitor) unkIdent(id *ast.Ident) (semtok.TokenType, []string) {
571574
return semtok.TokMethod, def
572575
}
573576
return semtok.TokVariable, nil
574-
case *ast.LabeledStmt, *ast.BranchStmt:
575-
// nothing to report
577+
case *ast.LabeledStmt:
578+
if id == parent.Label {
579+
return semtok.TokLabel, def
580+
}
581+
case *ast.BranchStmt:
582+
if id == parent.Label {
583+
return semtok.TokLabel, nil
584+
}
576585
case *ast.CompositeLit:
577586
if parent.Type == id {
578587
return semtok.TokType, nil
@@ -604,7 +613,17 @@ func isDeprecated(n *ast.CommentGroup) bool {
604613

605614
// definitionFor handles a defining identifier.
606615
func (tv *tokenVisitor) definitionFor(id *ast.Ident, obj types.Object) (semtok.TokenType, []string) {
607-
// PJW: obj == types.Label? probably a nothing
616+
// The definition of a types.Label cannot be found by
617+
// ascending the syntax tree, and doing so will reach the
618+
// FuncDecl, causing us to misinterpret the label as a
619+
// parameter (#65494).
620+
//
621+
// However, labels are reliably covered by the syntax
622+
// traversal, so we don't need to use type information.
623+
if is[*types.Label](obj) {
624+
return "", nil
625+
}
626+
608627
// PJW: look into replacing these syntactic tests with types more generally
609628
modifiers := []string{"definition"}
610629
for i := len(tv.stack) - 1; i >= 0; i-- {

gopls/internal/protocol/semantic.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ package protocol
88

99
import "fmt"
1010

11-
// SemanticTypes to use in case there is no client, as in the command line, or tests
11+
// SemanticTypes to use in case there is no client, as in the command line, or tests.
1212
func SemanticTypes() []string {
1313
return semanticTypes[:]
1414
}

gopls/internal/protocol/semtok/semtok.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ type Token struct {
1818
type TokenType string
1919

2020
const (
21+
// These are the tokens defined by LSP 3.17, but a client is
22+
// free to send its own set; any tokens that the server emits
23+
// that are not in this set are simply not encoded in the bitfield.
2124
TokNamespace TokenType = "namespace"
2225
TokType TokenType = "type"
2326
TokInterface TokenType = "interface"
@@ -32,6 +35,10 @@ const (
3235
TokNumber TokenType = "number"
3336
TokOperator TokenType = "operator"
3437
TokMacro TokenType = "macro" // for templates
38+
39+
// not part of LSP 3.17 (even though JS has labels)
40+
// https://github.com/microsoft/vscode-languageserver-node/issues/1422
41+
TokLabel TokenType = "label"
3542
)
3643

3744
// Encode returns the LSP encoding of a sequence of tokens.

gopls/internal/test/integration/fake/editor.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,8 @@ func clientCapabilities(cfg EditorConfig) (protocol.ClientCapabilities, error) {
337337
"struct", "typeParameter", "parameter", "variable", "property", "enumMember",
338338
"event", "function", "method", "macro", "keyword", "modifier", "comment",
339339
"string", "number", "regexp", "operator",
340+
// Additional types supported by this client:
341+
"label",
340342
}
341343
capabilities.TextDocument.SemanticTokens.TokenModifiers = []string{
342344
"declaration", "definition", "readonly", "static",

gopls/internal/test/marker/testdata/token/range.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,13 @@ func F() { //@token("F", "function", "definition")
1717
_ = x //@token("x", "variable", "")
1818
_ = F //@token("F", "function", "")
1919
}
20+
21+
func _() {
22+
// A goto's label cannot be found by ascending the syntax tree.
23+
goto loop //@ token("goto", "keyword", ""), token("loop", "label", "")
24+
25+
loop: //@token("loop", "label", "definition")
26+
for {
27+
continue loop //@ token("continue", "keyword", ""), token("loop", "label", "")
28+
}
29+
}

0 commit comments

Comments
 (0)