golang.org/x/tools/gopls@v0.15.3/internal/test/integration/diagnostics/diagnostics_test.go (about)

     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 diagnostics
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"os/exec"
    11  	"testing"
    12  
    13  	"golang.org/x/tools/gopls/internal/hooks"
    14  	"golang.org/x/tools/gopls/internal/protocol"
    15  	"golang.org/x/tools/gopls/internal/server"
    16  	. "golang.org/x/tools/gopls/internal/test/integration"
    17  	"golang.org/x/tools/gopls/internal/test/integration/fake"
    18  	"golang.org/x/tools/gopls/internal/util/bug"
    19  	"golang.org/x/tools/gopls/internal/util/goversion"
    20  	"golang.org/x/tools/internal/testenv"
    21  )
    22  
    23  func TestMain(m *testing.M) {
    24  	bug.PanicOnBugs = true
    25  	Main(m, hooks.Options)
    26  }
    27  
    28  // Use mod.com for all go.mod files due to golang/go#35230.
    29  const exampleProgram = `
    30  -- go.mod --
    31  module mod.com
    32  
    33  go 1.12
    34  -- main.go --
    35  package main
    36  
    37  import "fmt"
    38  
    39  func main() {
    40  	fmt.Println("Hello World.")
    41  }`
    42  
    43  func TestDiagnosticErrorInEditedFile(t *testing.T) {
    44  	// This test is very basic: start with a clean Go program, make an error, and
    45  	// get a diagnostic for that error. However, it also demonstrates how to
    46  	// combine Expectations to await more complex state in the editor.
    47  	Run(t, exampleProgram, func(t *testing.T, env *Env) {
    48  		// Deleting the 'n' at the end of Println should generate a single error
    49  		// diagnostic.
    50  		env.OpenFile("main.go")
    51  		env.RegexpReplace("main.go", "Printl(n)", "")
    52  		env.AfterChange(
    53  			Diagnostics(env.AtRegexp("main.go", "Printl")),
    54  			// Assert that this test has sent no error logs to the client. This is not
    55  			// strictly necessary for testing this regression, but is included here
    56  			// as an example of using the NoErrorLogs() expectation. Feel free to
    57  			// delete.
    58  			NoErrorLogs(),
    59  		)
    60  	})
    61  }
    62  
    63  func TestMissingImportDiagsClearOnFirstFile(t *testing.T) {
    64  	const onlyMod = `
    65  -- go.mod --
    66  module mod.com
    67  
    68  go 1.12
    69  `
    70  	Run(t, onlyMod, func(t *testing.T, env *Env) {
    71  		env.CreateBuffer("main.go", `package main
    72  
    73  func m() {
    74  	log.Println()
    75  }
    76  `)
    77  		env.AfterChange(Diagnostics(env.AtRegexp("main.go", "log")))
    78  		env.SaveBuffer("main.go")
    79  		env.AfterChange(NoDiagnostics(ForFile("main.go")))
    80  	})
    81  }
    82  
    83  func TestDiagnosticErrorInNewFile(t *testing.T) {
    84  	const brokenFile = `package main
    85  
    86  const Foo = "abc
    87  `
    88  	Run(t, brokenFile, func(t *testing.T, env *Env) {
    89  		env.CreateBuffer("broken.go", brokenFile)
    90  		env.AfterChange(Diagnostics(env.AtRegexp("broken.go", "\"abc")))
    91  	})
    92  }
    93  
    94  // badPackage contains a duplicate definition of the 'a' const.
    95  const badPackage = `
    96  -- go.mod --
    97  module mod.com
    98  
    99  go 1.12
   100  -- a.go --
   101  package consts
   102  
   103  const a = 1
   104  -- b.go --
   105  package consts
   106  
   107  const a = 2
   108  `
   109  
   110  func TestDiagnosticClearingOnEdit(t *testing.T) {
   111  	Run(t, badPackage, func(t *testing.T, env *Env) {
   112  		env.OpenFile("b.go")
   113  		env.AfterChange(
   114  			Diagnostics(env.AtRegexp("a.go", "a = 1")),
   115  			Diagnostics(env.AtRegexp("b.go", "a = 2")),
   116  		)
   117  
   118  		// Fix the error by editing the const name in b.go to `b`.
   119  		env.RegexpReplace("b.go", "(a) = 2", "b")
   120  		env.AfterChange(
   121  			NoDiagnostics(ForFile("a.go")),
   122  			NoDiagnostics(ForFile("b.go")),
   123  		)
   124  	})
   125  }
   126  
   127  func TestDiagnosticClearingOnDelete_Issue37049(t *testing.T) {
   128  	Run(t, badPackage, func(t *testing.T, env *Env) {
   129  		env.OpenFile("a.go")
   130  		env.AfterChange(
   131  			Diagnostics(env.AtRegexp("a.go", "a = 1")),
   132  			Diagnostics(env.AtRegexp("b.go", "a = 2")),
   133  		)
   134  		env.RemoveWorkspaceFile("b.go")
   135  
   136  		env.AfterChange(
   137  			NoDiagnostics(ForFile("a.go")),
   138  			NoDiagnostics(ForFile("b.go")),
   139  		)
   140  	})
   141  }
   142  
   143  func TestDiagnosticClearingOnClose(t *testing.T) {
   144  	Run(t, badPackage, func(t *testing.T, env *Env) {
   145  		env.CreateBuffer("c.go", `package consts
   146  
   147  const a = 3`)
   148  		env.AfterChange(
   149  			Diagnostics(env.AtRegexp("a.go", "a = 1")),
   150  			Diagnostics(env.AtRegexp("b.go", "a = 2")),
   151  			Diagnostics(env.AtRegexp("c.go", "a = 3")),
   152  		)
   153  		env.CloseBuffer("c.go")
   154  		env.AfterChange(
   155  			Diagnostics(env.AtRegexp("a.go", "a = 1")),
   156  			Diagnostics(env.AtRegexp("b.go", "a = 2")),
   157  			NoDiagnostics(ForFile("c.go")),
   158  		)
   159  	})
   160  }
   161  
   162  // Tests golang/go#37978.
   163  func TestIssue37978(t *testing.T) {
   164  	Run(t, exampleProgram, func(t *testing.T, env *Env) {
   165  		// Create a new workspace-level directory and empty file.
   166  		env.CreateBuffer("c/c.go", "")
   167  
   168  		// Write the file contents with a missing import.
   169  		env.EditBuffer("c/c.go", protocol.TextEdit{
   170  			NewText: `package c
   171  
   172  const a = http.MethodGet
   173  `,
   174  		})
   175  		env.AfterChange(
   176  			Diagnostics(env.AtRegexp("c/c.go", "http.MethodGet")),
   177  		)
   178  		// Save file, which will organize imports, adding the expected import.
   179  		// Expect the diagnostics to clear.
   180  		env.SaveBuffer("c/c.go")
   181  		env.AfterChange(
   182  			NoDiagnostics(ForFile("c/c.go")),
   183  		)
   184  	})
   185  }
   186  
   187  // Tests golang/go#38878: good a.go, bad a_test.go, remove a_test.go but its errors remain
   188  // If the file is open in the editor, this is working as intended
   189  // If the file is not open in the editor, the errors go away
   190  const test38878 = `
   191  -- go.mod --
   192  module foo
   193  
   194  go 1.12
   195  -- a.go --
   196  package x
   197  
   198  // import "fmt"
   199  
   200  func f() {}
   201  
   202  -- a_test.go --
   203  package x
   204  
   205  import "testing"
   206  
   207  func TestA(t *testing.T) {
   208  	f(3)
   209  }
   210  `
   211  
   212  // Tests golang/go#38878: deleting a test file should clear its errors, and
   213  // not break the workspace.
   214  func TestDeleteTestVariant(t *testing.T) {
   215  	Run(t, test38878, func(t *testing.T, env *Env) {
   216  		env.AfterChange(Diagnostics(env.AtRegexp("a_test.go", `f\((3)\)`)))
   217  		env.RemoveWorkspaceFile("a_test.go")
   218  		env.AfterChange(NoDiagnostics(ForFile("a_test.go")))
   219  
   220  		// Make sure the test variant has been removed from the workspace by
   221  		// triggering a metadata load.
   222  		env.OpenFile("a.go")
   223  		env.RegexpReplace("a.go", `// import`, "import")
   224  		env.AfterChange(Diagnostics(env.AtRegexp("a.go", `"fmt"`)))
   225  	})
   226  }
   227  
   228  // Tests golang/go#38878: deleting a test file on disk while it's still open
   229  // should not clear its errors.
   230  func TestDeleteTestVariant_DiskOnly(t *testing.T) {
   231  	Run(t, test38878, func(t *testing.T, env *Env) {
   232  		env.OpenFile("a_test.go")
   233  		env.AfterChange(Diagnostics(AtPosition("a_test.go", 5, 3)))
   234  		env.Sandbox.Workdir.RemoveFile(context.Background(), "a_test.go")
   235  		env.AfterChange(Diagnostics(AtPosition("a_test.go", 5, 3)))
   236  	})
   237  }
   238  
   239  // TestNoMod confirms that gopls continues to work when a user adds a go.mod
   240  // file to their workspace.
   241  func TestNoMod(t *testing.T) {
   242  	const noMod = `
   243  -- main.go --
   244  package main
   245  
   246  import "mod.com/bob"
   247  
   248  func main() {
   249  	bob.Hello()
   250  }
   251  -- bob/bob.go --
   252  package bob
   253  
   254  func Hello() {
   255  	var x int
   256  }
   257  `
   258  
   259  	t.Run("manual", func(t *testing.T) {
   260  		Run(t, noMod, func(t *testing.T, env *Env) {
   261  			env.OnceMet(
   262  				InitialWorkspaceLoad,
   263  				Diagnostics(env.AtRegexp("main.go", `"mod.com/bob"`)),
   264  			)
   265  			env.CreateBuffer("go.mod", `module mod.com
   266  
   267  	go 1.12
   268  `)
   269  			env.SaveBuffer("go.mod")
   270  			var d protocol.PublishDiagnosticsParams
   271  			env.AfterChange(
   272  				NoDiagnostics(ForFile("main.go")),
   273  				Diagnostics(env.AtRegexp("bob/bob.go", "x")),
   274  				ReadDiagnostics("bob/bob.go", &d),
   275  			)
   276  			if len(d.Diagnostics) != 1 {
   277  				t.Fatalf("expected 1 diagnostic, got %v", len(d.Diagnostics))
   278  			}
   279  		})
   280  	})
   281  	t.Run("initialized", func(t *testing.T) {
   282  		Run(t, noMod, func(t *testing.T, env *Env) {
   283  			env.OnceMet(
   284  				InitialWorkspaceLoad,
   285  				Diagnostics(env.AtRegexp("main.go", `"mod.com/bob"`)),
   286  			)
   287  			env.RunGoCommand("mod", "init", "mod.com")
   288  			env.AfterChange(
   289  				NoDiagnostics(ForFile("main.go")),
   290  				Diagnostics(env.AtRegexp("bob/bob.go", "x")),
   291  			)
   292  		})
   293  	})
   294  
   295  	t.Run("without workspace module", func(t *testing.T) {
   296  		WithOptions(
   297  			Modes(Default),
   298  		).Run(t, noMod, func(t *testing.T, env *Env) {
   299  			env.OnceMet(
   300  				InitialWorkspaceLoad,
   301  				Diagnostics(env.AtRegexp("main.go", `"mod.com/bob"`)),
   302  			)
   303  			if err := env.Sandbox.RunGoCommand(env.Ctx, "", "mod", []string{"init", "mod.com"}, nil, true); err != nil {
   304  				t.Fatal(err)
   305  			}
   306  			env.AfterChange(
   307  				NoDiagnostics(ForFile("main.go")),
   308  				Diagnostics(env.AtRegexp("bob/bob.go", "x")),
   309  			)
   310  		})
   311  	})
   312  }
   313  
   314  // Tests golang/go#38267.
   315  func TestIssue38267(t *testing.T) {
   316  	const testPackage = `
   317  -- go.mod --
   318  module mod.com
   319  
   320  go 1.12
   321  -- lib.go --
   322  package lib
   323  
   324  func Hello(x string) {
   325  	_ = x
   326  }
   327  -- lib_test.go --
   328  package lib
   329  
   330  import "testing"
   331  
   332  type testStruct struct{
   333  	name string
   334  }
   335  
   336  func TestHello(t *testing.T) {
   337  	testStructs := []*testStruct{
   338  		&testStruct{"hello"},
   339  		&testStruct{"goodbye"},
   340  	}
   341  	for y := range testStructs {
   342  		_ = y
   343  	}
   344  }
   345  `
   346  
   347  	Run(t, testPackage, func(t *testing.T, env *Env) {
   348  		env.OpenFile("lib_test.go")
   349  		env.AfterChange(
   350  			Diagnostics(AtPosition("lib_test.go", 10, 2)),
   351  			Diagnostics(AtPosition("lib_test.go", 11, 2)),
   352  		)
   353  		env.OpenFile("lib.go")
   354  		env.RegexpReplace("lib.go", "_ = x", "var y int")
   355  		env.AfterChange(
   356  			Diagnostics(env.AtRegexp("lib.go", "y int")),
   357  			NoDiagnostics(ForFile("lib_test.go")),
   358  		)
   359  	})
   360  }
   361  
   362  // Tests golang/go#38328.
   363  func TestPackageChange_Issue38328(t *testing.T) {
   364  	const packageChange = `
   365  -- go.mod --
   366  module fake
   367  
   368  go 1.12
   369  -- a.go --
   370  package foo
   371  func main() {}
   372  `
   373  	Run(t, packageChange, func(t *testing.T, env *Env) {
   374  		env.OpenFile("a.go")
   375  		env.RegexpReplace("a.go", "foo", "foox")
   376  		env.AfterChange(
   377  			NoDiagnostics(ForFile("a.go")),
   378  		)
   379  	})
   380  }
   381  
   382  const testPackageWithRequire = `
   383  -- go.mod --
   384  module mod.com
   385  
   386  go 1.12
   387  
   388  require foo.test v1.2.3
   389  -- go.sum --
   390  foo.test v1.2.3 h1:TMA+lyd1ck0TqjSFpNe4T6cf/K6TYkoHwOOcMBMjaEw=
   391  foo.test v1.2.3/go.mod h1:Ij3kyLIe5lzjycjh13NL8I2gX0quZuTdW0MnmlwGBL4=
   392  -- print.go --
   393  package lib
   394  
   395  import (
   396  	"fmt"
   397  
   398  	"foo.test/bar"
   399  )
   400  
   401  func PrintAnswer() {
   402  	fmt.Printf("answer: %s", bar.Answer)
   403  }
   404  `
   405  
   406  const testPackageWithRequireProxy = `
   407  -- foo.test@v1.2.3/go.mod --
   408  module foo.test
   409  
   410  go 1.12
   411  -- foo.test@v1.2.3/bar/const.go --
   412  package bar
   413  
   414  const Answer = 42
   415  `
   416  
   417  func TestResolveDiagnosticWithDownload(t *testing.T) {
   418  	WithOptions(
   419  		ProxyFiles(testPackageWithRequireProxy),
   420  	).Run(t, testPackageWithRequire, func(t *testing.T, env *Env) {
   421  		env.OpenFile("print.go")
   422  		// Check that gopackages correctly loaded this dependency. We should get a
   423  		// diagnostic for the wrong formatting type.
   424  		env.AfterChange(
   425  			Diagnostics(
   426  				env.AtRegexp("print.go", "fmt.Printf"),
   427  				WithMessage("wrong type int"),
   428  			),
   429  		)
   430  	})
   431  }
   432  
   433  func TestMissingDependency(t *testing.T) {
   434  	Run(t, testPackageWithRequire, func(t *testing.T, env *Env) {
   435  		env.OpenFile("print.go")
   436  		env.Await(
   437  			// Log messages are asynchronous to other events on the LSP stream, so we
   438  			// can't use OnceMet or AfterChange here.
   439  			LogMatching(protocol.Error, "initial workspace load failed", 1, false),
   440  		)
   441  	})
   442  }
   443  
   444  // Tests golang/go#36951.
   445  func TestAdHocPackages_Issue36951(t *testing.T) {
   446  	const adHoc = `
   447  -- b/b.go --
   448  package b
   449  
   450  func Hello() {
   451  	var x int
   452  }
   453  `
   454  	Run(t, adHoc, func(t *testing.T, env *Env) {
   455  		env.OpenFile("b/b.go")
   456  		env.AfterChange(
   457  			Diagnostics(env.AtRegexp("b/b.go", "x")),
   458  		)
   459  	})
   460  }
   461  
   462  // Tests golang/go#37984: GOPATH should be read from the go command.
   463  func TestNoGOPATH_Issue37984(t *testing.T) {
   464  	const files = `
   465  -- main.go --
   466  package main
   467  
   468  func _() {
   469  	fmt.Println("Hello World")
   470  }
   471  `
   472  	WithOptions(
   473  		EnvVars{
   474  			"GOPATH":      "",
   475  			"GO111MODULE": "off",
   476  		},
   477  	).Run(t, files, func(t *testing.T, env *Env) {
   478  		env.OpenFile("main.go")
   479  		env.AfterChange(Diagnostics(env.AtRegexp("main.go", "fmt")))
   480  		env.SaveBuffer("main.go")
   481  		env.AfterChange(NoDiagnostics(ForFile("main.go")))
   482  	})
   483  }
   484  
   485  // Tests golang/go#38669.
   486  func TestEqualInEnv_Issue38669(t *testing.T) {
   487  	const files = `
   488  -- go.mod --
   489  module mod.com
   490  
   491  go 1.12
   492  -- main.go --
   493  package main
   494  
   495  var _ = x.X
   496  -- x/x.go --
   497  package x
   498  
   499  var X = 0
   500  `
   501  	WithOptions(
   502  		EnvVars{"GOFLAGS": "-tags=foo"},
   503  	).Run(t, files, func(t *testing.T, env *Env) {
   504  		env.OpenFile("main.go")
   505  		env.OrganizeImports("main.go")
   506  		env.AfterChange(NoDiagnostics(ForFile("main.go")))
   507  	})
   508  }
   509  
   510  // Tests golang/go#38467.
   511  func TestNoSuggestedFixesForGeneratedFiles_Issue38467(t *testing.T) {
   512  	const generated = `
   513  -- go.mod --
   514  module mod.com
   515  
   516  go 1.12
   517  -- main.go --
   518  package main
   519  
   520  // Code generated by generator.go. DO NOT EDIT.
   521  
   522  func _() {
   523  	for i, _ := range []string{} {
   524  		_ = i
   525  	}
   526  }
   527  `
   528  	Run(t, generated, func(t *testing.T, env *Env) {
   529  		env.OpenFile("main.go")
   530  		var d protocol.PublishDiagnosticsParams
   531  		env.AfterChange(
   532  			Diagnostics(AtPosition("main.go", 5, 8)),
   533  			ReadDiagnostics("main.go", &d),
   534  		)
   535  		if fixes := env.GetQuickFixes("main.go", d.Diagnostics); len(fixes) != 0 {
   536  			t.Errorf("got quick fixes %v, wanted none", fixes)
   537  		}
   538  	})
   539  }
   540  
   541  // Expect a module/GOPATH error if there is an error in the file at startup.
   542  // Tests golang/go#37279.
   543  func TestBrokenWorkspace_OutsideModule(t *testing.T) {
   544  	const noModule = `
   545  -- a.go --
   546  package foo
   547  
   548  import "mod.com/hello"
   549  
   550  func f() {
   551  	hello.Goodbye()
   552  }
   553  `
   554  	Run(t, noModule, func(t *testing.T, env *Env) {
   555  		env.OpenFile("a.go")
   556  		env.AfterChange(
   557  			// AdHoc views are not critical errors, but their missing import
   558  			// diagnostics should specifically mention GOROOT or GOPATH (and not
   559  			// modules).
   560  			NoOutstandingWork(nil),
   561  			Diagnostics(
   562  				env.AtRegexp("a.go", `"mod.com`),
   563  				WithMessage("GOROOT or GOPATH"),
   564  			),
   565  		)
   566  		// Deleting the import dismisses the warning.
   567  		env.RegexpReplace("a.go", `import "mod.com/hello"`, "")
   568  		env.AfterChange(
   569  			NoOutstandingWork(IgnoreTelemetryPromptWork),
   570  		)
   571  	})
   572  }
   573  
   574  func TestNonGoFolder(t *testing.T) {
   575  	const files = `
   576  -- hello.txt --
   577  hi mom
   578  `
   579  	for _, go111module := range []string{"on", "off", ""} {
   580  		t.Run(fmt.Sprintf("GO111MODULE_%v", go111module), func(t *testing.T) {
   581  			WithOptions(
   582  				EnvVars{"GO111MODULE": go111module},
   583  			).Run(t, files, func(t *testing.T, env *Env) {
   584  				env.OnceMet(
   585  					InitialWorkspaceLoad,
   586  					NoOutstandingWork(IgnoreTelemetryPromptWork),
   587  				)
   588  			})
   589  		})
   590  	}
   591  }
   592  
   593  // Tests the repro case from golang/go#38602. Diagnostics are now handled properly,
   594  // which blocks type checking.
   595  func TestConflictingMainPackageErrors(t *testing.T) {
   596  	const collision = `
   597  -- x/x.go --
   598  package x
   599  
   600  import "x/hello"
   601  
   602  func Hello() {
   603  	hello.HiThere()
   604  }
   605  -- x/main.go --
   606  package main
   607  
   608  func main() {
   609  	fmt.Println("")
   610  }
   611  `
   612  	WithOptions(
   613  		InGOPATH(),
   614  		EnvVars{"GO111MODULE": "off"},
   615  	).Run(t, collision, func(t *testing.T, env *Env) {
   616  		env.OpenFile("x/x.go")
   617  		env.AfterChange(
   618  			Diagnostics(env.AtRegexp("x/x.go", `^`), WithMessage("found packages main (main.go) and x (x.go)")),
   619  			Diagnostics(env.AtRegexp("x/main.go", `^`), WithMessage("found packages main (main.go) and x (x.go)")),
   620  		)
   621  
   622  		// We don't recover cleanly from the errors without good overlay support.
   623  		if testenv.Go1Point() >= 16 {
   624  			env.RegexpReplace("x/x.go", `package x`, `package main`)
   625  			env.AfterChange(
   626  				Diagnostics(env.AtRegexp("x/main.go", `fmt`)),
   627  			)
   628  		}
   629  	})
   630  }
   631  
   632  const ardanLabsProxy = `
   633  -- github.com/ardanlabs/conf@v1.2.3/go.mod --
   634  module github.com/ardanlabs/conf
   635  
   636  go 1.12
   637  -- github.com/ardanlabs/conf@v1.2.3/conf.go --
   638  package conf
   639  
   640  var ErrHelpWanted error
   641  `
   642  
   643  // Test for golang/go#38211.
   644  func Test_Issue38211(t *testing.T) {
   645  	const ardanLabs = `
   646  -- go.mod --
   647  module mod.com
   648  
   649  go 1.14
   650  -- main.go --
   651  package main
   652  
   653  import "github.com/ardanlabs/conf"
   654  
   655  func main() {
   656  	_ = conf.ErrHelpWanted
   657  }
   658  `
   659  	WithOptions(
   660  		ProxyFiles(ardanLabsProxy),
   661  	).Run(t, ardanLabs, func(t *testing.T, env *Env) {
   662  		// Expect a diagnostic with a suggested fix to add
   663  		// "github.com/ardanlabs/conf" to the go.mod file.
   664  		env.OpenFile("go.mod")
   665  		env.OpenFile("main.go")
   666  		var d protocol.PublishDiagnosticsParams
   667  		env.AfterChange(
   668  			Diagnostics(env.AtRegexp("main.go", `"github.com/ardanlabs/conf"`)),
   669  			ReadDiagnostics("main.go", &d),
   670  		)
   671  		env.ApplyQuickFixes("main.go", d.Diagnostics)
   672  		env.SaveBuffer("go.mod")
   673  		env.AfterChange(
   674  			NoDiagnostics(ForFile("main.go")),
   675  		)
   676  		// Comment out the line that depends on conf and expect a
   677  		// diagnostic and a fix to remove the import.
   678  		env.RegexpReplace("main.go", "_ = conf.ErrHelpWanted", "//_ = conf.ErrHelpWanted")
   679  		env.AfterChange(
   680  			Diagnostics(env.AtRegexp("main.go", `"github.com/ardanlabs/conf"`)),
   681  		)
   682  		env.SaveBuffer("main.go")
   683  		// Expect a diagnostic and fix to remove the dependency in the go.mod.
   684  		env.AfterChange(
   685  			NoDiagnostics(ForFile("main.go")),
   686  			Diagnostics(env.AtRegexp("go.mod", "require github.com/ardanlabs/conf"), WithMessage("not used in this module")),
   687  			ReadDiagnostics("go.mod", &d),
   688  		)
   689  		env.ApplyQuickFixes("go.mod", d.Diagnostics)
   690  		env.SaveBuffer("go.mod")
   691  		env.AfterChange(
   692  			NoDiagnostics(ForFile("go.mod")),
   693  		)
   694  		// Uncomment the lines and expect a new diagnostic for the import.
   695  		env.RegexpReplace("main.go", "//_ = conf.ErrHelpWanted", "_ = conf.ErrHelpWanted")
   696  		env.SaveBuffer("main.go")
   697  		env.AfterChange(
   698  			Diagnostics(env.AtRegexp("main.go", `"github.com/ardanlabs/conf"`)),
   699  		)
   700  	})
   701  }
   702  
   703  // Test for golang/go#38207.
   704  func TestNewModule_Issue38207(t *testing.T) {
   705  	const emptyFile = `
   706  -- go.mod --
   707  module mod.com
   708  
   709  go 1.12
   710  -- main.go --
   711  `
   712  	WithOptions(
   713  		ProxyFiles(ardanLabsProxy),
   714  	).Run(t, emptyFile, func(t *testing.T, env *Env) {
   715  		env.CreateBuffer("main.go", `package main
   716  
   717  import "github.com/ardanlabs/conf"
   718  
   719  func main() {
   720  	_ = conf.ErrHelpWanted
   721  }
   722  `)
   723  		env.SaveBuffer("main.go")
   724  		var d protocol.PublishDiagnosticsParams
   725  		env.AfterChange(
   726  			Diagnostics(env.AtRegexp("main.go", `"github.com/ardanlabs/conf"`), WithMessage("no required module")),
   727  			ReadDiagnostics("main.go", &d),
   728  		)
   729  		env.ApplyQuickFixes("main.go", d.Diagnostics)
   730  		env.AfterChange(
   731  			NoDiagnostics(ForFile("main.go")),
   732  		)
   733  	})
   734  }
   735  
   736  // Test for golang/go#36960.
   737  func TestNewFileBadImports_Issue36960(t *testing.T) {
   738  	const simplePackage = `
   739  -- go.mod --
   740  module mod.com
   741  
   742  go 1.14
   743  -- a/a1.go --
   744  package a
   745  
   746  import "fmt"
   747  
   748  func _() {
   749  	fmt.Println("hi")
   750  }
   751  `
   752  	Run(t, simplePackage, func(t *testing.T, env *Env) {
   753  		env.OpenFile("a/a1.go")
   754  		env.CreateBuffer("a/a2.go", ``)
   755  		env.SaveBufferWithoutActions("a/a2.go")
   756  		env.AfterChange(
   757  			NoDiagnostics(ForFile("a/a1.go")),
   758  		)
   759  		env.EditBuffer("a/a2.go", fake.NewEdit(0, 0, 0, 0, `package a`))
   760  		env.AfterChange(
   761  			NoDiagnostics(ForFile("a/a1.go")),
   762  		)
   763  	})
   764  }
   765  
   766  // This test tries to replicate the workflow of a user creating a new x test.
   767  // It also tests golang/go#39315.
   768  func TestManuallyCreatingXTest(t *testing.T) {
   769  	// Create a package that already has a test variant (in-package test).
   770  	const testVariant = `
   771  -- go.mod --
   772  module mod.com
   773  
   774  go 1.15
   775  -- hello/hello.go --
   776  package hello
   777  
   778  func Hello() {
   779  	var x int
   780  }
   781  -- hello/hello_test.go --
   782  package hello
   783  
   784  import "testing"
   785  
   786  func TestHello(t *testing.T) {
   787  	var x int
   788  	Hello()
   789  }
   790  `
   791  	Run(t, testVariant, func(t *testing.T, env *Env) {
   792  		// Open the file, triggering the workspace load.
   793  		// There are errors in the code to ensure all is working as expected.
   794  		env.OpenFile("hello/hello.go")
   795  		env.AfterChange(
   796  			Diagnostics(env.AtRegexp("hello/hello.go", "x")),
   797  			Diagnostics(env.AtRegexp("hello/hello_test.go", "x")),
   798  		)
   799  
   800  		// Create an empty file with the intention of making it an x test.
   801  		// This resembles a typical flow in an editor like VS Code, in which
   802  		// a user would create an empty file and add content, saving
   803  		// intermittently.
   804  		// TODO(rstambler): There might be more edge cases here, as file
   805  		// content can be added incrementally.
   806  		env.CreateBuffer("hello/hello_x_test.go", ``)
   807  
   808  		// Save the empty file (no actions since formatting will fail).
   809  		env.SaveBufferWithoutActions("hello/hello_x_test.go")
   810  
   811  		// Add the content. The missing import is for the package under test.
   812  		env.EditBuffer("hello/hello_x_test.go", fake.NewEdit(0, 0, 0, 0, `package hello_test
   813  
   814  import (
   815  	"testing"
   816  )
   817  
   818  func TestHello(t *testing.T) {
   819  	hello.Hello()
   820  }
   821  `))
   822  		// Expect a diagnostic for the missing import. Save, which should
   823  		// trigger import organization. The diagnostic should clear.
   824  		env.AfterChange(
   825  			Diagnostics(env.AtRegexp("hello/hello_x_test.go", "hello.Hello")),
   826  		)
   827  		env.SaveBuffer("hello/hello_x_test.go")
   828  		env.AfterChange(
   829  			NoDiagnostics(ForFile("hello/hello_x_test.go")),
   830  		)
   831  	})
   832  }
   833  
   834  // Reproduce golang/go#40690.
   835  func TestCreateOnlyXTest(t *testing.T) {
   836  	const mod = `
   837  -- go.mod --
   838  module mod.com
   839  
   840  go 1.12
   841  -- foo/foo.go --
   842  package foo
   843  -- foo/bar_test.go --
   844  `
   845  	Run(t, mod, func(t *testing.T, env *Env) {
   846  		env.OpenFile("foo/bar_test.go")
   847  		env.EditBuffer("foo/bar_test.go", fake.NewEdit(0, 0, 0, 0, "package foo"))
   848  		env.Await(env.DoneWithChange())
   849  		env.RegexpReplace("foo/bar_test.go", "package foo", `package foo_test
   850  
   851  import "testing"
   852  
   853  func TestX(t *testing.T) {
   854  	var x int
   855  }
   856  `)
   857  		env.AfterChange(
   858  			Diagnostics(env.AtRegexp("foo/bar_test.go", "x")),
   859  		)
   860  	})
   861  }
   862  
   863  func TestChangePackageName(t *testing.T) {
   864  	const mod = `
   865  -- go.mod --
   866  module mod.com
   867  
   868  go 1.12
   869  -- foo/foo.go --
   870  package foo
   871  -- foo/bar_test.go --
   872  package foo_
   873  `
   874  	Run(t, mod, func(t *testing.T, env *Env) {
   875  		env.OpenFile("foo/bar_test.go")
   876  		env.AfterChange()
   877  		env.RegexpReplace("foo/bar_test.go", "package foo_", "package foo_test")
   878  		env.AfterChange(
   879  			NoDiagnostics(ForFile("foo/bar_test.go")),
   880  			NoDiagnostics(ForFile("foo/foo.go")),
   881  		)
   882  	})
   883  }
   884  
   885  func TestIgnoredFiles(t *testing.T) {
   886  	const ws = `
   887  -- go.mod --
   888  module mod.com
   889  
   890  go 1.12
   891  -- _foo/x.go --
   892  package x
   893  
   894  var _ = foo.Bar
   895  `
   896  	Run(t, ws, func(t *testing.T, env *Env) {
   897  		env.OpenFile("_foo/x.go")
   898  		env.AfterChange(
   899  			NoDiagnostics(ForFile("_foo/x.go")),
   900  		)
   901  	})
   902  }
   903  
   904  // Partially reproduces golang/go#38977, moving a file between packages.
   905  // It also gets hit by some go command bug fixed in 1.15, but we don't
   906  // care about that so much here.
   907  func TestDeletePackage(t *testing.T) {
   908  	const ws = `
   909  -- go.mod --
   910  module mod.com
   911  
   912  go 1.15
   913  -- a/a.go --
   914  package a
   915  
   916  const A = 1
   917  
   918  -- b/b.go --
   919  package b
   920  
   921  import "mod.com/a"
   922  
   923  const B = a.A
   924  
   925  -- c/c.go --
   926  package c
   927  
   928  import "mod.com/a"
   929  
   930  const C = a.A
   931  `
   932  	Run(t, ws, func(t *testing.T, env *Env) {
   933  		env.OpenFile("b/b.go")
   934  		env.Await(env.DoneWithOpen())
   935  		// Delete c/c.go, the only file in package c.
   936  		env.RemoveWorkspaceFile("c/c.go")
   937  
   938  		// We should still get diagnostics for files that exist.
   939  		env.RegexpReplace("b/b.go", `a.A`, "a.Nonexistant")
   940  		env.AfterChange(
   941  			Diagnostics(env.AtRegexp("b/b.go", `Nonexistant`)),
   942  		)
   943  	})
   944  }
   945  
   946  // This is a copy of the scenario_default/quickfix_empty_files.txt test from
   947  // govim. Reproduces golang/go#39646.
   948  func TestQuickFixEmptyFiles(t *testing.T) {
   949  	const mod = `
   950  -- go.mod --
   951  module mod.com
   952  
   953  go 1.12
   954  `
   955  	// To fully recreate the govim tests, we create files by inserting
   956  	// a newline, adding to the file, and then deleting the newline.
   957  	// Wait for each event to process to avoid cancellations and force
   958  	// package loads.
   959  	writeGoVim := func(env *Env, name, content string) {
   960  		env.WriteWorkspaceFile(name, "")
   961  		env.Await(env.DoneWithChangeWatchedFiles())
   962  
   963  		env.CreateBuffer(name, "\n")
   964  		env.Await(env.DoneWithOpen())
   965  
   966  		env.EditBuffer(name, fake.NewEdit(1, 0, 1, 0, content))
   967  		env.Await(env.DoneWithChange())
   968  
   969  		env.EditBuffer(name, fake.NewEdit(0, 0, 1, 0, ""))
   970  		env.Await(env.DoneWithChange())
   971  	}
   972  
   973  	const p = `package p; func DoIt(s string) {};`
   974  	const main = `package main
   975  
   976  import "mod.com/p"
   977  
   978  func main() {
   979  	p.DoIt(5)
   980  }
   981  `
   982  	// A simple version of the test that reproduces most of the problems it
   983  	// exposes.
   984  	t.Run("short", func(t *testing.T) {
   985  		Run(t, mod, func(t *testing.T, env *Env) {
   986  			writeGoVim(env, "p/p.go", p)
   987  			writeGoVim(env, "main.go", main)
   988  			env.AfterChange(
   989  				Diagnostics(env.AtRegexp("main.go", "5")),
   990  			)
   991  		})
   992  	})
   993  
   994  	// A full version that replicates the whole flow of the test.
   995  	t.Run("full", func(t *testing.T) {
   996  		Run(t, mod, func(t *testing.T, env *Env) {
   997  			writeGoVim(env, "p/p.go", p)
   998  			writeGoVim(env, "main.go", main)
   999  			writeGoVim(env, "p/p_test.go", `package p
  1000  
  1001  import "testing"
  1002  
  1003  func TestDoIt(t *testing.T) {
  1004  	DoIt(5)
  1005  }
  1006  `)
  1007  			writeGoVim(env, "p/x_test.go", `package p_test
  1008  
  1009  import (
  1010  	"testing"
  1011  
  1012  	"mod.com/p"
  1013  )
  1014  
  1015  func TestDoIt(t *testing.T) {
  1016  	p.DoIt(5)
  1017  }
  1018  `)
  1019  			env.AfterChange(
  1020  				Diagnostics(env.AtRegexp("main.go", "5")),
  1021  				Diagnostics(env.AtRegexp("p/p_test.go", "5")),
  1022  				Diagnostics(env.AtRegexp("p/x_test.go", "5")),
  1023  			)
  1024  			env.RegexpReplace("p/p.go", "s string", "i int")
  1025  			env.AfterChange(
  1026  				NoDiagnostics(ForFile("main.go")),
  1027  				NoDiagnostics(ForFile("p/p_test.go")),
  1028  				NoDiagnostics(ForFile("p/x_test.go")),
  1029  			)
  1030  		})
  1031  	})
  1032  }
  1033  
  1034  func TestSingleFile(t *testing.T) {
  1035  	const mod = `
  1036  -- go.mod --
  1037  module mod.com
  1038  
  1039  go 1.13
  1040  -- a/a.go --
  1041  package a
  1042  
  1043  func _() {
  1044  	var x int
  1045  }
  1046  `
  1047  	WithOptions(
  1048  		// Empty workspace folders.
  1049  		WorkspaceFolders(),
  1050  	).Run(t, mod, func(t *testing.T, env *Env) {
  1051  		env.OpenFile("a/a.go")
  1052  		env.AfterChange(
  1053  			Diagnostics(env.AtRegexp("a/a.go", "x")),
  1054  		)
  1055  	})
  1056  }
  1057  
  1058  // Reproduces the case described in
  1059  // https://github.com/golang/go/issues/39296#issuecomment-652058883.
  1060  func TestPkgm(t *testing.T) {
  1061  	const basic = `
  1062  -- go.mod --
  1063  module mod.com
  1064  
  1065  go 1.15
  1066  -- foo/foo.go --
  1067  package foo
  1068  
  1069  import "fmt"
  1070  
  1071  func Foo() {
  1072  	fmt.Println("")
  1073  }
  1074  `
  1075  	Run(t, basic, func(t *testing.T, env *Env) {
  1076  		env.WriteWorkspaceFile("foo/foo_test.go", `package main
  1077  
  1078  func main() {
  1079  
  1080  }`)
  1081  		env.OpenFile("foo/foo_test.go")
  1082  		env.RegexpReplace("foo/foo_test.go", `package main`, `package foo`)
  1083  		env.AfterChange(NoDiagnostics(ForFile("foo/foo.go")))
  1084  	})
  1085  }
  1086  
  1087  func TestClosingBuffer(t *testing.T) {
  1088  	const basic = `
  1089  -- go.mod --
  1090  module mod.com
  1091  
  1092  go 1.14
  1093  -- main.go --
  1094  package main
  1095  
  1096  func main() {}
  1097  `
  1098  	Run(t, basic, func(t *testing.T, env *Env) {
  1099  		env.Editor.CreateBuffer(env.Ctx, "foo.go", `package main`)
  1100  		env.AfterChange()
  1101  		env.CloseBuffer("foo.go")
  1102  		env.AfterChange(NoLogMatching(protocol.Info, "packages=0"))
  1103  	})
  1104  }
  1105  
  1106  // Reproduces golang/go#38424.
  1107  func TestCutAndPaste(t *testing.T) {
  1108  	const basic = `
  1109  -- go.mod --
  1110  module mod.com
  1111  
  1112  go 1.14
  1113  -- main2.go --
  1114  package main
  1115  `
  1116  	Run(t, basic, func(t *testing.T, env *Env) {
  1117  		env.CreateBuffer("main.go", "")
  1118  		env.Await(env.DoneWithOpen())
  1119  
  1120  		env.SaveBufferWithoutActions("main.go")
  1121  		env.Await(env.DoneWithSave(), env.DoneWithChangeWatchedFiles())
  1122  
  1123  		env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main
  1124  
  1125  func main() {
  1126  }
  1127  `))
  1128  		env.Await(env.DoneWithChange())
  1129  
  1130  		env.SaveBuffer("main.go")
  1131  		env.Await(env.DoneWithSave(), env.DoneWithChangeWatchedFiles())
  1132  
  1133  		env.EditBuffer("main.go", fake.NewEdit(0, 0, 4, 0, ""))
  1134  		env.Await(env.DoneWithChange())
  1135  
  1136  		env.EditBuffer("main.go", fake.NewEdit(0, 0, 0, 0, `package main
  1137  
  1138  func main() {
  1139  	var x int
  1140  }
  1141  `))
  1142  		env.AfterChange(
  1143  			Diagnostics(env.AtRegexp("main.go", "x")),
  1144  		)
  1145  	})
  1146  }
  1147  
  1148  // Reproduces golang/go#39763.
  1149  func TestInvalidPackageName(t *testing.T) {
  1150  	const pkgDefault = `
  1151  -- go.mod --
  1152  module mod.com
  1153  
  1154  go 1.12
  1155  -- main.go --
  1156  package default
  1157  
  1158  func main() {}
  1159  `
  1160  	Run(t, pkgDefault, func(t *testing.T, env *Env) {
  1161  		env.OpenFile("main.go")
  1162  		env.AfterChange(
  1163  			Diagnostics(
  1164  				env.AtRegexp("main.go", "default"),
  1165  				WithMessage("expected 'IDENT'"),
  1166  			),
  1167  		)
  1168  	})
  1169  }
  1170  
  1171  // This test verifies that the workspace scope is effectively limited to the
  1172  // workspace folder, if expandWorkspaceToModule is set.
  1173  func TestExpandWorkspaceToModule(t *testing.T) {
  1174  	const mod = `
  1175  -- go.mod --
  1176  module mod.com
  1177  
  1178  go 1.12
  1179  -- a/main.go --
  1180  package main
  1181  
  1182  func main() {}
  1183  -- main.go --
  1184  package main
  1185  
  1186  func main() {
  1187  	var x int
  1188  }
  1189  `
  1190  	WithOptions(
  1191  		WorkspaceFolders("a"),
  1192  	).Run(t, mod, func(t *testing.T, env *Env) {
  1193  		env.OpenFile("a/main.go")
  1194  		env.AfterChange(
  1195  			Diagnostics(env.AtRegexp("main.go", "x")),
  1196  		)
  1197  	})
  1198  	WithOptions(
  1199  		WorkspaceFolders("a"),
  1200  		Settings{"expandWorkspaceToModule": false},
  1201  	).Run(t, mod, func(t *testing.T, env *Env) {
  1202  		env.OpenFile("a/main.go")
  1203  		env.AfterChange(
  1204  			NoDiagnostics(ForFile("main.go")),
  1205  		)
  1206  	})
  1207  }
  1208  
  1209  // This test verifies that the workspace scope is effectively limited to the
  1210  // set of active modules.
  1211  //
  1212  // We should not get diagnostics or file watching patterns for paths outside of
  1213  // the active workspace.
  1214  func TestWorkspaceModules(t *testing.T) {
  1215  	const mod = `
  1216  -- go.work --
  1217  go 1.18
  1218  
  1219  use a
  1220  -- a/go.mod --
  1221  module mod.com/a
  1222  
  1223  go 1.12
  1224  -- a/a.go --
  1225  package a
  1226  
  1227  func _() {
  1228  	var x int
  1229  }
  1230  -- b/go.mod --
  1231  module mod.com/b
  1232  
  1233  go 1.18
  1234  `
  1235  	WithOptions(
  1236  		Settings{
  1237  			"subdirWatchPatterns": "on",
  1238  		},
  1239  	).Run(t, mod, func(t *testing.T, env *Env) {
  1240  		env.OpenFile("a/a.go")
  1241  		// Writing this file may cause the snapshot to 'know' about the file b, but
  1242  		// that shouldn't cause it to watch the 'b' directory.
  1243  		env.WriteWorkspaceFile("b/b.go", `package b
  1244  
  1245  func _() {
  1246  	var x int
  1247  }
  1248  `)
  1249  		env.AfterChange(
  1250  			Diagnostics(env.AtRegexp("a/a.go", "x")),
  1251  			NoDiagnostics(ForFile("b/b.go")),
  1252  			FileWatchMatching("a$"),
  1253  			NoFileWatchMatching("b$"),
  1254  		)
  1255  	})
  1256  }
  1257  
  1258  func TestSimplifyCompositeLitDiagnostic(t *testing.T) {
  1259  	const files = `
  1260  -- go.mod --
  1261  module mod.com
  1262  
  1263  go 1.12
  1264  -- main.go --
  1265  package main
  1266  
  1267  import "fmt"
  1268  
  1269  type t struct {
  1270  	msg string
  1271  }
  1272  
  1273  func main() {
  1274  	x := []t{t{"msg"}}
  1275  	fmt.Println(x)
  1276  }
  1277  `
  1278  
  1279  	WithOptions(
  1280  		Settings{"staticcheck": true},
  1281  	).Run(t, files, func(t *testing.T, env *Env) {
  1282  		env.OpenFile("main.go")
  1283  		var d protocol.PublishDiagnosticsParams
  1284  		env.AfterChange(
  1285  			Diagnostics(env.AtRegexp("main.go", `t{"msg"}`), WithMessage("redundant type")),
  1286  			ReadDiagnostics("main.go", &d),
  1287  		)
  1288  		if tags := d.Diagnostics[0].Tags; len(tags) == 0 || tags[0] != protocol.Unnecessary {
  1289  			t.Errorf("wanted Unnecessary tag on diagnostic, got %v", tags)
  1290  		}
  1291  		env.ApplyQuickFixes("main.go", d.Diagnostics)
  1292  		env.AfterChange(NoDiagnostics(ForFile("main.go")))
  1293  	})
  1294  }
  1295  
  1296  // Test some secondary diagnostics
  1297  func TestSecondaryDiagnostics(t *testing.T) {
  1298  	const dir = `
  1299  -- go.mod --
  1300  module mod.com
  1301  
  1302  go 1.12
  1303  -- main.go --
  1304  package main
  1305  func main() {
  1306  	panic("not here")
  1307  }
  1308  -- other.go --
  1309  package main
  1310  func main() {}
  1311  `
  1312  	Run(t, dir, func(t *testing.T, env *Env) {
  1313  		env.OpenFile("main.go")
  1314  		env.OpenFile("other.go")
  1315  		var mainDiags, otherDiags protocol.PublishDiagnosticsParams
  1316  		env.AfterChange(
  1317  			ReadDiagnostics("main.go", &mainDiags),
  1318  			ReadDiagnostics("other.go", &otherDiags),
  1319  		)
  1320  		if len(mainDiags.Diagnostics) != 1 {
  1321  			t.Fatalf("main.go, got %d diagnostics, expected 1", len(mainDiags.Diagnostics))
  1322  		}
  1323  		keep := mainDiags.Diagnostics[0]
  1324  		if len(otherDiags.Diagnostics) != 1 {
  1325  			t.Fatalf("other.go: got %d diagnostics, expected 1", len(otherDiags.Diagnostics))
  1326  		}
  1327  		if len(otherDiags.Diagnostics[0].RelatedInformation) != 1 {
  1328  			t.Fatalf("got %d RelatedInformations, expected 1", len(otherDiags.Diagnostics[0].RelatedInformation))
  1329  		}
  1330  		// check that the RelatedInformation matches the error from main.go
  1331  		c := otherDiags.Diagnostics[0].RelatedInformation[0]
  1332  		if c.Location.Range != keep.Range {
  1333  			t.Errorf("locations don't match. Got %v expected %v", c.Location.Range, keep.Range)
  1334  		}
  1335  	})
  1336  }
  1337  
  1338  func TestOrphanedFiles(t *testing.T) {
  1339  	const files = `
  1340  -- go.mod --
  1341  module mod.com
  1342  
  1343  go 1.12
  1344  -- a/a.go --
  1345  package a
  1346  
  1347  func main() {
  1348  	var x int
  1349  }
  1350  -- a/a_exclude.go --
  1351  // +build exclude
  1352  
  1353  package a
  1354  
  1355  func _() {
  1356  	var x int
  1357  }
  1358  `
  1359  	Run(t, files, func(t *testing.T, env *Env) {
  1360  		env.OpenFile("a/a.go")
  1361  		env.AfterChange(
  1362  			Diagnostics(env.AtRegexp("a/a.go", "x")),
  1363  		)
  1364  		env.OpenFile("a/a_exclude.go")
  1365  
  1366  		loadOnce := LogMatching(protocol.Info, "query=.*file=.*a_exclude.go", 1, false)
  1367  
  1368  		// can't use OnceMet or AfterChange as logs are async
  1369  		env.Await(loadOnce)
  1370  		// ...but ensure that the change has been fully processed before editing.
  1371  		// Otherwise, there may be a race where the snapshot is cloned before all
  1372  		// state changes resulting from the load have been processed
  1373  		// (golang/go#61521).
  1374  		env.AfterChange()
  1375  
  1376  		// Check that orphaned files are not reloaded, by making a change in
  1377  		// a.go file and confirming that the workspace diagnosis did not reload
  1378  		// a_exclude.go.
  1379  		//
  1380  		// This is racy (but fails open) because logs are asynchronous to other LSP
  1381  		// operations. There's a chance gopls _did_ log, and we just haven't seen
  1382  		// it yet.
  1383  		env.RegexpReplace("a/a.go", "package a", "package a // arbitrary comment")
  1384  		env.AfterChange(loadOnce)
  1385  	})
  1386  }
  1387  
  1388  func TestEnableAllExperiments(t *testing.T) {
  1389  	// Before the oldest supported Go version, gopls sends a warning to upgrade
  1390  	// Go, which fails the expectation below.
  1391  	testenv.NeedsGo1Point(t, goversion.OldestSupported())
  1392  
  1393  	const mod = `
  1394  -- go.mod --
  1395  module mod.com
  1396  
  1397  go 1.12
  1398  -- main.go --
  1399  package main
  1400  
  1401  import "bytes"
  1402  
  1403  func b(c bytes.Buffer) {
  1404  	_ = 1
  1405  }
  1406  `
  1407  	WithOptions(
  1408  		Settings{"allExperiments": true},
  1409  	).Run(t, mod, func(t *testing.T, env *Env) {
  1410  		// Confirm that the setting doesn't cause any warnings.
  1411  		env.OnceMet(
  1412  			InitialWorkspaceLoad,
  1413  			NoShownMessage(""), // empty substring to match any message
  1414  		)
  1415  	})
  1416  }
  1417  
  1418  func TestSwig(t *testing.T) {
  1419  	if _, err := exec.LookPath("swig"); err != nil {
  1420  		t.Skip("skipping test: swig not available")
  1421  	}
  1422  	if _, err := exec.LookPath("g++"); err != nil {
  1423  		t.Skip("skipping test: g++ not available")
  1424  	}
  1425  
  1426  	const mod = `
  1427  -- go.mod --
  1428  module mod.com
  1429  
  1430  go 1.12
  1431  -- pkg/simple/export_swig.go --
  1432  package simple
  1433  
  1434  func ExportSimple(x, y int) int {
  1435  	return Gcd(x, y)
  1436  }
  1437  -- pkg/simple/simple.swigcxx --
  1438  %module simple
  1439  
  1440  %inline %{
  1441  extern int gcd(int x, int y)
  1442  {
  1443    int g;
  1444    g = y;
  1445    while (x > 0) {
  1446      g = x;
  1447      x = y % x;
  1448      y = g;
  1449    }
  1450    return g;
  1451  }
  1452  %}
  1453  -- main.go --
  1454  package a
  1455  
  1456  func main() {
  1457  	var x int
  1458  }
  1459  `
  1460  	Run(t, mod, func(t *testing.T, env *Env) {
  1461  		env.OnceMet(
  1462  			InitialWorkspaceLoad,
  1463  			NoDiagnostics(WithMessage("illegal character U+0023 '#'")),
  1464  		)
  1465  	})
  1466  }
  1467  
  1468  // When foo_test.go is opened, gopls will object to the borked package name.
  1469  // This test asserts that when the package name is fixed, gopls will soon after
  1470  // have no more complaints about it.
  1471  // https://github.com/golang/go/issues/41061
  1472  func TestRenamePackage(t *testing.T) {
  1473  	const proxy = `
  1474  -- example.com@v1.2.3/go.mod --
  1475  module example.com
  1476  
  1477  go 1.12
  1478  -- example.com@v1.2.3/blah/blah.go --
  1479  package blah
  1480  
  1481  const Name = "Blah"
  1482  -- random.org@v1.2.3/go.mod --
  1483  module random.org
  1484  
  1485  go 1.12
  1486  -- random.org@v1.2.3/blah/blah.go --
  1487  package hello
  1488  
  1489  const Name = "Hello"
  1490  `
  1491  
  1492  	const contents = `
  1493  -- go.mod --
  1494  module mod.com
  1495  
  1496  go 1.12
  1497  -- main.go --
  1498  package main
  1499  
  1500  import "example.com/blah"
  1501  
  1502  func main() {
  1503  	blah.Hello()
  1504  }
  1505  -- bob.go --
  1506  package main
  1507  -- foo/foo.go --
  1508  package foo
  1509  -- foo/foo_test.go --
  1510  package foo_
  1511  `
  1512  
  1513  	WithOptions(
  1514  		ProxyFiles(proxy),
  1515  		InGOPATH(),
  1516  		EnvVars{"GO111MODULE": "off"},
  1517  	).Run(t, contents, func(t *testing.T, env *Env) {
  1518  		// Simulate typing character by character.
  1519  		env.OpenFile("foo/foo_test.go")
  1520  		env.Await(env.DoneWithOpen())
  1521  		env.RegexpReplace("foo/foo_test.go", "_", "_t")
  1522  		env.Await(env.DoneWithChange())
  1523  		env.RegexpReplace("foo/foo_test.go", "_t", "_test")
  1524  		env.AfterChange(
  1525  			NoDiagnostics(ForFile("foo/foo_test.go")),
  1526  			NoOutstandingWork(IgnoreTelemetryPromptWork),
  1527  		)
  1528  	})
  1529  }
  1530  
  1531  // TestProgressBarErrors confirms that critical workspace load errors are shown
  1532  // and updated via progress reports.
  1533  func TestProgressBarErrors(t *testing.T) {
  1534  	const pkg = `
  1535  -- go.mod --
  1536  modul mod.com
  1537  
  1538  go 1.12
  1539  -- main.go --
  1540  package main
  1541  `
  1542  	Run(t, pkg, func(t *testing.T, env *Env) {
  1543  		env.OpenFile("go.mod")
  1544  		env.AfterChange(
  1545  			OutstandingWork(server.WorkspaceLoadFailure, "unknown directive"),
  1546  		)
  1547  		env.EditBuffer("go.mod", fake.NewEdit(0, 0, 3, 0, `module mod.com
  1548  
  1549  go 1.hello
  1550  `))
  1551  		// As of golang/go#42529, go.mod changes do not reload the workspace until
  1552  		// they are saved.
  1553  		env.SaveBufferWithoutActions("go.mod")
  1554  		env.AfterChange(
  1555  			OutstandingWork(server.WorkspaceLoadFailure, "invalid go version"),
  1556  		)
  1557  		env.RegexpReplace("go.mod", "go 1.hello", "go 1.12")
  1558  		env.SaveBufferWithoutActions("go.mod")
  1559  		env.AfterChange(
  1560  			NoOutstandingWork(IgnoreTelemetryPromptWork),
  1561  		)
  1562  	})
  1563  }
  1564  
  1565  func TestDeleteDirectory(t *testing.T) {
  1566  	const mod = `
  1567  -- bob/bob.go --
  1568  package bob
  1569  
  1570  func Hello() {
  1571  	var x int
  1572  }
  1573  -- go.mod --
  1574  module mod.com
  1575  -- cmd/main.go --
  1576  package main
  1577  
  1578  import "mod.com/bob"
  1579  
  1580  func main() {
  1581  	bob.Hello()
  1582  }
  1583  `
  1584  	WithOptions(
  1585  		Settings{
  1586  			// Now that we don't watch subdirs by default (except for VS Code),
  1587  			// we must explicitly ask gopls to requests subdir watch patterns.
  1588  			"subdirWatchPatterns": "on",
  1589  		},
  1590  	).Run(t, mod, func(t *testing.T, env *Env) {
  1591  		env.OnceMet(
  1592  			InitialWorkspaceLoad,
  1593  			FileWatchMatching("bob"),
  1594  		)
  1595  		env.RemoveWorkspaceFile("bob")
  1596  		env.AfterChange(
  1597  			Diagnostics(env.AtRegexp("cmd/main.go", `"mod.com/bob"`)),
  1598  			NoDiagnostics(ForFile("bob/bob.go")),
  1599  			NoFileWatchMatching("bob"),
  1600  		)
  1601  	})
  1602  }
  1603  
  1604  // Confirms that circular imports are tested and reported.
  1605  func TestCircularImports(t *testing.T) {
  1606  	const mod = `
  1607  -- go.mod --
  1608  module mod.com
  1609  
  1610  go 1.12
  1611  -- self/self.go --
  1612  package self
  1613  
  1614  import _ "mod.com/self"
  1615  func Hello() {}
  1616  -- double/a/a.go --
  1617  package a
  1618  
  1619  import _ "mod.com/double/b"
  1620  -- double/b/b.go --
  1621  package b
  1622  
  1623  import _ "mod.com/double/a"
  1624  -- triple/a/a.go --
  1625  package a
  1626  
  1627  import _ "mod.com/triple/b"
  1628  -- triple/b/b.go --
  1629  package b
  1630  
  1631  import _ "mod.com/triple/c"
  1632  -- triple/c/c.go --
  1633  package c
  1634  
  1635  import _ "mod.com/triple/a"
  1636  `
  1637  	Run(t, mod, func(t *testing.T, env *Env) {
  1638  		env.OnceMet(
  1639  			InitialWorkspaceLoad,
  1640  			Diagnostics(env.AtRegexp("self/self.go", `_ "mod.com/self"`), WithMessage("import cycle not allowed")),
  1641  			Diagnostics(env.AtRegexp("double/a/a.go", `_ "mod.com/double/b"`), WithMessage("import cycle not allowed")),
  1642  			Diagnostics(env.AtRegexp("triple/a/a.go", `_ "mod.com/triple/b"`), WithMessage("import cycle not allowed")),
  1643  		)
  1644  	})
  1645  }
  1646  
  1647  // Tests golang/go#46667: deleting a problematic import path should resolve
  1648  // import cycle errors.
  1649  func TestResolveImportCycle(t *testing.T) {
  1650  	const mod = `
  1651  -- go.mod --
  1652  module mod.test
  1653  
  1654  go 1.16
  1655  -- a/a.go --
  1656  package a
  1657  
  1658  import "mod.test/b"
  1659  
  1660  const A = b.A
  1661  const B = 2
  1662  -- b/b.go --
  1663  package b
  1664  
  1665  import "mod.test/a"
  1666  
  1667  const A = 1
  1668  const B = a.B
  1669  	`
  1670  	Run(t, mod, func(t *testing.T, env *Env) {
  1671  		env.OpenFile("a/a.go")
  1672  		env.OpenFile("b/b.go")
  1673  		env.AfterChange(
  1674  			// The Go command sometimes tells us about only one of the import cycle
  1675  			// errors below. Also, sometimes we get an error during type checking
  1676  			// instead of during list, due to missing metadata. This is likely due to
  1677  			// a race.
  1678  			// For robustness of this test, succeed if we get any reasonable error.
  1679  			//
  1680  			// TODO(golang/go#52904): we should get *both* of these errors.
  1681  			// TODO(golang/go#64899): we should always get an import cycle error
  1682  			// rather than a missing metadata error.
  1683  			AnyOf(
  1684  				Diagnostics(env.AtRegexp("a/a.go", `"mod.test/b"`)),
  1685  				Diagnostics(env.AtRegexp("b/b.go", `"mod.test/a"`)),
  1686  			),
  1687  		)
  1688  		env.RegexpReplace("b/b.go", `const B = a\.B`, "")
  1689  		env.SaveBuffer("b/b.go")
  1690  		env.AfterChange(
  1691  			NoDiagnostics(ForFile("a/a.go")),
  1692  			NoDiagnostics(ForFile("b/b.go")),
  1693  		)
  1694  	})
  1695  }
  1696  
  1697  func TestBadImport(t *testing.T) {
  1698  	const mod = `
  1699  -- go.mod --
  1700  module mod.com
  1701  
  1702  go 1.12
  1703  -- main.go --
  1704  package main
  1705  
  1706  import (
  1707  	_ "nosuchpkg"
  1708  )
  1709  `
  1710  	t.Run("module", func(t *testing.T) {
  1711  		Run(t, mod, func(t *testing.T, env *Env) {
  1712  			env.OnceMet(
  1713  				InitialWorkspaceLoad,
  1714  				Diagnostics(env.AtRegexp("main.go", `"nosuchpkg"`), WithMessage(`could not import nosuchpkg (no required module provides package "nosuchpkg"`)),
  1715  			)
  1716  		})
  1717  	})
  1718  	t.Run("GOPATH", func(t *testing.T) {
  1719  		WithOptions(
  1720  			InGOPATH(),
  1721  			EnvVars{"GO111MODULE": "off"},
  1722  			Modes(Default),
  1723  		).Run(t, mod, func(t *testing.T, env *Env) {
  1724  			env.OnceMet(
  1725  				InitialWorkspaceLoad,
  1726  				Diagnostics(env.AtRegexp("main.go", `"nosuchpkg"`), WithMessage(`cannot find package "nosuchpkg"`)),
  1727  			)
  1728  		})
  1729  	})
  1730  }
  1731  
  1732  func TestNestedModules(t *testing.T) {
  1733  	const proxy = `
  1734  -- nested.com@v1.0.0/go.mod --
  1735  module nested.com
  1736  
  1737  go 1.12
  1738  -- nested.com@v1.0.0/hello/hello.go --
  1739  package hello
  1740  
  1741  func Hello() {}
  1742  `
  1743  
  1744  	const nested = `
  1745  -- go.mod --
  1746  module mod.com
  1747  
  1748  go 1.12
  1749  
  1750  require nested.com v1.0.0
  1751  -- go.sum --
  1752  nested.com v1.0.0 h1:I6spLE4CgFqMdBPc+wTV2asDO2QJ3tU0YAT+jkLeN1I=
  1753  nested.com v1.0.0/go.mod h1:ly53UzXQgVjSlV7wicdBB4p8BxfytuGT1Xcyv0ReJfI=
  1754  -- main.go --
  1755  package main
  1756  
  1757  import "nested.com/hello"
  1758  
  1759  func main() {
  1760  	hello.Hello()
  1761  }
  1762  -- nested/go.mod --
  1763  module nested.com
  1764  
  1765  -- nested/hello/hello.go --
  1766  package hello
  1767  
  1768  func Hello() {
  1769  	helloHelper()
  1770  }
  1771  -- nested/hello/hello_helper.go --
  1772  package hello
  1773  
  1774  func helloHelper() {}
  1775  `
  1776  	WithOptions(
  1777  		ProxyFiles(proxy),
  1778  		Modes(Default),
  1779  	).Run(t, nested, func(t *testing.T, env *Env) {
  1780  		// Expect a diagnostic in a nested module.
  1781  		env.OpenFile("nested/hello/hello.go")
  1782  		env.AfterChange(
  1783  			NoDiagnostics(ForFile("nested/hello/hello.go")),
  1784  		)
  1785  		loc := env.GoToDefinition(env.RegexpSearch("nested/hello/hello.go", "helloHelper"))
  1786  		want := "nested/hello/hello_helper.go"
  1787  		if got := env.Sandbox.Workdir.URIToPath(loc.URI); got != want {
  1788  			t.Errorf("Definition() returned %q, want %q", got, want)
  1789  		}
  1790  	})
  1791  }
  1792  
  1793  func TestAdHocPackagesReloading(t *testing.T) {
  1794  	const nomod = `
  1795  -- main.go --
  1796  package main
  1797  
  1798  func main() {}
  1799  `
  1800  	Run(t, nomod, func(t *testing.T, env *Env) {
  1801  		env.OpenFile("main.go")
  1802  		env.RegexpReplace("main.go", "{}", "{ var x int; }") // simulate typing
  1803  		env.AfterChange(NoLogMatching(protocol.Info, "packages=1"))
  1804  	})
  1805  }
  1806  
  1807  func TestBuildTagChange(t *testing.T) {
  1808  	const files = `
  1809  -- go.mod --
  1810  module mod.com
  1811  
  1812  go 1.12
  1813  -- foo.go --
  1814  // decoy comment
  1815  // +build hidden
  1816  // decoy comment
  1817  
  1818  package foo
  1819  var Foo = 1
  1820  -- bar.go --
  1821  package foo
  1822  var Bar = Foo
  1823  `
  1824  
  1825  	Run(t, files, func(t *testing.T, env *Env) {
  1826  		env.OpenFile("foo.go")
  1827  		env.AfterChange(Diagnostics(env.AtRegexp("bar.go", `Foo`)))
  1828  		env.RegexpReplace("foo.go", `\+build`, "")
  1829  		env.AfterChange(NoDiagnostics(ForFile("bar.go")))
  1830  	})
  1831  
  1832  }
  1833  
  1834  func TestIssue44736(t *testing.T) {
  1835  	const files = `
  1836  	-- go.mod --
  1837  module blah.com
  1838  
  1839  go 1.16
  1840  -- main.go --
  1841  package main
  1842  
  1843  import "fmt"
  1844  
  1845  func main() {
  1846  	asdf
  1847  	fmt.Printf("This is a test %v")
  1848  	fdas
  1849  }
  1850  -- other.go --
  1851  package main
  1852  
  1853  `
  1854  	Run(t, files, func(t *testing.T, env *Env) {
  1855  		env.OpenFile("main.go")
  1856  		env.OpenFile("other.go")
  1857  		env.AfterChange(
  1858  			Diagnostics(env.AtRegexp("main.go", "asdf")),
  1859  			Diagnostics(env.AtRegexp("main.go", "fdas")),
  1860  		)
  1861  		env.SetBufferContent("other.go", "package main\n\nasdf")
  1862  		// The new diagnostic in other.go should not suppress diagnostics in main.go.
  1863  		env.AfterChange(
  1864  			Diagnostics(env.AtRegexp("other.go", "asdf"), WithMessage("expected declaration")),
  1865  			Diagnostics(env.AtRegexp("main.go", "asdf")),
  1866  		)
  1867  	})
  1868  }
  1869  
  1870  func TestInitialization(t *testing.T) {
  1871  	const files = `
  1872  -- go.mod --
  1873  module mod.com
  1874  
  1875  go 1.16
  1876  -- main.go --
  1877  package main
  1878  `
  1879  	Run(t, files, func(t *testing.T, env *Env) {
  1880  		env.OpenFile("go.mod")
  1881  		env.Await(env.DoneWithOpen())
  1882  		env.RegexpReplace("go.mod", "module", "modul")
  1883  		env.SaveBufferWithoutActions("go.mod")
  1884  		env.AfterChange(
  1885  			NoLogMatching(protocol.Error, "initial workspace load failed"),
  1886  		)
  1887  	})
  1888  }
  1889  
  1890  // This test confirms that the view does not reinitialize when a go.mod file is
  1891  // opened.
  1892  func TestNoReinitialize(t *testing.T) {
  1893  	const files = `
  1894  -- go.mod --
  1895  module mod.com
  1896  
  1897  go 1.12
  1898  -- main.go --
  1899  package main
  1900  
  1901  func main() {}
  1902  `
  1903  	Run(t, files, func(t *testing.T, env *Env) {
  1904  		env.OpenFile("go.mod")
  1905  		env.Await(
  1906  			// Check that we have only loaded "<dir>/..." once.
  1907  			// Log messages are asynchronous to other events on the LSP stream, so we
  1908  			// can't use OnceMet or AfterChange here.
  1909  			LogMatching(protocol.Info, `.*query=.*\.\.\..*`, 1, false),
  1910  		)
  1911  	})
  1912  }
  1913  
  1914  func TestLangVersion(t *testing.T) {
  1915  	const files = `
  1916  -- go.mod --
  1917  module mod.com
  1918  
  1919  go 1.12
  1920  -- main.go --
  1921  package main
  1922  
  1923  const C = 0b10
  1924  `
  1925  	Run(t, files, func(t *testing.T, env *Env) {
  1926  		env.OnceMet(
  1927  			InitialWorkspaceLoad,
  1928  			Diagnostics(env.AtRegexp("main.go", `0b10`), WithMessage("go1.13 or later")),
  1929  		)
  1930  		env.WriteWorkspaceFile("go.mod", "module mod.com \n\ngo 1.13\n")
  1931  		env.AfterChange(
  1932  			NoDiagnostics(ForFile("main.go")),
  1933  		)
  1934  	})
  1935  }
  1936  
  1937  func TestNoQuickFixForUndeclaredConstraint(t *testing.T) {
  1938  	const files = `
  1939  -- go.mod --
  1940  module mod.com
  1941  
  1942  go 1.18
  1943  -- main.go --
  1944  package main
  1945  
  1946  func F[T C](_ T) {
  1947  }
  1948  `
  1949  
  1950  	Run(t, files, func(t *testing.T, env *Env) {
  1951  		var d protocol.PublishDiagnosticsParams
  1952  		env.OnceMet(
  1953  			InitialWorkspaceLoad,
  1954  			Diagnostics(env.AtRegexp("main.go", `C`)),
  1955  			ReadDiagnostics("main.go", &d),
  1956  		)
  1957  		if fixes := env.GetQuickFixes("main.go", d.Diagnostics); len(fixes) != 0 {
  1958  			t.Errorf("got quick fixes %v, wanted none", fixes)
  1959  		}
  1960  	})
  1961  }
  1962  
  1963  func TestEditGoDirective(t *testing.T) {
  1964  	const files = `
  1965  -- go.mod --
  1966  module mod.com
  1967  
  1968  go 1.16
  1969  -- main.go --
  1970  package main
  1971  
  1972  func F[T any](_ T) {
  1973  }
  1974  `
  1975  	Run(t, files, func(_ *testing.T, env *Env) { // Create a new workspace-level directory and empty file.
  1976  		var d protocol.PublishDiagnosticsParams
  1977  		env.OnceMet(
  1978  			InitialWorkspaceLoad,
  1979  			Diagnostics(env.AtRegexp("main.go", `T any`), WithMessage("type parameter")),
  1980  			ReadDiagnostics("main.go", &d),
  1981  		)
  1982  
  1983  		env.ApplyQuickFixes("main.go", d.Diagnostics)
  1984  		env.AfterChange(
  1985  			NoDiagnostics(ForFile("main.go")),
  1986  		)
  1987  	})
  1988  }
  1989  
  1990  func TestEditGoDirectiveWorkspace(t *testing.T) {
  1991  	const files = `
  1992  -- go.mod --
  1993  module mod.com
  1994  
  1995  go 1.16
  1996  -- go.work --
  1997  go 1.18
  1998  
  1999  use .
  2000  -- main.go --
  2001  package main
  2002  
  2003  func F[T any](_ T) {
  2004  }
  2005  `
  2006  	Run(t, files, func(_ *testing.T, env *Env) { // Create a new workspace-level directory and empty file.
  2007  		var d protocol.PublishDiagnosticsParams
  2008  
  2009  		// We should have a diagnostic because generics are not supported at 1.16.
  2010  		env.OnceMet(
  2011  			InitialWorkspaceLoad,
  2012  			Diagnostics(env.AtRegexp("main.go", `T any`), WithMessage("type parameter")),
  2013  			ReadDiagnostics("main.go", &d),
  2014  		)
  2015  
  2016  		// This diagnostic should have a quick fix to edit the go version.
  2017  		env.ApplyQuickFixes("main.go", d.Diagnostics)
  2018  
  2019  		// Once the edit is applied, the problematic diagnostics should be
  2020  		// resolved.
  2021  		env.AfterChange(
  2022  			NoDiagnostics(ForFile("main.go")),
  2023  		)
  2024  	})
  2025  }
  2026  
  2027  // This test demonstrates that analysis facts are correctly propagated
  2028  // across packages.
  2029  func TestInterpackageAnalysis(t *testing.T) {
  2030  	const src = `
  2031  -- go.mod --
  2032  module example.com
  2033  -- a/a.go --
  2034  package a
  2035  
  2036  import "example.com/b"
  2037  
  2038  func _() {
  2039  	new(b.B).Printf("%d", "s") // printf error
  2040  }
  2041  
  2042  -- b/b.go --
  2043  package b
  2044  
  2045  import "example.com/c"
  2046  
  2047  type B struct{}
  2048  
  2049  func (B) Printf(format string, args ...interface{}) {
  2050  	c.MyPrintf(format, args...)
  2051  }
  2052  
  2053  -- c/c.go --
  2054  package c
  2055  
  2056  import "fmt"
  2057  
  2058  func MyPrintf(format string, args ...interface{}) {
  2059  	fmt.Printf(format, args...)
  2060  }
  2061  `
  2062  	Run(t, src, func(t *testing.T, env *Env) {
  2063  		env.OpenFile("a/a.go")
  2064  		env.AfterChange(
  2065  			Diagnostics(
  2066  				env.AtRegexp("a/a.go", "new.*Printf"),
  2067  				WithMessage("format %d has arg \"s\" of wrong type string"),
  2068  			),
  2069  		)
  2070  	})
  2071  }
  2072  
  2073  // This test ensures that only Analyzers with RunDespiteErrors=true
  2074  // are invoked on a package that would not compile, even if the errors
  2075  // are distant and localized.
  2076  func TestErrorsThatPreventAnalysis(t *testing.T) {
  2077  	const src = `
  2078  -- go.mod --
  2079  module example.com
  2080  -- a/a.go --
  2081  package a
  2082  
  2083  import "fmt"
  2084  import "sync"
  2085  import _ "example.com/b"
  2086  
  2087  func _() {
  2088  	// The copylocks analyzer (RunDespiteErrors, FactTypes={}) does run.
  2089  	var mu sync.Mutex
  2090  	mu2 := mu // copylocks error, reported
  2091  	_ = &mu2
  2092  
  2093  	// The printf analyzer (!RunDespiteErrors, FactTypes!={}) does not run:
  2094  	//  (c, printf) failed because of type error in c
  2095  	//  (b, printf) and (a, printf) do not run because of failed prerequisites.
  2096  	fmt.Printf("%d", "s") // printf error, unreported
  2097  
  2098  	// The bools analyzer (!RunDespiteErrors, FactTypes={}) does not run:
  2099  	var cond bool
  2100  	_ = cond != true && cond != true // bools error, unreported
  2101  }
  2102  
  2103  -- b/b.go --
  2104  package b
  2105  
  2106  import _ "example.com/c"
  2107  
  2108  -- c/c.go --
  2109  package c
  2110  
  2111  var _ = 1 / "" // type error
  2112  
  2113  `
  2114  	Run(t, src, func(t *testing.T, env *Env) {
  2115  		var diags protocol.PublishDiagnosticsParams
  2116  		env.OpenFile("a/a.go")
  2117  		env.AfterChange(
  2118  			Diagnostics(env.AtRegexp("a/a.go", "mu2 := (mu)"), WithMessage("assignment copies lock value")),
  2119  			ReadDiagnostics("a/a.go", &diags))
  2120  
  2121  		// Assert that there were no other diagnostics.
  2122  		// In particular:
  2123  		// - "fmt.Printf" does not trigger a [printf] finding;
  2124  		// - "cond != true" does not trigger a [bools] finding.
  2125  		//
  2126  		// We use this check in preference to NoDiagnosticAtRegexp
  2127  		// as it is robust in case of minor mistakes in the position
  2128  		// regexp, and because it reports unexpected diagnostics.
  2129  		if got, want := len(diags.Diagnostics), 1; got != want {
  2130  			t.Errorf("got %d diagnostics in a/a.go, want %d:", got, want)
  2131  			for i, diag := range diags.Diagnostics {
  2132  				t.Logf("Diagnostics[%d] = %+v", i, diag)
  2133  			}
  2134  		}
  2135  	})
  2136  }
  2137  
  2138  // This test demonstrates the deprecated symbol analyzer
  2139  // produces deprecation notices with expected severity and tags.
  2140  func TestDeprecatedAnalysis(t *testing.T) {
  2141  	const src = `
  2142  -- go.mod --
  2143  module example.com
  2144  -- a/a.go --
  2145  package a
  2146  
  2147  import "example.com/b"
  2148  
  2149  func _() {
  2150  	new(b.B).Obsolete() // deprecated
  2151  }
  2152  
  2153  -- b/b.go --
  2154  package b
  2155  
  2156  type B struct{}
  2157  
  2158  // Deprecated: use New instead.
  2159  func (B) Obsolete() {}
  2160  
  2161  func (B) New() {}
  2162  `
  2163  	Run(t, src, func(t *testing.T, env *Env) {
  2164  		env.OpenFile("a/a.go")
  2165  		env.AfterChange(
  2166  			Diagnostics(
  2167  				env.AtRegexp("a/a.go", "new.*Obsolete"),
  2168  				WithMessage("use New instead."),
  2169  				WithSeverityTags("deprecated", protocol.SeverityHint, []protocol.DiagnosticTag{protocol.Deprecated}),
  2170  			),
  2171  		)
  2172  	})
  2173  }
  2174  
  2175  func TestDiagnosticsOnlyOnSaveFile(t *testing.T) {
  2176  	// This functionality is broken because the new orphaned file diagnostics
  2177  	// logic wants to publish diagnostics for changed files, independent of any
  2178  	// snapshot diagnostics pass, and this causes stale diagnostics to be
  2179  	// invalidated.
  2180  	//
  2181  	// We can fix this behavior more correctly by also honoring the
  2182  	// diagnosticsTrigger in DiagnoseOrphanedFiles, but that would require
  2183  	// resolving configuration that is independent of the snapshot. In other
  2184  	// words, we need to figure out which cache.Folder.Options applies to the
  2185  	// changed file, even if it does not have a snapshot.
  2186  	t.Skip("temporary skip for golang/go#57979: revisit after zero-config logic is in place")
  2187  
  2188  	const onlyMod = `
  2189  -- go.mod --
  2190  module mod.com
  2191  
  2192  go 1.12
  2193  -- main.go --
  2194  package main
  2195  
  2196  func main() {
  2197  	Foo()
  2198  }
  2199  -- foo.go --
  2200  package main
  2201  
  2202  func Foo() {}
  2203  `
  2204  	WithOptions(
  2205  		Settings{
  2206  			"diagnosticsTrigger": "Save",
  2207  		},
  2208  	).Run(t, onlyMod, func(t *testing.T, env *Env) {
  2209  		env.OpenFile("foo.go")
  2210  		env.RegexpReplace("foo.go", "(Foo)", "Bar") // Makes reference to Foo undefined/undeclared.
  2211  		env.AfterChange(NoDiagnostics())            // No diagnostics update until file save.
  2212  
  2213  		env.SaveBuffer("foo.go")
  2214  		// Compiler's error message about undeclared names vary depending on the version,
  2215  		// but must be explicit about the problematic name.
  2216  		env.AfterChange(Diagnostics(env.AtRegexp("main.go", "Foo"), WithMessage("Foo")))
  2217  
  2218  		env.OpenFile("main.go")
  2219  		env.RegexpReplace("main.go", "(Foo)", "Bar")
  2220  		// No diagnostics update until file save. That results in outdated diagnostic.
  2221  		env.AfterChange(Diagnostics(env.AtRegexp("main.go", "Bar"), WithMessage("Foo")))
  2222  
  2223  		env.SaveBuffer("main.go")
  2224  		env.AfterChange(NoDiagnostics())
  2225  	})
  2226  }