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  }