1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package regtest
6
7import (
8	"path"
9	"strings"
10	"testing"
11
12	"golang.org/x/tools/internal/lsp/tests"
13)
14
15const internalDefinition = `
16-- go.mod --
17module mod.com
18
19go 1.12
20-- main.go --
21package main
22
23import "fmt"
24
25func main() {
26	fmt.Println(message)
27}
28-- const.go --
29package main
30
31const message = "Hello World."
32`
33
34func TestGoToInternalDefinition(t *testing.T) {
35	runner.Run(t, internalDefinition, func(t *testing.T, env *Env) {
36		env.OpenFile("main.go")
37		name, pos := env.GoToDefinition("main.go", env.RegexpSearch("main.go", "message"))
38		if want := "const.go"; name != want {
39			t.Errorf("GoToDefinition: got file %q, want %q", name, want)
40		}
41		if want := env.RegexpSearch("const.go", "message"); pos != want {
42			t.Errorf("GoToDefinition: got position %v, want %v", pos, want)
43		}
44	})
45}
46
47const stdlibDefinition = `
48-- go.mod --
49module mod.com
50
51go 1.12
52-- main.go --
53package main
54
55import "fmt"
56
57func main() {
58	fmt.Printf()
59}`
60
61func TestGoToStdlibDefinition_Issue37045(t *testing.T) {
62	runner.Run(t, stdlibDefinition, func(t *testing.T, env *Env) {
63		env.OpenFile("main.go")
64		name, pos := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `fmt.(Printf)`))
65		if got, want := path.Base(name), "print.go"; got != want {
66			t.Errorf("GoToDefinition: got file %q, want %q", name, want)
67		}
68
69		// Test that we can jump to definition from outside our workspace.
70		// See golang.org/issues/37045.
71		newName, newPos := env.GoToDefinition(name, pos)
72		if newName != name {
73			t.Errorf("GoToDefinition is not idempotent: got %q, want %q", newName, name)
74		}
75		if newPos != pos {
76			t.Errorf("GoToDefinition is not idempotent: got %v, want %v", newPos, pos)
77		}
78	})
79}
80
81func TestUnexportedStdlib_Issue40809(t *testing.T) {
82	runner.Run(t, stdlibDefinition, func(t *testing.T, env *Env) {
83		env.OpenFile("main.go")
84		name, _ := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `fmt.(Printf)`))
85		env.OpenFile(name)
86
87		pos := env.RegexpSearch(name, `:=\s*(newPrinter)\(\)`)
88
89		// Check that we can find references on a reference
90		refs := env.References(name, pos)
91		if len(refs) < 5 {
92			t.Errorf("expected 5+ references to newPrinter, found: %#v", refs)
93		}
94
95		name, pos = env.GoToDefinition(name, pos)
96		content, _ := env.Hover(name, pos)
97		if !strings.Contains(content.Value, "newPrinter") {
98			t.Fatal("definition of newPrinter went to the incorrect place")
99		}
100		// And on the definition too.
101		refs = env.References(name, pos)
102		if len(refs) < 5 {
103			t.Errorf("expected 5+ references to newPrinter, found: %#v", refs)
104		}
105	})
106}
107
108// Test the hover on an error's Error function.
109// This can't be done via the marker tests because Error is a builtin.
110func TestHoverOnError(t *testing.T) {
111	const mod = `
112-- go.mod --
113module mod.com
114
115go 1.12
116-- main.go --
117package main
118
119func main() {
120	var err error
121	err.Error()
122}`
123	run(t, mod, func(t *testing.T, env *Env) {
124		env.OpenFile("main.go")
125		content, _ := env.Hover("main.go", env.RegexpSearch("main.go", "Error"))
126		if content == nil {
127			t.Fatalf("nil hover content for Error")
128		}
129		want := "```go\nfunc (error).Error() string\n```"
130		if content.Value != want {
131			t.Fatalf("hover failed:\n%s", tests.Diff(t, want, content.Value))
132		}
133	})
134}
135