mirror of https://github.com/golang/go.git
290 lines
6.8 KiB
Go
290 lines
6.8 KiB
Go
// Copyright 2020 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 (
|
|
"path"
|
|
"strings"
|
|
"testing"
|
|
|
|
"golang.org/x/tools/gopls/internal/lsp/protocol"
|
|
. "golang.org/x/tools/gopls/internal/lsp/regtest"
|
|
"golang.org/x/tools/gopls/internal/lsp/tests/compare"
|
|
"golang.org/x/tools/internal/testenv"
|
|
|
|
"golang.org/x/tools/gopls/internal/lsp/fake"
|
|
)
|
|
|
|
const internalDefinition = `
|
|
-- go.mod --
|
|
module mod.com
|
|
|
|
go 1.12
|
|
-- main.go --
|
|
package main
|
|
|
|
import "fmt"
|
|
|
|
func main() {
|
|
fmt.Println(message)
|
|
}
|
|
-- const.go --
|
|
package main
|
|
|
|
const message = "Hello World."
|
|
`
|
|
|
|
func TestGoToInternalDefinition(t *testing.T) {
|
|
Run(t, internalDefinition, func(t *testing.T, env *Env) {
|
|
env.OpenFile("main.go")
|
|
name, pos := env.GoToDefinition("main.go", env.RegexpSearch("main.go", "message"))
|
|
if want := "const.go"; name != want {
|
|
t.Errorf("GoToDefinition: got file %q, want %q", name, want)
|
|
}
|
|
if want := env.RegexpSearch("const.go", "message"); pos != want {
|
|
t.Errorf("GoToDefinition: got position %v, want %v", pos, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
const stdlibDefinition = `
|
|
-- go.mod --
|
|
module mod.com
|
|
|
|
go 1.12
|
|
-- main.go --
|
|
package main
|
|
|
|
import "fmt"
|
|
|
|
func main() {
|
|
fmt.Printf()
|
|
}`
|
|
|
|
func TestGoToStdlibDefinition_Issue37045(t *testing.T) {
|
|
Run(t, stdlibDefinition, func(t *testing.T, env *Env) {
|
|
env.OpenFile("main.go")
|
|
name, pos := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `fmt.(Printf)`))
|
|
if got, want := path.Base(name), "print.go"; got != want {
|
|
t.Errorf("GoToDefinition: got file %q, want %q", name, want)
|
|
}
|
|
|
|
// Test that we can jump to definition from outside our workspace.
|
|
// See golang.org/issues/37045.
|
|
newName, newPos := env.GoToDefinition(name, pos)
|
|
if newName != name {
|
|
t.Errorf("GoToDefinition is not idempotent: got %q, want %q", newName, name)
|
|
}
|
|
if newPos != pos {
|
|
t.Errorf("GoToDefinition is not idempotent: got %v, want %v", newPos, pos)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestUnexportedStdlib_Issue40809(t *testing.T) {
|
|
Run(t, stdlibDefinition, func(t *testing.T, env *Env) {
|
|
env.OpenFile("main.go")
|
|
name, _ := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `fmt.(Printf)`))
|
|
env.OpenFile(name)
|
|
|
|
pos := env.RegexpSearch(name, `:=\s*(newPrinter)\(\)`)
|
|
|
|
// Check that we can find references on a reference
|
|
refs := env.References(name, pos)
|
|
if len(refs) < 5 {
|
|
t.Errorf("expected 5+ references to newPrinter, found: %#v", refs)
|
|
}
|
|
|
|
name, pos = env.GoToDefinition(name, pos)
|
|
content, _ := env.Hover(name, pos)
|
|
if !strings.Contains(content.Value, "newPrinter") {
|
|
t.Fatal("definition of newPrinter went to the incorrect place")
|
|
}
|
|
// And on the definition too.
|
|
refs = env.References(name, pos)
|
|
if len(refs) < 5 {
|
|
t.Errorf("expected 5+ references to newPrinter, found: %#v", refs)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Test the hover on an error's Error function.
|
|
// This can't be done via the marker tests because Error is a builtin.
|
|
func TestHoverOnError(t *testing.T) {
|
|
const mod = `
|
|
-- go.mod --
|
|
module mod.com
|
|
|
|
go 1.12
|
|
-- main.go --
|
|
package main
|
|
|
|
func main() {
|
|
var err error
|
|
err.Error()
|
|
}`
|
|
Run(t, mod, func(t *testing.T, env *Env) {
|
|
env.OpenFile("main.go")
|
|
content, _ := env.Hover("main.go", env.RegexpSearch("main.go", "Error"))
|
|
if content == nil {
|
|
t.Fatalf("nil hover content for Error")
|
|
}
|
|
want := "```go\nfunc (error).Error() string\n```"
|
|
if content.Value != want {
|
|
t.Fatalf("hover failed:\n%s", compare.Text(want, content.Value))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestImportShortcut(t *testing.T) {
|
|
const mod = `
|
|
-- go.mod --
|
|
module mod.com
|
|
|
|
go 1.12
|
|
-- main.go --
|
|
package main
|
|
|
|
import "fmt"
|
|
|
|
func main() {}
|
|
`
|
|
for _, tt := range []struct {
|
|
wantLinks int
|
|
wantDef bool
|
|
importShortcut string
|
|
}{
|
|
{1, false, "Link"},
|
|
{0, true, "Definition"},
|
|
{1, true, "Both"},
|
|
} {
|
|
t.Run(tt.importShortcut, func(t *testing.T) {
|
|
WithOptions(
|
|
Settings{"importShortcut": tt.importShortcut},
|
|
).Run(t, mod, func(t *testing.T, env *Env) {
|
|
env.OpenFile("main.go")
|
|
file, pos := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `"fmt"`))
|
|
if !tt.wantDef && (file != "" || pos != (fake.Pos{})) {
|
|
t.Fatalf("expected no definition, got one: %s:%v", file, pos)
|
|
} else if tt.wantDef && file == "" && pos == (fake.Pos{}) {
|
|
t.Fatalf("expected definition, got none")
|
|
}
|
|
links := env.DocumentLink("main.go")
|
|
if len(links) != tt.wantLinks {
|
|
t.Fatalf("expected %v links, got %v", tt.wantLinks, len(links))
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGoToTypeDefinition_Issue38589(t *testing.T) {
|
|
const mod = `
|
|
-- go.mod --
|
|
module mod.com
|
|
|
|
go 1.12
|
|
-- main.go --
|
|
package main
|
|
|
|
type Int int
|
|
|
|
type Struct struct{}
|
|
|
|
func F1() {}
|
|
func F2() (int, error) { return 0, nil }
|
|
func F3() (**Struct, bool, *Int, error) { return nil, false, nil, nil }
|
|
func F4() (**Struct, bool, *float64, error) { return nil, false, nil, nil }
|
|
|
|
func main() {}
|
|
`
|
|
|
|
for _, tt := range []struct {
|
|
re string
|
|
wantError bool
|
|
wantTypeRe string
|
|
}{
|
|
{re: `F1`, wantError: true},
|
|
{re: `F2`, wantError: true},
|
|
{re: `F3`, wantError: true},
|
|
{re: `F4`, wantError: false, wantTypeRe: `type (Struct)`},
|
|
} {
|
|
t.Run(tt.re, func(t *testing.T) {
|
|
Run(t, mod, func(t *testing.T, env *Env) {
|
|
env.OpenFile("main.go")
|
|
|
|
_, pos, err := env.Editor.GoToTypeDefinition(env.Ctx, "main.go", env.RegexpSearch("main.go", tt.re))
|
|
if tt.wantError {
|
|
if err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
return
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("expected nil error, got %s", err)
|
|
}
|
|
|
|
typePos := env.RegexpSearch("main.go", tt.wantTypeRe)
|
|
if pos != typePos {
|
|
t.Errorf("invalid pos: want %+v, got %+v", typePos, pos)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
// Test for golang/go#47825.
|
|
func TestImportTestVariant(t *testing.T) {
|
|
testenv.NeedsGo1Point(t, 13)
|
|
|
|
const mod = `
|
|
-- go.mod --
|
|
module mod.com
|
|
|
|
go 1.12
|
|
-- client/test/role.go --
|
|
package test
|
|
|
|
import _ "mod.com/client"
|
|
|
|
type RoleSetup struct{}
|
|
-- client/client_role_test.go --
|
|
package client_test
|
|
|
|
import (
|
|
"testing"
|
|
_ "mod.com/client"
|
|
ctest "mod.com/client/test"
|
|
)
|
|
|
|
func TestClient(t *testing.T) {
|
|
_ = ctest.RoleSetup{}
|
|
}
|
|
-- client/client_test.go --
|
|
package client
|
|
|
|
import "testing"
|
|
|
|
func TestClient(t *testing.T) {}
|
|
-- client.go --
|
|
package client
|
|
`
|
|
Run(t, mod, func(t *testing.T, env *Env) {
|
|
env.OpenFile("client/client_role_test.go")
|
|
env.GoToDefinition("client/client_role_test.go", env.RegexpSearch("client/client_role_test.go", "RoleSetup"))
|
|
})
|
|
}
|
|
|
|
// This test exercises a crashing pattern from golang/go#49223.
|
|
func TestGoToCrashingDefinition_Issue49223(t *testing.T) {
|
|
Run(t, "", func(t *testing.T, env *Env) {
|
|
params := &protocol.DefinitionParams{}
|
|
params.TextDocument.URI = protocol.DocumentURI("fugitive%3A///Users/user/src/mm/ems/.git//0/pkg/domain/treasury/provider.go")
|
|
params.Position.Character = 18
|
|
params.Position.Line = 0
|
|
env.Editor.Server.Definition(env.Ctx, params)
|
|
})
|
|
}
|