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.
12 "golang.org/x/tools/internal/lsp"
13 "golang.org/x/tools/internal/lsp/fake"
14 "golang.org/x/tools/internal/testenv"
17 const workspaceProxy = `
18 -- example.com@v1.2.3/go.mod --
22 -- example.com@v1.2.3/blah/blah.go --
26 fmt.Println("something")
28 -- random.org@v1.2.3/go.mod --
32 -- random.org@v1.2.3/bye/bye.go --
40 // TODO: Add a replace directive.
41 const workspaceModule = `
73 -- pkg/inner/inner.go --
76 import "example.com/blah"
81 -- goodbye/bye/bye.go --
91 // Confirm that find references returns all of the references in the module,
92 // regardless of what the workspace root is.
93 func TestReferences(t *testing.T) {
94 for _, tt := range []struct {
102 name: "subdirectory",
103 rootPath: "pkg/inner",
106 t.Run(tt.name, func(t *testing.T) {
107 opts := []RunOption{WithProxyFiles(workspaceProxy)}
108 if tt.rootPath != "" {
109 opts = append(opts, WithRootPath(tt.rootPath))
111 withOptions(opts...).run(t, workspaceModule, func(t *testing.T, env *Env) {
112 f := "pkg/inner/inner.go"
114 locations := env.References(f, env.RegexpSearch(f, `SaySomething`))
116 if got := len(locations); got != want {
117 t.Fatalf("expected %v locations, got %v", want, got)
124 // Make sure that analysis diagnostics are cleared for the whole package when
125 // the only opened file is closed. This test was inspired by the experience in
126 // VS Code, where clicking on a reference result triggers a
127 // textDocument/didOpen without a corresponding textDocument/didClose.
128 func TestClearAnalysisDiagnostics(t *testing.T) {
129 withOptions(WithProxyFiles(workspaceProxy), WithRootPath("pkg/inner")).run(t, workspaceModule, func(t *testing.T, env *Env) {
130 env.OpenFile("pkg/main.go")
132 env.DiagnosticAtRegexp("pkg/main2.go", "fmt.Print"),
134 env.CloseBuffer("pkg/main.go")
136 EmptyDiagnostics("pkg/main2.go"),
141 // This test checks that gopls updates the set of files it watches when a
142 // replace target is added to the go.mod.
143 func TestWatchReplaceTargets(t *testing.T) {
144 withOptions(WithProxyFiles(workspaceProxy), WithRootPath("pkg")).run(t, workspaceModule, func(t *testing.T, env *Env) {
145 // Add a replace directive and expect the files that gopls is watching
147 dir := env.Sandbox.Workdir.URI("goodbye").SpanURI().Filename()
148 goModWithReplace := fmt.Sprintf(`%s
149 replace random.org => %s
150 `, env.ReadWorkspaceFile("pkg/go.mod"), dir)
151 env.WriteWorkspaceFile("pkg/go.mod", goModWithReplace)
153 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
154 UnregistrationMatching("didChangeWatchedFiles"),
155 RegistrationMatching("didChangeWatchedFiles"),
160 const workspaceModuleProxy = `
161 -- b.com@v1.2.3/go.mod --
165 -- b.com@v1.2.3/b/b.go --
171 func TestAutomaticWorkspaceModule_Interdependent(t *testing.T) {
172 const multiModule = `
200 WithProxyFiles(workspaceModuleProxy),
201 WithModes(Experimental),
202 ).run(t, multiModule, func(t *testing.T, env *Env) {
204 env.DiagnosticAtRegexp("moda/a/a.go", "x"),
205 env.DiagnosticAtRegexp("modb/b/b.go", "x"),
206 env.NoDiagnosticAtRegexp("moda/a/a.go", `"b.com/b"`),
211 // This change tests that the version of the module used changes after it has
212 // been deleted from the workspace.
213 func TestDeleteModule_Interdependent(t *testing.T) {
214 const multiModule = `
242 WithProxyFiles(workspaceModuleProxy),
243 WithModes(Experimental),
244 ).run(t, multiModule, func(t *testing.T, env *Env) {
245 env.OpenFile("moda/a/a.go")
247 original, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
248 if want := "modb/b/b.go"; !strings.HasSuffix(original, want) {
249 t.Errorf("expected %s, got %v", want, original)
251 env.CloseBuffer(original)
252 env.RemoveWorkspaceFile("modb/b/b.go")
253 env.RemoveWorkspaceFile("modb/go.mod")
255 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 2),
257 got, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
258 if want := "b.com@v1.2.3/b/b.go"; !strings.HasSuffix(got, want) {
259 t.Errorf("expected %s, got %v", want, got)
264 // Tests that the version of the module used changes after it has been added
266 func TestCreateModule_Interdependent(t *testing.T) {
267 const multiModule = `
286 WithModes(Experimental),
287 WithProxyFiles(workspaceModuleProxy),
288 ).run(t, multiModule, func(t *testing.T, env *Env) {
289 env.OpenFile("moda/a/a.go")
290 original, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
291 if want := "b.com@v1.2.3/b/b.go"; !strings.HasSuffix(original, want) {
292 t.Errorf("expected %s, got %v", want, original)
294 env.CloseBuffer(original)
295 env.WriteWorkspaceFiles(map[string]string{
296 "modb/go.mod": "module b.com",
297 "modb/b/b.go": `package b
306 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
307 env.DiagnosticAtRegexp("modb/b/b.go", "x"),
310 got, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
311 if want := "modb/b/b.go"; !strings.HasSuffix(got, want) {
312 t.Errorf("expected %s, got %v", want, original)
317 // This test confirms that a gopls workspace can recover from initialization
318 // with one invalid module.
319 func TestOneBrokenModule(t *testing.T) {
320 const multiModule = `
338 modul b.com // typo here
348 WithProxyFiles(workspaceModuleProxy),
349 WithModes(Experimental),
350 ).run(t, multiModule, func(t *testing.T, env *Env) {
351 env.OpenFile("modb/go.mod")
354 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1),
355 DiagnosticAt("modb/go.mod", 0, 0),
358 env.RegexpReplace("modb/go.mod", "modul", "module")
359 env.Editor.SaveBufferWithoutActions(env.Ctx, "modb/go.mod")
361 env.DiagnosticAtRegexp("modb/b/b.go", "x"),
366 func TestUseGoplsMod(t *testing.T) {
367 const multiModule = `
394 module gopls-workspace
397 a.com v0.0.0-goplsworkspace
401 replace a.com => $SANDBOX_WORKDIR/moda/a
404 WithProxyFiles(workspaceModuleProxy),
405 WithModes(Experimental),
406 ).run(t, multiModule, func(t *testing.T, env *Env) {
407 env.OpenFile("moda/a/a.go")
408 original, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
409 if want := "b.com@v1.2.3/b/b.go"; !strings.HasSuffix(original, want) {
410 t.Errorf("expected %s, got %v", want, original)
412 workdir := env.Sandbox.Workdir.RootURI().SpanURI().Filename()
413 env.WriteWorkspaceFile("gopls.mod", fmt.Sprintf(`module gopls-workspace
416 a.com v0.0.0-goplsworkspace
417 b.com v0.0.0-goplsworkspace
420 replace a.com => %s/moda/a
421 replace b.com => %s/modb
422 `, workdir, workdir))
425 CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1),
426 env.DiagnosticAtRegexp("modb/b/b.go", "x"),
429 newLocation, _ := env.GoToDefinition("moda/a/a.go", env.RegexpSearch("moda/a/a.go", "Hello"))
430 if want := "modb/b/b.go"; !strings.HasSuffix(newLocation, want) {
431 t.Errorf("expected %s, got %v", want, newLocation)
436 func TestNonWorkspaceFileCreation(t *testing.T) {
437 testenv.NeedsGo1Point(t, 13)
452 run(t, files, func(t *testing.T, env *Env) {
453 env.CreateBuffer("/tmp/foo.go", "")
454 env.EditBuffer("/tmp/foo.go", fake.NewEdit(0, 0, 0, 0, code))
455 env.GoToDefinition("/tmp/foo.go", env.RegexpSearch("/tmp/foo.go", `Printf`))
459 func TestMultiModuleV2(t *testing.T) {
460 const multiModule = `
464 require b.com/v2 v2.0.0
495 module gopkg.in/yaml.v1 // test gopkg.in versions
504 WithModes(Experimental),
505 ).run(t, multiModule, func(t *testing.T, env *Env) {
507 env.DiagnosticAtRegexp("moda/a/a.go", "x"),
508 env.DiagnosticAtRegexp("modb/b/b.go", "x"),
509 env.DiagnosticAtRegexp("modb/v2/b/b.go", "x"),
510 env.DiagnosticAtRegexp("modc/main.go", "x"),