X-Git-Url: https://git.josue.xyz/?a=blobdiff_plain;f=.config%2Fcoc%2Fextensions%2Fcoc-go-data%2Ftools%2Fpkg%2Fmod%2Fgolang.org%2Fx%2Ftools%2Fgopls%40v0.6.9%2Finternal%2Fregtest%2Fmodfile%2Fmodfile_test.go;fp=.config%2Fcoc%2Fextensions%2Fcoc-go-data%2Ftools%2Fpkg%2Fmod%2Fgolang.org%2Fx%2Ftools%2Fgopls%40v0.6.9%2Finternal%2Fregtest%2Fmodfile%2Fmodfile_test.go;h=33b65feb35a7ca0ddba192117bbbd4cda18da4ed;hb=3c06164f15bd10aed7d66b6314764a2961a14762;hp=0000000000000000000000000000000000000000;hpb=0e9c3ceb40901f4d44981c1376cb9e23a248e006;p=dotfiles%2F.git diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools/gopls@v0.6.9/internal/regtest/modfile/modfile_test.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools/gopls@v0.6.9/internal/regtest/modfile/modfile_test.go new file mode 100644 index 00000000..33b65feb --- /dev/null +++ b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools/gopls@v0.6.9/internal/regtest/modfile/modfile_test.go @@ -0,0 +1,1112 @@ +// 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 modfile + +import ( + "path/filepath" + "strings" + "testing" + + . "golang.org/x/tools/gopls/internal/regtest" + + "golang.org/x/tools/internal/lsp/protocol" + "golang.org/x/tools/internal/lsp/tests" + "golang.org/x/tools/internal/testenv" +) + +func TestMain(m *testing.M) { + Main(m) +} + +const workspaceProxy = ` +-- example.com@v1.2.3/go.mod -- +module example.com + +go 1.12 +-- example.com@v1.2.3/blah/blah.go -- +package blah + +func SaySomething() { + fmt.Println("something") +} +-- random.org@v1.2.3/go.mod -- +module random.org + +go 1.12 +-- random.org@v1.2.3/bye/bye.go -- +package bye + +func Goodbye() { + println("Bye") +} +` + +const proxy = ` +-- example.com@v1.2.3/go.mod -- +module example.com + +go 1.12 +-- example.com@v1.2.3/blah/blah.go -- +package blah + +const Name = "Blah" +-- random.org@v1.2.3/go.mod -- +module random.org + +go 1.12 +-- random.org@v1.2.3/blah/blah.go -- +package hello + +const Name = "Hello" +` + +func TestModFileModification(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const untidyModule = ` +-- a/go.mod -- +module mod.com + +-- a/main.go -- +package main + +import "example.com/blah" + +func main() { + println(blah.Name) +} +` + + runner := RunMultiple{ + {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))}, + {"nested", WithOptions(ProxyFiles(proxy))}, + } + + t.Run("basic", func(t *testing.T) { + runner.Run(t, untidyModule, func(t *testing.T, env *Env) { + // Open the file and make sure that the initial workspace load does not + // modify the go.mod file. + goModContent := env.ReadWorkspaceFile("a/go.mod") + env.OpenFile("a/main.go") + env.Await( + env.DiagnosticAtRegexp("a/main.go", "\"example.com/blah\""), + ) + if got := env.ReadWorkspaceFile("a/go.mod"); got != goModContent { + t.Fatalf("go.mod changed on disk:\n%s", tests.Diff(t, goModContent, got)) + } + // Save the buffer, which will format and organize imports. + // Confirm that the go.mod file still does not change. + env.SaveBuffer("a/main.go") + env.Await( + env.DiagnosticAtRegexp("a/main.go", "\"example.com/blah\""), + ) + if got := env.ReadWorkspaceFile("a/go.mod"); got != goModContent { + t.Fatalf("go.mod changed on disk:\n%s", tests.Diff(t, goModContent, got)) + } + }) + }) + + // Reproduce golang/go#40269 by deleting and recreating main.go. + t.Run("delete main.go", func(t *testing.T) { + t.Skip("This test will be flaky until golang/go#40269 is resolved.") + + runner.Run(t, untidyModule, func(t *testing.T, env *Env) { + goModContent := env.ReadWorkspaceFile("a/go.mod") + mainContent := env.ReadWorkspaceFile("a/main.go") + env.OpenFile("a/main.go") + env.SaveBuffer("a/main.go") + + env.RemoveWorkspaceFile("a/main.go") + env.Await( + env.DoneWithOpen(), + env.DoneWithSave(), + env.DoneWithChangeWatchedFiles(), + ) + + env.WriteWorkspaceFile("main.go", mainContent) + env.Await( + env.DiagnosticAtRegexp("main.go", "\"example.com/blah\""), + ) + if got := env.ReadWorkspaceFile("go.mod"); got != goModContent { + t.Fatalf("go.mod changed on disk:\n%s", tests.Diff(t, goModContent, got)) + } + }) + }) +} + +func TestGoGetFix(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + const mod = ` +-- a/go.mod -- +module mod.com + +go 1.12 + +-- a/main.go -- +package main + +import "example.com/blah" + +var _ = blah.Name +` + + const want = `module mod.com + +go 1.12 + +require example.com v1.2.3 +` + + RunMultiple{ + {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))}, + {"nested", WithOptions(ProxyFiles(proxy))}, + }.Run(t, mod, func(t *testing.T, env *Env) { + if strings.Contains(t.Name(), "workspace_module") { + t.Skip("workspace module mode doesn't set -mod=readonly") + } + env.OpenFile("a/main.go") + var d protocol.PublishDiagnosticsParams + env.Await( + OnceMet( + env.DiagnosticAtRegexp("a/main.go", `"example.com/blah"`), + ReadDiagnostics("a/main.go", &d), + ), + ) + var goGetDiag protocol.Diagnostic + for _, diag := range d.Diagnostics { + if strings.Contains(diag.Message, "could not import") { + goGetDiag = diag + } + } + env.ApplyQuickFixes("a/main.go", []protocol.Diagnostic{goGetDiag}) + if got := env.ReadWorkspaceFile("a/go.mod"); got != want { + t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got)) + } + }) +} + +// Tests that multiple missing dependencies gives good single fixes. +func TestMissingDependencyFixes(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + const mod = ` +-- a/go.mod -- +module mod.com + +go 1.12 + +-- a/main.go -- +package main + +import "example.com/blah" +import "random.org/blah" + +var _, _ = blah.Name, hello.Name +` + + const want = `module mod.com + +go 1.12 + +require random.org v1.2.3 +` + + RunMultiple{ + {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))}, + {"nested", WithOptions(ProxyFiles(proxy))}, + }.Run(t, mod, func(t *testing.T, env *Env) { + env.OpenFile("a/main.go") + var d protocol.PublishDiagnosticsParams + env.Await( + OnceMet( + env.DiagnosticAtRegexp("a/main.go", `"random.org/blah"`), + ReadDiagnostics("a/main.go", &d), + ), + ) + var randomDiag protocol.Diagnostic + for _, diag := range d.Diagnostics { + if strings.Contains(diag.Message, "random.org") { + randomDiag = diag + } + } + env.ApplyQuickFixes("a/main.go", []protocol.Diagnostic{randomDiag}) + if got := env.ReadWorkspaceFile("a/go.mod"); got != want { + t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got)) + } + }) +} + +func TestIndirectDependencyFix(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const mod = ` +-- a/go.mod -- +module mod.com + +go 1.12 + +require example.com v1.2.3 // indirect +-- a/go.sum -- +example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY= +example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo= +-- a/main.go -- +package main + +import "example.com/blah" + +func main() { + fmt.Println(blah.Name) +` + const want = `module mod.com + +go 1.12 + +require example.com v1.2.3 +` + + RunMultiple{ + {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))}, + {"nested", WithOptions(ProxyFiles(proxy))}, + }.Run(t, mod, func(t *testing.T, env *Env) { + env.OpenFile("a/go.mod") + var d protocol.PublishDiagnosticsParams + env.Await( + OnceMet( + env.DiagnosticAtRegexp("a/go.mod", "// indirect"), + ReadDiagnostics("a/go.mod", &d), + ), + ) + env.ApplyQuickFixes("a/go.mod", d.Diagnostics) + if got := env.Editor.BufferText("a/go.mod"); got != want { + t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got)) + } + }) +} + +func TestUnusedDiag(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const proxy = ` +-- example.com@v1.0.0/x.go -- +package pkg +const X = 1 +` + const files = ` +-- a/go.mod -- +module mod.com +go 1.14 +require example.com v1.0.0 +-- a/go.sum -- +example.com v1.0.0 h1:38O7j5rEBajXk+Q5wzLbRN7KqMkSgEiN9NqcM1O2bBM= +example.com v1.0.0/go.mod h1:vUsPMGpx9ZXXzECCOsOmYCW7npJTwuA16yl89n3Mgls= +-- a/main.go -- +package main +func main() {} +` + + const want = `module mod.com + +go 1.14 +` + + RunMultiple{ + {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))}, + {"nested", WithOptions(ProxyFiles(proxy))}, + }.Run(t, files, func(t *testing.T, env *Env) { + env.OpenFile("a/go.mod") + var d protocol.PublishDiagnosticsParams + env.Await( + OnceMet( + env.DiagnosticAtRegexp("a/go.mod", `require example.com`), + ReadDiagnostics("a/go.mod", &d), + ), + ) + env.ApplyQuickFixes("a/go.mod", d.Diagnostics) + if got := env.Editor.BufferText("a/go.mod"); got != want { + t.Fatalf("unexpected go.mod content:\n%s", tests.Diff(t, want, got)) + } + }) +} + +// Test to reproduce golang/go#39041. It adds a new require to a go.mod file +// that already has an unused require. +func TestNewDepWithUnusedDep(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const proxy = ` +-- github.com/esimov/caire@v1.2.5/go.mod -- +module github.com/esimov/caire + +go 1.12 +-- github.com/esimov/caire@v1.2.5/caire.go -- +package caire + +func RemoveTempImage() {} +-- google.golang.org/protobuf@v1.20.0/go.mod -- +module google.golang.org/protobuf + +go 1.12 +-- google.golang.org/protobuf@v1.20.0/hello/hello.go -- +package hello +` + const repro = ` +-- a/go.mod -- +module mod.com + +go 1.14 + +require google.golang.org/protobuf v1.20.0 +-- a/go.sum -- +github.com/esimov/caire v1.2.5 h1:OcqDII/BYxcBYj3DuwDKjd+ANhRxRqLa2n69EGje7qw= +github.com/esimov/caire v1.2.5/go.mod h1:mXnjRjg3+WUtuhfSC1rKRmdZU9vJZyS1ZWU0qSvJhK8= +google.golang.org/protobuf v1.20.0 h1:y9T1vAtFKQg0faFNMOxJU7WuEqPWolVkjIkU6aI8qCY= +google.golang.org/protobuf v1.20.0/go.mod h1:FcqsytGClbtLv1ot8NvsJHjBi0h22StKVP+K/j2liKA= +-- a/main.go -- +package main + +import ( + "github.com/esimov/caire" +) + +func _() { + caire.RemoveTempImage() +}` + + RunMultiple{ + {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))}, + {"nested", WithOptions(ProxyFiles(proxy))}, + }.Run(t, repro, func(t *testing.T, env *Env) { + env.OpenFile("a/main.go") + var d protocol.PublishDiagnosticsParams + env.Await( + OnceMet( + env.DiagnosticAtRegexp("a/main.go", `"github.com/esimov/caire"`), + ReadDiagnostics("a/main.go", &d), + ), + ) + env.ApplyQuickFixes("a/main.go", d.Diagnostics) + want := `module mod.com + +go 1.14 + +require ( + github.com/esimov/caire v1.2.5 + google.golang.org/protobuf v1.20.0 +) +` + if got := env.ReadWorkspaceFile("a/go.mod"); got != want { + t.Fatalf("TestNewDepWithUnusedDep failed:\n%s", tests.Diff(t, want, got)) + } + }) +} + +// TODO: For this test to be effective, the sandbox's file watcher must respect +// the file watching GlobPattern in the capability registration. See +// golang/go#39384. +func TestModuleChangesOnDisk(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const mod = ` +-- a/go.mod -- +module mod.com + +go 1.12 + +require example.com v1.2.3 +-- a/go.sum -- +example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY= +example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo= +-- a/main.go -- +package main + +func main() { + fmt.Println(blah.Name) +` + RunMultiple{ + {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))}, + {"nested", WithOptions(ProxyFiles(proxy))}, + }.Run(t, mod, func(t *testing.T, env *Env) { + env.Await(env.DiagnosticAtRegexp("a/go.mod", "require")) + env.RunGoCommandInDir("a", "mod", "tidy") + env.Await( + EmptyDiagnostics("a/go.mod"), + ) + }) +} + +// Tests golang/go#39784: a missing indirect dependency, necessary +// due to blah@v2.0.0's incomplete go.mod file. +func TestBadlyVersionedModule(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const proxy = ` +-- example.com/blah/@v/v1.0.0.mod -- +module example.com + +go 1.12 +-- example.com/blah@v1.0.0/blah.go -- +package blah + +const Name = "Blah" +-- example.com/blah/v2/@v/v2.0.0.mod -- +module example.com + +go 1.12 +-- example.com/blah/v2@v2.0.0/blah.go -- +package blah + +import "example.com/blah" + +var V1Name = blah.Name +const Name = "Blah" +` + const files = ` +-- a/go.mod -- +module mod.com + +go 1.12 + +require example.com/blah/v2 v2.0.0 +-- a/go.sum -- +example.com/blah v1.0.0 h1:kGPlWJbMsn1P31H9xp/q2mYI32cxLnCvauHN0AVaHnc= +example.com/blah v1.0.0/go.mod h1:PZUQaGFeVjyDmAE8ywmLbmDn3fj4Ws8epg4oLuDzW3M= +example.com/blah/v2 v2.0.0 h1:DNPsFPkKtTdxclRheaMCiYAoYizp6PuBzO0OmLOO0pY= +example.com/blah/v2 v2.0.0/go.mod h1:UZiKbTwobERo/hrqFLvIQlJwQZQGxWMVY4xere8mj7w= +-- a/main.go -- +package main + +import "example.com/blah/v2" + +var _ = blah.Name +` + RunMultiple{ + {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))}, + {"nested", WithOptions(ProxyFiles(proxy))}, + }.Run(t, files, func(t *testing.T, env *Env) { + env.OpenFile("a/main.go") + env.OpenFile("a/go.mod") + env.Await( + // We would like for the error to appear in the v2 module, but + // as of writing non-workspace packages are not diagnosed. + env.DiagnosticAtRegexpWithMessage("a/main.go", `"example.com/blah/v2"`, "cannot find module providing"), + env.DiagnosticAtRegexpWithMessage("a/go.mod", `require example.com/blah/v2`, "cannot find module providing"), + ) + env.ApplyQuickFixes("a/go.mod", env.DiagnosticsFor("a/go.mod").Diagnostics) + const want = `module mod.com + +go 1.12 + +require ( + example.com/blah v1.0.0 // indirect + example.com/blah/v2 v2.0.0 +) +` + env.SaveBuffer("a/go.mod") + env.Await(EmptyDiagnostics("a/main.go")) + if got := env.Editor.BufferText("a/go.mod"); got != want { + t.Fatalf("suggested fixes failed:\n%s", tests.Diff(t, want, got)) + } + }) +} + +// Reproduces golang/go#38232. +func TestUnknownRevision(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const unknown = ` +-- a/go.mod -- +module mod.com + +require ( + example.com v1.2.2 +) +-- a/main.go -- +package main + +import "example.com/blah" + +func main() { + var x = blah.Name +} +` + + runner := RunMultiple{ + {"default", WithOptions(ProxyFiles(proxy), WorkspaceFolders("a"))}, + {"nested", WithOptions(ProxyFiles(proxy))}, + } + // Start from a bad state/bad IWL, and confirm that we recover. + t.Run("bad", func(t *testing.T) { + runner.Run(t, unknown, func(t *testing.T, env *Env) { + env.OpenFile("a/go.mod") + env.Await( + env.DiagnosticAtRegexp("a/go.mod", "example.com v1.2.2"), + ) + env.RegexpReplace("a/go.mod", "v1.2.2", "v1.2.3") + env.SaveBuffer("a/go.mod") // Save to trigger diagnostics. + + d := protocol.PublishDiagnosticsParams{} + env.Await( + OnceMet( + // Make sure the diagnostic mentions the new version -- the old diagnostic is in the same place. + env.DiagnosticAtRegexpWithMessage("a/go.mod", "example.com v1.2.3", "example.com@v1.2.3"), + ReadDiagnostics("a/go.mod", &d), + ), + ) + env.ApplyQuickFixes("a/go.mod", d.Diagnostics) + env.SaveBuffer("a/go.mod") // Save to trigger diagnostics. + env.Await( + EmptyDiagnostics("a/go.mod"), + env.DiagnosticAtRegexp("a/main.go", "x = "), + ) + }) + }) + + const known = ` +-- a/go.mod -- +module mod.com + +require ( + example.com v1.2.3 +) +-- a/go.sum -- +example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY= +example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo= +-- a/main.go -- +package main + +import "example.com/blah" + +func main() { + var x = blah.Name +} +` + // Start from a good state, transform to a bad state, and confirm that we + // still recover. + t.Run("good", func(t *testing.T) { + runner.Run(t, known, func(t *testing.T, env *Env) { + env.OpenFile("a/go.mod") + env.Await( + env.DiagnosticAtRegexp("a/main.go", "x = "), + ) + env.RegexpReplace("a/go.mod", "v1.2.3", "v1.2.2") + env.Editor.SaveBuffer(env.Ctx, "a/go.mod") // go.mod changes must be on disk + env.Await( + env.DiagnosticAtRegexp("a/go.mod", "example.com v1.2.2"), + ) + env.RegexpReplace("a/go.mod", "v1.2.2", "v1.2.3") + env.Editor.SaveBuffer(env.Ctx, "a/go.mod") // go.mod changes must be on disk + env.Await( + env.DiagnosticAtRegexp("a/main.go", "x = "), + ) + }) + }) +} + +// Confirm that an error in an indirect dependency of a requirement is surfaced +// as a diagnostic in the go.mod file. +func TestErrorInIndirectDependency(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const badProxy = ` +-- example.com@v1.2.3/go.mod -- +module example.com + +go 1.12 + +require random.org v1.2.3 // indirect +-- example.com@v1.2.3/blah/blah.go -- +package blah + +const Name = "Blah" +-- random.org@v1.2.3/go.mod -- +module bob.org + +go 1.12 +-- random.org@v1.2.3/blah/blah.go -- +package hello + +const Name = "Hello" +` + const module = ` +-- a/go.mod -- +module mod.com + +go 1.14 + +require example.com v1.2.3 +-- a/main.go -- +package main + +import "example.com/blah" + +func main() { + println(blah.Name) +} +` + RunMultiple{ + {"default", WithOptions(ProxyFiles(badProxy), WorkspaceFolders("a"))}, + {"nested", WithOptions(ProxyFiles(badProxy))}, + }.Run(t, module, func(t *testing.T, env *Env) { + env.OpenFile("a/go.mod") + env.Await( + env.DiagnosticAtRegexp("a/go.mod", "require example.com v1.2.3"), + ) + }) +} + +// A copy of govim's config_set_env_goflags_mod_readonly test. +func TestGovimModReadonly(t *testing.T) { + const mod = ` +-- go.mod -- +module mod.com + +go 1.13 +-- main.go -- +package main + +import "example.com/blah" + +func main() { + println(blah.Name) +} +` + WithOptions( + EditorConfig{ + Env: map[string]string{ + "GOFLAGS": "-mod=readonly", + }, + }, + ProxyFiles(proxy), + Modes(Singleton), + ).Run(t, mod, func(t *testing.T, env *Env) { + env.OpenFile("main.go") + original := env.ReadWorkspaceFile("go.mod") + env.Await( + env.DiagnosticAtRegexp("main.go", `"example.com/blah"`), + ) + got := env.ReadWorkspaceFile("go.mod") + if got != original { + t.Fatalf("go.mod file modified:\n%s", tests.Diff(t, original, got)) + } + env.RunGoCommand("get", "example.com/blah@v1.2.3") + env.RunGoCommand("mod", "tidy") + env.Await( + EmptyDiagnostics("main.go"), + ) + }) +} + +func TestMultiModuleModDiagnostics(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const mod = ` +-- a/go.mod -- +module moda.com + +go 1.14 + +require ( + example.com v1.2.3 +) +-- a/go.sum -- +example.com v1.2.3 h1:Yryq11hF02fEf2JlOS2eph+ICE2/ceevGV3C9dl5V/c= +example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo= +-- a/main.go -- +package main + +func main() {} +-- b/go.mod -- +module modb.com + +require example.com v1.2.3 + +go 1.14 +-- b/main.go -- +package main + +import "example.com/blah" + +func main() { + blah.SaySomething() +} +` + WithOptions( + ProxyFiles(workspaceProxy), + Modes(Experimental), + ).Run(t, mod, func(t *testing.T, env *Env) { + env.Await( + env.DiagnosticAtRegexpWithMessage("a/go.mod", "example.com v1.2.3", "is not used"), + ) + }) +} + +func TestModTidyWithBuildTags(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const mod = ` +-- go.mod -- +module mod.com + +go 1.14 +-- main.go -- +// +build bob + +package main + +import "example.com/blah" + +func main() { + blah.SaySomething() +} +` + WithOptions( + ProxyFiles(workspaceProxy), + EditorConfig{ + BuildFlags: []string{"-tags", "bob"}, + }, + ).Run(t, mod, func(t *testing.T, env *Env) { + env.Await( + env.DiagnosticAtRegexp("main.go", `"example.com/blah"`), + ) + }) +} + +func TestModTypoDiagnostic(t *testing.T) { + const mod = ` +-- go.mod -- +module mod.com + +go 1.12 +-- main.go -- +package main + +func main() {} +` + Run(t, mod, func(t *testing.T, env *Env) { + env.OpenFile("go.mod") + env.RegexpReplace("go.mod", "module", "modul") + env.Await( + env.DiagnosticAtRegexp("go.mod", "modul"), + ) + }) +} + +func TestSumUpdateFixesDiagnostics(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const mod = ` +-- go.mod -- +module mod.com + +go 1.12 + +require ( + example.com v1.2.3 +) +-- go.sum -- +-- main.go -- +package main + +import ( + "example.com/blah" +) + +func main() { + println(blah.Name) +} +` + WithOptions( + ProxyFiles(workspaceProxy), + ).Run(t, mod, func(t *testing.T, env *Env) { + d := &protocol.PublishDiagnosticsParams{} + env.OpenFile("go.mod") + env.Await( + OnceMet( + env.GoSumDiagnostic("go.mod", `example.com v1.2.3`), + ReadDiagnostics("go.mod", d), + ), + ) + env.ApplyQuickFixes("go.mod", d.Diagnostics) + env.SaveBuffer("go.mod") // Save to trigger diagnostics. + env.Await( + EmptyDiagnostics("go.mod"), + ) + }) +} + +// This test confirms that editing a go.mod file only causes metadata +// to be invalidated when it's saved. +func TestGoModInvalidatesOnSave(t *testing.T) { + const mod = ` +-- go.mod -- +module mod.com + +go 1.12 +-- main.go -- +package main + +func main() { + hello() +} +-- hello.go -- +package main + +func hello() {} +` + WithOptions( + // TODO(rFindley) this doesn't work in multi-module workspace mode, because + // it keeps around the last parsing modfile. Update this test to also + // exercise the workspace module. + Modes(Singleton), + ).Run(t, mod, func(t *testing.T, env *Env) { + env.OpenFile("go.mod") + env.Await(env.DoneWithOpen()) + env.RegexpReplace("go.mod", "module", "modul") + // Confirm that we still have metadata with only on-disk edits. + env.OpenFile("main.go") + file, _ := env.GoToDefinition("main.go", env.RegexpSearch("main.go", "hello")) + if filepath.Base(file) != "hello.go" { + t.Fatalf("expected definition in hello.go, got %s", file) + } + // Confirm that we no longer have metadata when the file is saved. + env.SaveBufferWithoutActions("go.mod") + _, _, err := env.Editor.GoToDefinition(env.Ctx, "main.go", env.RegexpSearch("main.go", "hello")) + if err == nil { + t.Fatalf("expected error, got none") + } + }) +} + +func TestRemoveUnusedDependency(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const proxy = ` +-- hasdep.com@v1.2.3/go.mod -- +module hasdep.com + +go 1.12 + +require example.com v1.2.3 +-- hasdep.com@v1.2.3/a/a.go -- +package a +-- example.com@v1.2.3/go.mod -- +module example.com + +go 1.12 +-- example.com@v1.2.3/blah/blah.go -- +package blah + +const Name = "Blah" +-- random.com@v1.2.3/go.mod -- +module random.com + +go 1.12 +-- random.com@v1.2.3/blah/blah.go -- +package blah + +const Name = "Blah" +` + t.Run("almost tidied", func(t *testing.T) { + const mod = ` +-- go.mod -- +module mod.com + +go 1.12 + +require hasdep.com v1.2.3 +-- go.sum -- +example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY= +example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo= +hasdep.com v1.2.3 h1:00y+N5oD+SpKoqV1zP2VOPawcW65Zb9NebANY3GSzGI= +hasdep.com v1.2.3/go.mod h1:ePVZOlez+KZEOejfLPGL2n4i8qiAjrkhQZ4wcImqAes= +-- main.go -- +package main + +func main() {} +` + WithOptions( + ProxyFiles(proxy), + ).Run(t, mod, func(t *testing.T, env *Env) { + env.OpenFile("go.mod") + d := &protocol.PublishDiagnosticsParams{} + env.Await( + OnceMet( + env.DiagnosticAtRegexp("go.mod", "require hasdep.com v1.2.3"), + ReadDiagnostics("go.mod", d), + ), + ) + const want = `module mod.com + +go 1.12 +` + env.ApplyQuickFixes("go.mod", d.Diagnostics) + if got := env.Editor.BufferText("go.mod"); got != want { + t.Fatalf("unexpected content in go.mod:\n%s", tests.Diff(t, want, got)) + } + }) + }) + + t.Run("not tidied", func(t *testing.T) { + const mod = ` +-- go.mod -- +module mod.com + +go 1.12 + +require hasdep.com v1.2.3 +require random.com v1.2.3 +-- go.sum -- +example.com v1.2.3 h1:ihBTGWGjTU3V4ZJ9OmHITkU9WQ4lGdQkMjgyLFk0FaY= +example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo= +hasdep.com v1.2.3 h1:00y+N5oD+SpKoqV1zP2VOPawcW65Zb9NebANY3GSzGI= +hasdep.com v1.2.3/go.mod h1:ePVZOlez+KZEOejfLPGL2n4i8qiAjrkhQZ4wcImqAes= +random.com v1.2.3 h1:PzYTykzqqH6+qU0dIgh9iPFbfb4Mm8zNBjWWreRKtx0= +random.com v1.2.3/go.mod h1:8EGj+8a4Hw1clAp8vbaeHAsKE4sbm536FP7nKyXO+qQ= +-- main.go -- +package main + +func main() {} +` + WithOptions( + ProxyFiles(proxy), + ).Run(t, mod, func(t *testing.T, env *Env) { + d := &protocol.PublishDiagnosticsParams{} + env.OpenFile("go.mod") + pos := env.RegexpSearch("go.mod", "require hasdep.com v1.2.3") + env.Await( + OnceMet( + DiagnosticAt("go.mod", pos.Line, pos.Column), + ReadDiagnostics("go.mod", d), + ), + ) + const want = `module mod.com + +go 1.12 + +require random.com v1.2.3 +` + var diagnostics []protocol.Diagnostic + for _, d := range d.Diagnostics { + if d.Range.Start.Line != uint32(pos.Line) { + continue + } + diagnostics = append(diagnostics, d) + } + env.ApplyQuickFixes("go.mod", diagnostics) + if got := env.Editor.BufferText("go.mod"); got != want { + t.Fatalf("unexpected content in go.mod:\n%s", tests.Diff(t, want, got)) + } + }) + }) +} + +func TestSumUpdateQuickFix(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + const mod = ` +-- go.mod -- +module mod.com + +go 1.12 + +require ( + example.com v1.2.3 +) +-- go.sum -- +-- main.go -- +package main + +import ( + "example.com/blah" +) + +func main() { + blah.Hello() +} +` + WithOptions( + ProxyFiles(workspaceProxy), + Modes(Singleton), + ).Run(t, mod, func(t *testing.T, env *Env) { + env.OpenFile("go.mod") + params := &protocol.PublishDiagnosticsParams{} + env.Await( + OnceMet( + env.GoSumDiagnostic("go.mod", "example.com"), + ReadDiagnostics("go.mod", params), + ), + ) + env.ApplyQuickFixes("go.mod", params.Diagnostics) + const want = `example.com v1.2.3 h1:Yryq11hF02fEf2JlOS2eph+ICE2/ceevGV3C9dl5V/c= +example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo= +` + if got := env.ReadWorkspaceFile("go.sum"); got != want { + t.Fatalf("unexpected go.sum contents:\n%s", tests.Diff(t, want, got)) + } + }) +} + +func TestDownloadDeps(t *testing.T) { + testenv.NeedsGo1Point(t, 14) + + const proxy = ` +-- example.com@v1.2.3/go.mod -- +module example.com + +go 1.12 + +require random.org v1.2.3 +-- example.com@v1.2.3/blah/blah.go -- +package blah + +import "random.org/bye" + +func SaySomething() { + bye.Goodbye() +} +-- random.org@v1.2.3/go.mod -- +module random.org + +go 1.12 +-- random.org@v1.2.3/bye/bye.go -- +package bye + +func Goodbye() { + println("Bye") +} +` + + const mod = ` +-- go.mod -- +module mod.com + +go 1.12 +-- go.sum -- +-- main.go -- +package main + +import ( + "example.com/blah" +) + +func main() { + blah.SaySomething() +} +` + WithOptions( + ProxyFiles(proxy), + Modes(Singleton), + ).Run(t, mod, func(t *testing.T, env *Env) { + env.OpenFile("main.go") + d := &protocol.PublishDiagnosticsParams{} + env.Await( + env.DiagnosticAtRegexpWithMessage("main.go", `"example.com/blah"`, `could not import example.com/blah (no required module provides package "example.com/blah")`), + ReadDiagnostics("main.go", d), + ) + env.ApplyQuickFixes("main.go", d.Diagnostics) + env.Await( + EmptyDiagnostics("main.go"), + NoDiagnostics("go.mod"), + ) + }) +}