golang.org/x/tools/gopls@v0.15.3/internal/test/integration/workspace/broken_test.go (about) 1 // Copyright 2022 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 workspace 6 7 import ( 8 "strings" 9 "testing" 10 11 "golang.org/x/tools/gopls/internal/server" 12 . "golang.org/x/tools/gopls/internal/test/integration" 13 "golang.org/x/tools/internal/testenv" 14 ) 15 16 // This file holds various tests for UX with respect to broken workspaces. 17 // 18 // TODO: consolidate other tests here. 19 // 20 // TODO: write more tests: 21 // - an explicit GOWORK value that doesn't exist 22 // - using modules and/or GOWORK inside of GOPATH? 23 24 // Test for golang/go#53933 25 func TestBrokenWorkspace_DuplicateModules(t *testing.T) { 26 // The go command error message was improved in Go 1.20 to mention multiple 27 // modules. 28 testenv.NeedsGo1Point(t, 20) 29 30 // This proxy module content is replaced by the workspace, but is still 31 // required for module resolution to function in the Go command. 32 const proxy = ` 33 -- example.com/foo@v0.0.1/go.mod -- 34 module example.com/foo 35 36 go 1.12 37 ` 38 39 const src = ` 40 -- go.work -- 41 go 1.18 42 43 use ( 44 ./package1 45 ./package1/vendor/example.com/foo 46 ./package2 47 ./package2/vendor/example.com/foo 48 ) 49 50 -- package1/go.mod -- 51 module mod.test 52 53 go 1.18 54 55 require example.com/foo v0.0.1 56 -- package1/main.go -- 57 package main 58 59 import "example.com/foo" 60 61 func main() { 62 _ = foo.CompleteMe 63 } 64 -- package1/vendor/example.com/foo/go.mod -- 65 module example.com/foo 66 67 go 1.18 68 -- package1/vendor/example.com/foo/foo.go -- 69 package foo 70 71 const CompleteMe = 111 72 -- package2/go.mod -- 73 module mod2.test 74 75 go 1.18 76 77 require example.com/foo v0.0.1 78 -- package2/main.go -- 79 package main 80 81 import "example.com/foo" 82 83 func main() { 84 _ = foo.CompleteMe 85 } 86 -- package2/vendor/example.com/foo/go.mod -- 87 module example.com/foo 88 89 go 1.18 90 -- package2/vendor/example.com/foo/foo.go -- 91 package foo 92 93 const CompleteMe = 222 94 ` 95 96 WithOptions( 97 ProxyFiles(proxy), 98 ).Run(t, src, func(t *testing.T, env *Env) { 99 env.OpenFile("package1/main.go") 100 env.AfterChange( 101 OutstandingWork(server.WorkspaceLoadFailure, `module example.com/foo appears multiple times in workspace`), 102 ) 103 104 // Remove the redundant vendored copy of example.com. 105 env.WriteWorkspaceFile("go.work", `go 1.18 106 use ( 107 ./package1 108 ./package2 109 ./package2/vendor/example.com/foo 110 ) 111 `) 112 env.AfterChange(NoOutstandingWork(IgnoreTelemetryPromptWork)) 113 114 // Check that definitions in package1 go to the copy vendored in package2. 115 location := string(env.GoToDefinition(env.RegexpSearch("package1/main.go", "CompleteMe")).URI) 116 const wantLocation = "package2/vendor/example.com/foo/foo.go" 117 if !strings.HasSuffix(location, wantLocation) { 118 t.Errorf("got definition of CompleteMe at %q, want %q", location, wantLocation) 119 } 120 }) 121 } 122 123 // Test for golang/go#43186: correcting the module path should fix errors 124 // without restarting gopls. 125 func TestBrokenWorkspace_WrongModulePath(t *testing.T) { 126 const files = ` 127 -- go.mod -- 128 module mod.testx 129 130 go 1.18 131 -- p/internal/foo/foo.go -- 132 package foo 133 134 const C = 1 135 -- p/internal/bar/bar.go -- 136 package bar 137 138 import "mod.test/p/internal/foo" 139 140 const D = foo.C + 1 141 -- p/internal/bar/bar_test.go -- 142 package bar_test 143 144 import ( 145 "mod.test/p/internal/foo" 146 . "mod.test/p/internal/bar" 147 ) 148 149 const E = D + foo.C 150 -- p/internal/baz/baz_test.go -- 151 package baz_test 152 153 import ( 154 named "mod.test/p/internal/bar" 155 ) 156 157 const F = named.D - 3 158 ` 159 160 Run(t, files, func(t *testing.T, env *Env) { 161 env.OpenFile("p/internal/bar/bar.go") 162 env.AfterChange( 163 Diagnostics(env.AtRegexp("p/internal/bar/bar.go", "\"mod.test/p/internal/foo\"")), 164 ) 165 env.OpenFile("go.mod") 166 env.RegexpReplace("go.mod", "mod.testx", "mod.test") 167 env.SaveBuffer("go.mod") // saving triggers a reload 168 env.AfterChange(NoDiagnostics()) 169 }) 170 } 171 172 func TestMultipleModules_Warning(t *testing.T) { 173 t.Skip("temporary skip for golang/go#57979: revisit after zero-config logic is in place") 174 175 msgForVersion := func(ver int) string { 176 if ver >= 18 { 177 return `gopls was not able to find modules in your workspace.` 178 } else { 179 return `gopls requires a module at the root of your workspace.` 180 } 181 } 182 183 const modules = ` 184 -- a/go.mod -- 185 module a.com 186 187 go 1.12 188 -- a/a.go -- 189 package a 190 -- a/empty.go -- 191 // an empty file 192 -- b/go.mod -- 193 module b.com 194 195 go 1.12 196 -- b/b.go -- 197 package b 198 ` 199 for _, go111module := range []string{"on", "auto"} { 200 t.Run("GO111MODULE="+go111module, func(t *testing.T) { 201 WithOptions( 202 Modes(Default), 203 EnvVars{"GO111MODULE": go111module}, 204 ).Run(t, modules, func(t *testing.T, env *Env) { 205 ver := env.GoVersion() 206 msg := msgForVersion(ver) 207 env.OpenFile("a/a.go") 208 env.OpenFile("a/empty.go") 209 env.OpenFile("b/go.mod") 210 env.AfterChange( 211 Diagnostics(env.AtRegexp("a/a.go", "package a")), 212 Diagnostics(env.AtRegexp("b/go.mod", "module b.com")), 213 OutstandingWork(server.WorkspaceLoadFailure, msg), 214 ) 215 216 // Changing the workspace folders to the valid modules should resolve 217 // the workspace errors and diagnostics. 218 // 219 // TODO(rfindley): verbose work tracking doesn't follow changing the 220 // workspace folder, therefore we can't invoke AfterChange here. 221 env.ChangeWorkspaceFolders("a", "b") 222 env.Await( 223 NoDiagnostics(ForFile("a/a.go")), 224 NoDiagnostics(ForFile("b/go.mod")), 225 NoOutstandingWork(IgnoreTelemetryPromptWork), 226 ) 227 228 env.ChangeWorkspaceFolders(".") 229 230 // TODO(rfindley): when GO111MODULE=auto, we need to open or change a 231 // file here in order to detect a critical error. This is because gopls 232 // has forgotten about a/a.go, and therefore doesn't hit the heuristic 233 // "all packages are command-line-arguments". 234 // 235 // This is broken, and could be fixed by adjusting the heuristic to 236 // account for the scenario where there are *no* workspace packages, or 237 // (better) trying to get workspace packages for each open file. See 238 // also golang/go#54261. 239 env.OpenFile("b/b.go") 240 env.AfterChange( 241 // TODO(rfindley): fix these missing diagnostics. 242 // Diagnostics(env.AtRegexp("a/a.go", "package a")), 243 // Diagnostics(env.AtRegexp("b/go.mod", "module b.com")), 244 Diagnostics(env.AtRegexp("b/b.go", "package b")), 245 OutstandingWork(server.WorkspaceLoadFailure, msg), 246 ) 247 }) 248 }) 249 } 250 251 // Expect no warning if GO111MODULE=auto in a directory in GOPATH. 252 t.Run("GOPATH_GO111MODULE_auto", func(t *testing.T) { 253 WithOptions( 254 Modes(Default), 255 EnvVars{"GO111MODULE": "auto"}, 256 InGOPATH(), 257 ).Run(t, modules, func(t *testing.T, env *Env) { 258 env.OpenFile("a/a.go") 259 env.AfterChange( 260 NoDiagnostics(ForFile("a/a.go")), 261 NoOutstandingWork(IgnoreTelemetryPromptWork), 262 ) 263 }) 264 }) 265 }