blob: a96024b9ca2765b54da3c2e9dccfa6def9988bbc [file] [log] [blame]
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package misc
import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"golang.org/x/tools/gopls/internal/lsp"
"golang.org/x/tools/gopls/internal/lsp/protocol"
. "golang.org/x/tools/gopls/internal/lsp/regtest"
"golang.org/x/tools/internal/typeparams"
)
func TestBadURICrash_VSCodeIssue1498(t *testing.T) {
const src = `
-- go.mod --
module example.com
go 1.12
-- main.go --
package main
func main() {}
`
WithOptions(
Modes(Default),
Settings{"allExperiments": true},
).Run(t, src, func(t *testing.T, env *Env) {
params := &protocol.SemanticTokensParams{}
const badURI = "http://foo"
params.TextDocument.URI = badURI
// This call panicked in the past: golang/vscode-go#1498.
if _, err := env.Editor.Server.SemanticTokensFull(env.Ctx, params); err != nil {
// Requests to an invalid URI scheme shouldn't result in an error, we
// simply don't support this so return empty result. This could be
// changed, but for now assert on the current behavior.
t.Errorf("SemanticTokensFull(%q): %v", badURI, err)
}
})
}
// fix bug involving type parameters and regular parameters
// (golang/vscode-go#2527)
func TestSemantic_2527(t *testing.T) {
if !typeparams.Enabled {
t.Skip("type parameters are needed for this test")
}
// these are the expected types of identifiers in text order
want := []result{
{"package", "keyword", ""},
{"foo", "namespace", ""},
{"func", "keyword", ""},
{"Add", "function", "definition deprecated"},
{"T", "typeParameter", "definition"},
{"int", "type", "defaultLibrary"},
{"target", "parameter", "definition"},
{"T", "typeParameter", ""},
{"l", "parameter", "definition"},
{"T", "typeParameter", ""},
{"T", "typeParameter", ""},
{"return", "keyword", ""},
{"append", "function", "defaultLibrary"},
{"l", "parameter", ""},
{"target", "parameter", ""},
{"for", "keyword", ""},
{"range", "keyword", ""},
{"l", "parameter", ""},
{"return", "keyword", ""},
{"nil", "variable", "readonly defaultLibrary"},
}
src := `
-- go.mod --
module example.com
go 1.19
-- main.go --
package foo
// Deprecated (for testing)
func Add[T int](target T, l []T) []T {
return append(l, target)
for range l {} // test coverage
return nil
}
`
WithOptions(
Modes(Default),
Settings{"semanticTokens": true},
).Run(t, src, func(t *testing.T, env *Env) {
env.OpenFile("main.go")
env.AfterChange(
Diagnostics(env.AtRegexp("main.go", "for range")),
)
p := &protocol.SemanticTokensParams{
TextDocument: protocol.TextDocumentIdentifier{
URI: env.Sandbox.Workdir.URI("main.go"),
},
}
v, err := env.Editor.Server.SemanticTokensFull(env.Ctx, p)
if err != nil {
t.Fatal(err)
}
seen := interpret(v.Data, env.BufferText("main.go"))
if x := cmp.Diff(want, seen); x != "" {
t.Errorf("Semantic tokens do not match (-want +got):\n%s", x)
}
})
}
// fix inconsistency in TypeParameters
// https://github.com/golang/go/issues/57619
func TestSemantic_57619(t *testing.T) {
if !typeparams.Enabled {
t.Skip("type parameters are needed for this test")
}
src := `
-- go.mod --
module example.com
go 1.19
-- main.go --
package foo
type Smap[K int, V any] struct {
Store map[K]V
}
func (s *Smap[K, V]) Get(k K) (V, bool) {
v, ok := s.Store[k]
return v, ok
}
func New[K int, V any]() Smap[K, V] {
return Smap[K, V]{Store: make(map[K]V)}
}
`
WithOptions(
Modes(Default),
Settings{"semanticTokens": true},
).Run(t, src, func(t *testing.T, env *Env) {
env.OpenFile("main.go")
p := &protocol.SemanticTokensParams{
TextDocument: protocol.TextDocumentIdentifier{
URI: env.Sandbox.Workdir.URI("main.go"),
},
}
v, err := env.Editor.Server.SemanticTokensFull(env.Ctx, p)
if err != nil {
t.Fatal(err)
}
seen := interpret(v.Data, env.BufferText("main.go"))
for i, s := range seen {
if (s.Token == "K" || s.Token == "V") && s.TokenType != "typeParameter" {
t.Errorf("%d: expected K and V to be type parameters, but got %v", i, s)
}
}
})
}
type result struct {
Token string
TokenType string
Mod string
}
// human-readable version of the semantic tokens
// comment, string, number are elided
// (and in the future, maybe elide other things, like operators)
func interpret(x []uint32, contents string) []result {
lines := strings.Split(contents, "\n")
ans := []result{}
line, col := 1, 1
for i := 0; i < len(x); i += 5 {
line += int(x[i])
col += int(x[i+1])
if x[i] != 0 { // new line
col = int(x[i+1]) + 1 // 1-based column numbers
}
sz := x[i+2]
t := semanticTypes[x[i+3]]
if t == "comment" || t == "string" || t == "number" {
continue
}
l := x[i+4]
var mods []string
for i, mod := range semanticModifiers {
if l&(1<<i) != 0 {
mods = append(mods, mod)
}
}
// col is a utf-8 offset
tok := lines[line-1][col-1 : col-1+int(sz)]
ans = append(ans, result{tok, t, strings.Join(mods, " ")})
}
return ans
}
var (
semanticTypes = lsp.SemanticTypes()
semanticModifiers = lsp.SemanticModifiers()
)