.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools / gopls@v0.6.9 / internal / regtest / modfile / modfile_test.go
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
5 package modfile
6
7 import (
8         "path/filepath"
9         "strings"
10         "testing"
11
12         . "golang.org/x/tools/gopls/internal/regtest"
13
14         "golang.org/x/tools/internal/lsp/protocol"
15         "golang.org/x/tools/internal/lsp/tests"
16         "golang.org/x/tools/internal/testenv"
17 )
18
19 func TestMain(m *testing.M) {
20         Main(m)
21 }
22
23 const workspaceProxy = `
24 -- example.com@v1.2.3/go.mod --
25 module example.com
26
27 go 1.12
28 -- example.com@v1.2.3/blah/blah.go --
29 package blah
30
31 func SaySomething() {
32         fmt.Println("something")
33 }
34 -- random.org@v1.2.3/go.mod --
35 module random.org
36
37 go 1.12
38 -- random.org@v1.2.3/bye/bye.go --
39 package bye
40
41 func Goodbye() {
42         println("Bye")
43 }
44 `
45
46 const proxy = `
47 -- example.com@v1.2.3/go.mod --
48 module example.com
49
50 go 1.12
51 -- example.com@v1.2.3/blah/blah.go --
52 package blah
53
54 const Name = "Blah"
55 -- random.org@v1.2.3/go.mod --
56 module random.org
57
58 go 1.12
59 -- random.org@v1.2.3/blah/blah.go --
60 package hello
61
62 const Name = "Hello"
63 `
64
65 func TestModFileModification(t *testing.T) {
66         testenv.NeedsGo1Point(t, 14)
67
68         const untidyModule = `
69 -- a/go.mod --
70 module mod.com
71
72 -- a/main.go --
73 package main
74
75 import "example.com/blah"
76
77 func main() {
78         println(blah.Name)
79 }
80 `
81
82         runner := RunMultiple{
83                 {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
84                 {"nested", WithOptions(ProxyFiles(proxy))},
85         }
86
87         t.Run("basic", func(t *testing.T) {
88                 runner.Run(t, untidyModule, func(t *testing.T, env *Env) {
89                         // Open the file and make sure that the initial workspace load does not
90                         // modify the go.mod file.
91                         goModContent := env.ReadWorkspaceFile("a/go.mod")
92                         env.OpenFile("a/main.go")
93                         env.Await(
94                                 env.DiagnosticAtRegexp("a/main.go", "\"example.com/blah\""),
95                         )
96                         if got := env.ReadWorkspaceFile("a/go.mod"); got != goModContent {
97                                 t.Fatalf("go.mod changed on disk:\n%s", tests.Diff(t, goModContent, got))
98                         }
99                         // Save the buffer, which will format and organize imports.
100                         // Confirm that the go.mod file still does not change.
101                         env.SaveBuffer("a/main.go")
102                         env.Await(
103                                 env.DiagnosticAtRegexp("a/main.go", "\"example.com/blah\""),
104                         )
105                         if got := env.ReadWorkspaceFile("a/go.mod"); got != goModContent {
106                                 t.Fatalf("go.mod changed on disk:\n%s", tests.Diff(t, goModContent, got))
107                         }
108                 })
109         })
110
111         // Reproduce golang/go#40269 by deleting and recreating main.go.
112         t.Run("delete main.go", func(t *testing.T) {
113                 t.Skip("This test will be flaky until golang/go#40269 is resolved.")
114
115                 runner.Run(t, untidyModule, func(t *testing.T, env *Env) {
116                         goModContent := env.ReadWorkspaceFile("a/go.mod")
117                         mainContent := env.ReadWorkspaceFile("a/main.go")
118                         env.OpenFile("a/main.go")
119                         env.SaveBuffer("a/main.go")
120
121                         env.RemoveWorkspaceFile("a/main.go")
122                         env.Await(
123                                 env.DoneWithOpen(),
124                                 env.DoneWithSave(),
125                                 env.DoneWithChangeWatchedFiles(),
126                         )
127
128                         env.WriteWorkspaceFile("main.go", mainContent)
129                         env.Await(
130                                 env.DiagnosticAtRegexp("main.go", "\"example.com/blah\""),
131                         )
132                         if got := env.ReadWorkspaceFile("go.mod"); got != goModContent {
133                                 t.Fatalf("go.mod changed on disk:\n%s", tests.Diff(t, goModContent, got))
134                         }
135                 })
136         })
137 }
138
139 func TestGoGetFix(t *testing.T) {
140         testenv.NeedsGo1Point(t, 14)
141         const mod = `
142 -- a/go.mod --
143 module mod.com
144
145 go 1.12
146
147 -- a/main.go --
148 package main
149
150 import "example.com/blah"
151
152 var _ = blah.Name
153 `
154
155         const want = `module mod.com
156
157 go 1.12
158
159 require example.com v1.2.3
160 `
161
162         RunMultiple{
163                 {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
164                 {"nested", WithOptions(ProxyFiles(proxy))},
165         }.Run(t, mod, func(t *testing.T, env *Env) {
166                 if strings.Contains(t.Name(), "workspace_module") {
167                         t.Skip("workspace module mode doesn't set -mod=readonly")
168                 }
169                 env.OpenFile("a/main.go")
170                 var d protocol.PublishDiagnosticsParams
171                 env.Await(
172                         OnceMet(
173                                 env.DiagnosticAtRegexp("a/main.go", `"example.com/blah"`),
174                                 ReadDiagnostics("a/main.go", &d),
175                         ),
176                 )
177                 var goGetDiag protocol.Diagnostic
178                 for _, diag := range d.Diagnostics {
179                         if strings.Contains(diag.Message, "could not import") {
180                                 goGetDiag = diag
181                         }
182                 }
183                 env.ApplyQuickFixes("a/main.go", []protocol.Diagnostic{goGetDiag})
184                 if got := env.ReadWorkspaceFile("a/go.mod"); got != want {
185                         t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got))
186                 }
187         })
188 }
189
190 // Tests that multiple missing dependencies gives good single fixes.
191 func TestMissingDependencyFixes(t *testing.T) {
192         testenv.NeedsGo1Point(t, 14)
193         const mod = `
194 -- a/go.mod --
195 module mod.com
196
197 go 1.12
198
199 -- a/main.go --
200 package main
201
202 import "example.com/blah"
203 import "random.org/blah"
204
205 var _, _ = blah.Name, hello.Name
206 `
207
208         const want = `module mod.com
209
210 go 1.12
211
212 require random.org v1.2.3
213 `
214
215         RunMultiple{
216                 {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
217                 {"nested", WithOptions(ProxyFiles(proxy))},
218         }.Run(t, mod, func(t *testing.T, env *Env) {
219                 env.OpenFile("a/main.go")
220                 var d protocol.PublishDiagnosticsParams
221                 env.Await(
222                         OnceMet(
223                                 env.DiagnosticAtRegexp("a/main.go", `"random.org/blah"`),
224                                 ReadDiagnostics("a/main.go", &d),
225                         ),
226                 )
227                 var randomDiag protocol.Diagnostic
228                 for _, diag := range d.Diagnostics {
229                         if strings.Contains(diag.Message, "random.org") {
230                                 randomDiag = diag
231                         }
232                 }
233                 env.ApplyQuickFixes("a/main.go", []protocol.Diagnostic{randomDiag})
234                 if got := env.ReadWorkspaceFile("a/go.mod"); got != want {
235                         t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got))
236                 }
237         })
238 }
239
240 func TestIndirectDependencyFix(t *testing.T) {
241         testenv.NeedsGo1Point(t, 14)
242
243         const mod = `
244 -- a/go.mod --
245 module mod.com
246
247 go 1.12
248
249 require example.com v1.2.3 // indirect
250 -- a/go.sum --
251 example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
252 example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
253 -- a/main.go --
254 package main
255
256 import "example.com/blah"
257
258 func main() {
259         fmt.Println(blah.Name)
260 `
261         const want = `module mod.com
262
263 go 1.12
264
265 require example.com v1.2.3
266 `
267
268         RunMultiple{
269                 {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
270                 {"nested", WithOptions(ProxyFiles(proxy))},
271         }.Run(t, mod, func(t *testing.T, env *Env) {
272                 env.OpenFile("a/go.mod")
273                 var d protocol.PublishDiagnosticsParams
274                 env.Await(
275                         OnceMet(
276                                 env.DiagnosticAtRegexp("a/go.mod", "// indirect"),
277                                 ReadDiagnostics("a/go.mod", &d),
278                         ),
279                 )
280                 env.ApplyQuickFixes("a/go.mod", d.Diagnostics)
281                 if got := env.Editor.BufferText("a/go.mod"); got != want {
282                         t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got))
283                 }
284         })
285 }
286
287 func TestUnusedDiag(t *testing.T) {
288         testenv.NeedsGo1Point(t, 14)
289
290         const proxy = `
291 -- example.com@v1.0.0/x.go --
292 package pkg
293 const X = 1
294 `
295         const files = `
296 -- a/go.mod --
297 module mod.com
298 go 1.14
299 require example.com v1.0.0
300 -- a/go.sum --
301 example.com v1.0.0 h1:38O7j5rEBajXk+Q5wzLbRN7KqMkSgEiN9NqcM1O2bBM=
302 example.com v1.0.0/go.mod h1:vUsPMGpx9ZXXzECCOsOmYCW7npJTwuA16yl89n3Mgls=
303 -- a/main.go --
304 package main
305 func main() {}
306 `
307
308         const want = `module mod.com
309
310 go 1.14
311 `
312
313         RunMultiple{
314                 {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
315                 {"nested", WithOptions(ProxyFiles(proxy))},
316         }.Run(t, files, func(t *testing.T, env *Env) {
317                 env.OpenFile("a/go.mod")
318                 var d protocol.PublishDiagnosticsParams
319                 env.Await(
320                         OnceMet(
321                                 env.DiagnosticAtRegexp("a/go.mod", `require example.com`),
322                                 ReadDiagnostics("a/go.mod", &d),
323                         ),
324                 )
325                 env.ApplyQuickFixes("a/go.mod", d.Diagnostics)
326                 if got := env.Editor.BufferText("a/go.mod"); got != want {
327                         t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got))
328                 }
329         })
330 }
331
332 // Test to reproduce golang/go#39041. It adds a new require to a go.mod file
333 // that already has an unused require.
334 func TestNewDepWithUnusedDep(t *testing.T) {
335         testenv.NeedsGo1Point(t, 14)
336
337         const proxy = `
338 -- github.com/esimov/caire@v1.2.5/go.mod --
339 module github.com/esimov/caire
340
341 go 1.12
342 -- github.com/esimov/caire@v1.2.5/caire.go --
343 package caire
344
345 func RemoveTempImage() {}
346 -- google.golang.org/protobuf@v1.20.0/go.mod --
347 module google.golang.org/protobuf
348
349 go 1.12
350 -- google.golang.org/protobuf@v1.20.0/hello/hello.go --
351 package hello
352 `
353         const repro = `
354 -- a/go.mod --
355 module mod.com
356
357 go 1.14
358
359 require google.golang.org/protobuf v1.20.0
360 -- a/go.sum --
361 github.com/esimov/caire v1.2.5 h1:OcqDII/BYxcBYj3DuwDKjd+ANhRxRqLa2n69EGje7qw=
362 github.com/esimov/caire v1.2.5/go.mod h1:mXnjRjg3+WUtuhfSC1rKRmdZU9vJZyS1ZWU0qSvJhK8=
363 google.golang.org/protobuf v1.20.0 h1:y9T1vAtFKQg0faFNMOxJU7WuEqPWolVkjIkU6aI8qCY=
364 google.golang.org/protobuf v1.20.0/go.mod h1:FcqsytGClbtLv1ot8NvsJHjBi0h22StKVP+K/j2liKA=
365 -- a/main.go --
366 package main
367
368 import (
369     "github.com/esimov/caire"
370 )
371
372 func _() {
373     caire.RemoveTempImage()
374 }`
375
376         RunMultiple{
377                 {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
378                 {"nested", WithOptions(ProxyFiles(proxy))},
379         }.Run(t, repro, func(t *testing.T, env *Env) {
380                 env.OpenFile("a/main.go")
381                 var d protocol.PublishDiagnosticsParams
382                 env.Await(
383                         OnceMet(
384                                 env.DiagnosticAtRegexp("a/main.go", `"github.com/esimov/caire"`),
385                                 ReadDiagnostics("a/main.go", &d),
386                         ),
387                 )
388                 env.ApplyQuickFixes("a/main.go", d.Diagnostics)
389                 want := `module mod.com
390
391 go 1.14
392
393 require (
394         github.com/esimov/caire v1.2.5
395         google.golang.org/protobuf v1.20.0
396 )
397 `
398                 if got := env.ReadWorkspaceFile("a/go.mod"); got != want {
399                         t.Fatalf("TestNewDepWithUnusedDep failed:\n%s", tests.Diff(t, want, got))
400                 }
401         })
402 }
403
404 // TODO: For this test to be effective, the sandbox's file watcher must respect
405 // the file watching GlobPattern in the capability registration. See
406 // golang/go#39384.
407 func TestModuleChangesOnDisk(t *testing.T) {
408         testenv.NeedsGo1Point(t, 14)
409
410         const mod = `
411 -- a/go.mod --
412 module mod.com
413
414 go 1.12
415
416 require example.com v1.2.3
417 -- a/go.sum --
418 example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
419 example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
420 -- a/main.go --
421 package main
422
423 func main() {
424         fmt.Println(blah.Name)
425 `
426         RunMultiple{
427                 {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
428                 {"nested", WithOptions(ProxyFiles(proxy))},
429         }.Run(t, mod, func(t *testing.T, env *Env) {
430                 env.Await(env.DiagnosticAtRegexp("a/go.mod", "require"))
431                 env.RunGoCommandInDir("a", "mod", "tidy")
432                 env.Await(
433                         EmptyDiagnostics("a/go.mod"),
434                 )
435         })
436 }
437
438 // Tests golang/go#39784: a missing indirect dependency, necessary
439 // due to blah@v2.0.0's incomplete go.mod file.
440 func TestBadlyVersionedModule(t *testing.T) {
441         testenv.NeedsGo1Point(t, 14)
442
443         const proxy = `
444 -- example.com/blah/@v/v1.0.0.mod --
445 module example.com
446
447 go 1.12
448 -- example.com/blah@v1.0.0/blah.go --
449 package blah
450
451 const Name = "Blah"
452 -- example.com/blah/v2/@v/v2.0.0.mod --
453 module example.com
454
455 go 1.12
456 -- example.com/blah/v2@v2.0.0/blah.go --
457 package blah
458
459 import "example.com/blah"
460
461 var V1Name = blah.Name
462 const Name = "Blah"
463 `
464         const files = `
465 -- a/go.mod --
466 module mod.com
467
468 go 1.12
469
470 require example.com/blah/v2 v2.0.0
471 -- a/go.sum --
472 example.com/blah v1.0.0 h1:kGPlWJbMsn1P31H9xp/q2mYI32cxLnCvauHN0AVaHnc=
473 example.com/blah v1.0.0/go.mod h1:PZUQaGFeVjyDmAE8ywmLbmDn3fj4Ws8epg4oLuDzW3M=
474 example.com/blah/v2 v2.0.0 h1:DNPsFPkKtTdxclRheaMCiYAoYizp6PuBzO0OmLOO0pY=
475 example.com/blah/v2 v2.0.0/go.mod h1:UZiKbTwobERo/hrqFLvIQlJwQZQGxWMVY4xere8mj7w=
476 -- a/main.go --
477 package main
478
479 import "example.com/blah/v2"
480
481 var _ = blah.Name
482 `
483         RunMultiple{
484                 {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
485                 {"nested", WithOptions(ProxyFiles(proxy))},
486         }.Run(t, files, func(t *testing.T, env *Env) {
487                 env.OpenFile("a/main.go")
488                 env.OpenFile("a/go.mod")
489                 env.Await(
490                         // We would like for the error to appear in the v2 module, but
491                         // as of writing non-workspace packages are not diagnosed.
492                         env.DiagnosticAtRegexpWithMessage("a/main.go", `"example.com/blah/v2"`, "cannot find module providing"),
493                         env.DiagnosticAtRegexpWithMessage("a/go.mod", `require example.com/blah/v2`, "cannot find module providing"),
494                 )
495                 env.ApplyQuickFixes("a/go.mod", env.DiagnosticsFor("a/go.mod").Diagnostics)
496                 const want = `module mod.com
497
498 go 1.12
499
500 require (
501         example.com/blah v1.0.0 // indirect
502         example.com/blah/v2 v2.0.0
503 )
504 `
505                 env.SaveBuffer("a/go.mod")
506                 env.Await(EmptyDiagnostics("a/main.go"))
507                 if got := env.Editor.BufferText("a/go.mod"); got != want {
508                         t.Fatalf("suggested fixes failed:\n%s", tests.Diff(t, want, got))
509                 }
510         })
511 }
512
513 // Reproduces golang/go#38232.
514 func TestUnknownRevision(t *testing.T) {
515         testenv.NeedsGo1Point(t, 14)
516
517         const unknown = `
518 -- a/go.mod --
519 module mod.com
520
521 require (
522         example.com v1.2.2
523 )
524 -- a/main.go --
525 package main
526
527 import "example.com/blah"
528
529 func main() {
530         var x = blah.Name
531 }
532 `
533
534         runner := RunMultiple{
535                 {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))},
536                 {"nested", WithOptions(ProxyFiles(proxy))},
537         }
538         // Start from a bad state/bad IWL, and confirm that we recover.
539         t.Run("bad", func(t *testing.T) {
540                 runner.Run(t, unknown, func(t *testing.T, env *Env) {
541                         env.OpenFile("a/go.mod")
542                         env.Await(
543                                 env.DiagnosticAtRegexp("a/go.mod", "example.com v1.2.2"),
544                         )
545                         env.RegexpReplace("a/go.mod", "v1.2.2", "v1.2.3")
546                         env.SaveBuffer("a/go.mod") // Save to trigger diagnostics.
547
548                         d := protocol.PublishDiagnosticsParams{}
549                         env.Await(
550                                 OnceMet(
551                                         // Make sure the diagnostic mentions the new version -- the old diagnostic is in the same place.
552                                         env.DiagnosticAtRegexpWithMessage("a/go.mod", "example.com v1.2.3", "example.com@v1.2.3"),
553                                         ReadDiagnostics("a/go.mod", &d),
554                                 ),
555                         )
556                         env.ApplyQuickFixes("a/go.mod", d.Diagnostics)
557                         env.SaveBuffer("a/go.mod") // Save to trigger diagnostics.
558                         env.Await(
559                                 EmptyDiagnostics("a/go.mod"),
560                                 env.DiagnosticAtRegexp("a/main.go", "x = "),
561                         )
562                 })
563         })
564
565         const known = `
566 -- a/go.mod --
567 module mod.com
568
569 require (
570         example.com v1.2.3
571 )
572 -- a/go.sum --
573 example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
574 example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
575 -- a/main.go --
576 package main
577
578 import "example.com/blah"
579
580 func main() {
581         var x = blah.Name
582 }
583 `
584         // Start from a good state, transform to a bad state, and confirm that we
585         // still recover.
586         t.Run("good", func(t *testing.T) {
587                 runner.Run(t, known, func(t *testing.T, env *Env) {
588                         env.OpenFile("a/go.mod")
589                         env.Await(
590                                 env.DiagnosticAtRegexp("a/main.go", "x = "),
591                         )
592                         env.RegexpReplace("a/go.mod", "v1.2.3", "v1.2.2")
593                         env.Editor.SaveBuffer(env.Ctx, "a/go.mod") // go.mod changes must be on disk
594                         env.Await(
595                                 env.DiagnosticAtRegexp("a/go.mod", "example.com v1.2.2"),
596                         )
597                         env.RegexpReplace("a/go.mod", "v1.2.2", "v1.2.3")
598                         env.Editor.SaveBuffer(env.Ctx, "a/go.mod") // go.mod changes must be on disk
599                         env.Await(
600                                 env.DiagnosticAtRegexp("a/main.go", "x = "),
601                         )
602                 })
603         })
604 }
605
606 // Confirm that an error in an indirect dependency of a requirement is surfaced
607 // as a diagnostic in the go.mod file.
608 func TestErrorInIndirectDependency(t *testing.T) {
609         testenv.NeedsGo1Point(t, 14)
610
611         const badProxy = `
612 -- example.com@v1.2.3/go.mod --
613 module example.com
614
615 go 1.12
616
617 require random.org v1.2.3 // indirect
618 -- example.com@v1.2.3/blah/blah.go --
619 package blah
620
621 const Name = "Blah"
622 -- random.org@v1.2.3/go.mod --
623 module bob.org
624
625 go 1.12
626 -- random.org@v1.2.3/blah/blah.go --
627 package hello
628
629 const Name = "Hello"
630 `
631         const module = `
632 -- a/go.mod --
633 module mod.com
634
635 go 1.14
636
637 require example.com v1.2.3
638 -- a/main.go --
639 package main
640
641 import "example.com/blah"
642
643 func main() {
644         println(blah.Name)
645 }
646 `
647         RunMultiple{
648                 {"default", WithOptions(ProxyFiles(badProxy), WorkspaceFolders("a"))},
649                 {"nested", WithOptions(ProxyFiles(badProxy))},
650         }.Run(t, module, func(t *testing.T, env *Env) {
651                 env.OpenFile("a/go.mod")
652                 env.Await(
653                         env.DiagnosticAtRegexp("a/go.mod", "require example.com v1.2.3"),
654                 )
655         })
656 }
657
658 // A copy of govim's config_set_env_goflags_mod_readonly test.
659 func TestGovimModReadonly(t *testing.T) {
660         const mod = `
661 -- go.mod --
662 module mod.com
663
664 go 1.13
665 -- main.go --
666 package main
667
668 import "example.com/blah"
669
670 func main() {
671         println(blah.Name)
672 }
673 `
674         WithOptions(
675                 EditorConfig{
676                         Env: map[string]string{
677                                 "GOFLAGS": "-mod=readonly",
678                         },
679                 },
680                 ProxyFiles(proxy),
681                 Modes(Singleton),
682         ).Run(t, mod, func(t *testing.T, env *Env) {
683                 env.OpenFile("main.go")
684                 original := env.ReadWorkspaceFile("go.mod")
685                 env.Await(
686                         env.DiagnosticAtRegexp("main.go", `"example.com/blah"`),
687                 )
688                 got := env.ReadWorkspaceFile("go.mod")
689                 if got != original {
690                         t.Fatalf("go.mod file modified:\n%s", tests.Diff(t, original, got))
691                 }
692                 env.RunGoCommand("get", "example.com/blah@v1.2.3")
693                 env.RunGoCommand("mod", "tidy")
694                 env.Await(
695                         EmptyDiagnostics("main.go"),
696                 )
697         })
698 }
699
700 func TestMultiModuleModDiagnostics(t *testing.T) {
701         testenv.NeedsGo1Point(t, 14)
702
703         const mod = `
704 -- a/go.mod --
705 module moda.com
706
707 go 1.14
708
709 require (
710         example.com v1.2.3
711 )
712 -- a/go.sum --
713 example.com v1.2.3 h1:Yryq11hF02fEf2JlOS2eph+ICE2/ceevGV3C9dl5V/c=
714 example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
715 -- a/main.go --
716 package main
717
718 func main() {}
719 -- b/go.mod --
720 module modb.com
721
722 require example.com v1.2.3
723
724 go 1.14
725 -- b/main.go --
726 package main
727
728 import "example.com/blah"
729
730 func main() {
731         blah.SaySomething()
732 }
733 `
734         WithOptions(
735                 ProxyFiles(workspaceProxy),
736                 Modes(Experimental),
737         ).Run(t, mod, func(t *testing.T, env *Env) {
738                 env.Await(
739                         env.DiagnosticAtRegexpWithMessage("a/go.mod", "example.com v1.2.3", "is not used"),
740                 )
741         })
742 }
743
744 func TestModTidyWithBuildTags(t *testing.T) {
745         testenv.NeedsGo1Point(t, 14)
746
747         const mod = `
748 -- go.mod --
749 module mod.com
750
751 go 1.14
752 -- main.go --
753 // +build bob
754
755 package main
756
757 import "example.com/blah"
758
759 func main() {
760         blah.SaySomething()
761 }
762 `
763         WithOptions(
764                 ProxyFiles(workspaceProxy),
765                 EditorConfig{
766                         BuildFlags: []string{"-tags", "bob"},
767                 },
768         ).Run(t, mod, func(t *testing.T, env *Env) {
769                 env.Await(
770                         env.DiagnosticAtRegexp("main.go", `"example.com/blah"`),
771                 )
772         })
773 }
774
775 func TestModTypoDiagnostic(t *testing.T) {
776         const mod = `
777 -- go.mod --
778 module mod.com
779
780 go 1.12
781 -- main.go --
782 package main
783
784 func main() {}
785 `
786         Run(t, mod, func(t *testing.T, env *Env) {
787                 env.OpenFile("go.mod")
788                 env.RegexpReplace("go.mod", "module", "modul")
789                 env.Await(
790                         env.DiagnosticAtRegexp("go.mod", "modul"),
791                 )
792         })
793 }
794
795 func TestSumUpdateFixesDiagnostics(t *testing.T) {
796         testenv.NeedsGo1Point(t, 14)
797
798         const mod = `
799 -- go.mod --
800 module mod.com
801
802 go 1.12
803
804 require (
805         example.com v1.2.3
806 )
807 -- go.sum --
808 -- main.go --
809 package main
810
811 import (
812         "example.com/blah"
813 )
814
815 func main() {
816         println(blah.Name)
817 }
818 `
819         WithOptions(
820                 ProxyFiles(workspaceProxy),
821         ).Run(t, mod, func(t *testing.T, env *Env) {
822                 d := &protocol.PublishDiagnosticsParams{}
823                 env.OpenFile("go.mod")
824                 env.Await(
825                         OnceMet(
826                                 env.GoSumDiagnostic("go.mod", `example.com v1.2.3`),
827                                 ReadDiagnostics("go.mod", d),
828                         ),
829                 )
830                 env.ApplyQuickFixes("go.mod", d.Diagnostics)
831                 env.SaveBuffer("go.mod") // Save to trigger diagnostics.
832                 env.Await(
833                         EmptyDiagnostics("go.mod"),
834                 )
835         })
836 }
837
838 // This test confirms that editing a go.mod file only causes metadata
839 // to be invalidated when it's saved.
840 func TestGoModInvalidatesOnSave(t *testing.T) {
841         const mod = `
842 -- go.mod --
843 module mod.com
844
845 go 1.12
846 -- main.go --
847 package main
848
849 func main() {
850         hello()
851 }
852 -- hello.go --
853 package main
854
855 func hello() {}
856 `
857         WithOptions(
858                 // TODO(rFindley) this doesn't work in multi-module workspace mode, because
859                 // it keeps around the last parsing modfile. Update this test to also
860                 // exercise the workspace module.
861                 Modes(Singleton),
862         ).Run(t, mod, func(t *testing.T, env *Env) {
863                 env.OpenFile("go.mod")
864                 env.Await(env.DoneWithOpen())
865                 env.RegexpReplace("go.mod", "module", "modul")
866                 // Confirm that we still have metadata with only on-disk edits.
867                 env.OpenFile("main.go")
868                 file, _ := env.GoToDefinition("main.go", env.RegexpSearch("main.go", "hello"))
869                 if filepath.Base(file) != "hello.go" {
870                         t.Fatalf("expected definition in hello.go, got %s", file)
871                 }
872                 // Confirm that we no longer have metadata when the file is saved.
873                 env.SaveBufferWithoutActions("go.mod")
874                 _, _, err := env.Editor.GoToDefinition(env.Ctx, "main.go", env.RegexpSearch("main.go", "hello"))
875                 if err == nil {
876                         t.Fatalf("expected error, got none")
877                 }
878         })
879 }
880
881 func TestRemoveUnusedDependency(t *testing.T) {
882         testenv.NeedsGo1Point(t, 14)
883
884         const proxy = `
885 -- hasdep.com@v1.2.3/go.mod --
886 module hasdep.com
887
888 go 1.12
889
890 require example.com v1.2.3
891 -- hasdep.com@v1.2.3/a/a.go --
892 package a
893 -- example.com@v1.2.3/go.mod --
894 module example.com
895
896 go 1.12
897 -- example.com@v1.2.3/blah/blah.go --
898 package blah
899
900 const Name = "Blah"
901 -- random.com@v1.2.3/go.mod --
902 module random.com
903
904 go 1.12
905 -- random.com@v1.2.3/blah/blah.go --
906 package blah
907
908 const Name = "Blah"
909 `
910         t.Run("almost tidied", func(t *testing.T) {
911                 const mod = `
912 -- go.mod --
913 module mod.com
914
915 go 1.12
916
917 require hasdep.com v1.2.3
918 -- go.sum --
919 example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
920 example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
921 hasdep.com v1.2.3 h1:00y+N5oD+SpKoqV1zP2VOPawcW65Zb9NebANY3GSzGI=
922 hasdep.com v1.2.3/go.mod h1:ePVZOlez+KZEOejfLPGL2n4i8qiAjrkhQZ4wcImqAes=
923 -- main.go --
924 package main
925
926 func main() {}
927 `
928                 WithOptions(
929                         ProxyFiles(proxy),
930                 ).Run(t, mod, func(t *testing.T, env *Env) {
931                         env.OpenFile("go.mod")
932                         d := &protocol.PublishDiagnosticsParams{}
933                         env.Await(
934                                 OnceMet(
935                                         env.DiagnosticAtRegexp("go.mod", "require hasdep.com v1.2.3"),
936                                         ReadDiagnostics("go.mod", d),
937                                 ),
938                         )
939                         const want = `module mod.com
940
941 go 1.12
942 `
943                         env.ApplyQuickFixes("go.mod", d.Diagnostics)
944                         if got := env.Editor.BufferText("go.mod"); got != want {
945                                 t.Fatalf("unexpected content in go.mod:\n%s", tests.Diff(t, want, got))
946                         }
947                 })
948         })
949
950         t.Run("not tidied", func(t *testing.T) {
951                 const mod = `
952 -- go.mod --
953 module mod.com
954
955 go 1.12
956
957 require hasdep.com v1.2.3
958 require random.com v1.2.3
959 -- go.sum --
960 example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY=
961 example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
962 hasdep.com v1.2.3 h1:00y+N5oD+SpKoqV1zP2VOPawcW65Zb9NebANY3GSzGI=
963 hasdep.com v1.2.3/go.mod h1:ePVZOlez+KZEOejfLPGL2n4i8qiAjrkhQZ4wcImqAes=
964 random.com v1.2.3 h1:PzYTykzqqH6+qU0dIgh9iPFbfb4Mm8zNBjWWreRKtx0=
965 random.com v1.2.3/go.mod h1:8EGj+8a4Hw1clAp8vbaeHAsKE4sbm536FP7nKyXO+qQ=
966 -- main.go --
967 package main
968
969 func main() {}
970 `
971                 WithOptions(
972                         ProxyFiles(proxy),
973                 ).Run(t, mod, func(t *testing.T, env *Env) {
974                         d := &protocol.PublishDiagnosticsParams{}
975                         env.OpenFile("go.mod")
976                         pos := env.RegexpSearch("go.mod", "require hasdep.com v1.2.3")
977                         env.Await(
978                                 OnceMet(
979                                         DiagnosticAt("go.mod", pos.Line, pos.Column),
980                                         ReadDiagnostics("go.mod", d),
981                                 ),
982                         )
983                         const want = `module mod.com
984
985 go 1.12
986
987 require random.com v1.2.3
988 `
989                         var diagnostics []protocol.Diagnostic
990                         for _, d := range d.Diagnostics {
991                                 if d.Range.Start.Line != uint32(pos.Line) {
992                                         continue
993                                 }
994                                 diagnostics = append(diagnostics, d)
995                         }
996                         env.ApplyQuickFixes("go.mod", diagnostics)
997                         if got := env.Editor.BufferText("go.mod"); got != want {
998                                 t.Fatalf("unexpected content in go.mod:\n%s", tests.Diff(t, want, got))
999                         }
1000                 })
1001         })
1002 }
1003
1004 func TestSumUpdateQuickFix(t *testing.T) {
1005         testenv.NeedsGo1Point(t, 14)
1006         const mod = `
1007 -- go.mod --
1008 module mod.com
1009
1010 go 1.12
1011
1012 require (
1013         example.com v1.2.3
1014 )
1015 -- go.sum --
1016 -- main.go --
1017 package main
1018
1019 import (
1020         "example.com/blah"
1021 )
1022
1023 func main() {
1024         blah.Hello()
1025 }
1026 `
1027         WithOptions(
1028                 ProxyFiles(workspaceProxy),
1029                 Modes(Singleton),
1030         ).Run(t, mod, func(t *testing.T, env *Env) {
1031                 env.OpenFile("go.mod")
1032                 params := &protocol.PublishDiagnosticsParams{}
1033                 env.Await(
1034                         OnceMet(
1035                                 env.GoSumDiagnostic("go.mod", "example.com"),
1036                                 ReadDiagnostics("go.mod", params),
1037                         ),
1038                 )
1039                 env.ApplyQuickFixes("go.mod", params.Diagnostics)
1040                 const want = `example.com v1.2.3 h1:Yryq11hF02fEf2JlOS2eph+ICE2/ceevGV3C9dl5V/c=
1041 example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
1042 `
1043                 if got := env.ReadWorkspaceFile("go.sum"); got != want {
1044                         t.Fatalf("unexpected go.sum contents:\n%s", tests.Diff(t, want, got))
1045                 }
1046         })
1047 }
1048
1049 func TestDownloadDeps(t *testing.T) {
1050         testenv.NeedsGo1Point(t, 14)
1051
1052         const proxy = `
1053 -- example.com@v1.2.3/go.mod --
1054 module example.com
1055
1056 go 1.12
1057
1058 require random.org v1.2.3
1059 -- example.com@v1.2.3/blah/blah.go --
1060 package blah
1061
1062 import "random.org/bye"
1063
1064 func SaySomething() {
1065         bye.Goodbye()
1066 }
1067 -- random.org@v1.2.3/go.mod --
1068 module random.org
1069
1070 go 1.12
1071 -- random.org@v1.2.3/bye/bye.go --
1072 package bye
1073
1074 func Goodbye() {
1075         println("Bye")
1076 }
1077 `
1078
1079         const mod = `
1080 -- go.mod --
1081 module mod.com
1082
1083 go 1.12
1084 -- go.sum --
1085 -- main.go --
1086 package main
1087
1088 import (
1089         "example.com/blah"
1090 )
1091
1092 func main() {
1093         blah.SaySomething()
1094 }
1095 `
1096         WithOptions(
1097                 ProxyFiles(proxy),
1098                 Modes(Singleton),
1099         ).Run(t, mod, func(t *testing.T, env *Env) {
1100                 env.OpenFile("main.go")
1101                 d := &protocol.PublishDiagnosticsParams{}
1102                 env.Await(
1103                         env.DiagnosticAtRegexpWithMessage("main.go", `"example.com/blah"`, `could not import example.com/blah (no required module provides package "example.com/blah")`),
1104                         ReadDiagnostics("main.go", d),
1105                 )
1106                 env.ApplyQuickFixes("main.go", d.Diagnostics)
1107                 env.Await(
1108                         EmptyDiagnostics("main.go"),
1109                         NoDiagnostics("go.mod"),
1110                 )
1111         })
1112 }