internal/lsp: add a new regtest to reproduce golang/go#39646

Change-Id: I51d8c66a83ecae1c8fc1f39c0e90a03a732c263b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/240063
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/internal/lsp/fake/editor.go b/internal/lsp/fake/editor.go
index 96a94cc..fff6412 100644
--- a/internal/lsp/fake/editor.go
+++ b/internal/lsp/fake/editor.go
@@ -214,6 +214,12 @@
 	if err != nil {
 		return err
 	}
+	return e.OpenFileWithContent(ctx, path, content)
+}
+
+// OpenFileWithContent creates a buffer for the given workdir-relative file
+// with the given contents.
+func (e *Editor) OpenFileWithContent(ctx context.Context, path, content string) error {
 	buf := newBuffer(path, content)
 	e.mu.Lock()
 	e.buffers[path] = buf
diff --git a/internal/lsp/regtest/diagnostics_test.go b/internal/lsp/regtest/diagnostics_test.go
index 72a5313..a46286e 100644
--- a/internal/lsp/regtest/diagnostics_test.go
+++ b/internal/lsp/regtest/diagnostics_test.go
@@ -861,3 +861,75 @@
 		env.Await(env.DiagnosticAtRegexp("b/b.go", `Nonexistant`))
 	})
 }
+
+// This is a copy of the scenario_default/quickfix_empty_files.txt test from
+// govim. Reproduces golang/go#39646.
+func TestQuickFixEmptyFiles(t *testing.T) {
+	const mod = `
+-- go.mod --
+module mod.com
+
+go 1.12
+`
+	runner.Run(t, mod, func(t *testing.T, env *Env) {
+		// To fully recreate the govim tests, we create files by inserting
+		// a newline, adding to the file, and then deleting the newline.
+		// Wait for each event to process to avoid cancellations.
+		writeGoVim := func(name, content string) {
+			env.OpenFileWithContent(name, "\n")
+			env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1))
+
+			env.EditBuffer(name, fake.NewEdit(1, 0, 1, 0, content))
+			env.Await(env.AnyDiagnosticAtCurrentVersion(name))
+
+			env.EditBuffer(name, fake.NewEdit(0, 0, 1, 0, ""))
+			env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChange), 1))
+		}
+		writeGoVim("p/p.go", `package p
+
+
+func DoIt(s string) {
+	var x int
+}
+`)
+		writeGoVim("main.go", `package main
+
+import "mod.com/p"
+
+func main() {
+	p.DoIt(5)
+}
+`)
+		writeGoVim("p/p_test.go", `package p
+
+import "testing"
+
+func TestDoIt(t *testing.T) {
+	DoIt(5)
+}
+`)
+		writeGoVim("p/x_test.go", `package p_test
+
+import (
+	"testing"
+
+	"mod.com/p"
+)
+
+func TestDoIt(t *testing.T) {
+	p.DoIt(5)
+}
+`)
+		env.Await(
+			env.DiagnosticAtRegexp("main.go", "5"),
+			env.DiagnosticAtRegexp("p/p_test.go", "5"),
+			env.DiagnosticAtRegexp("p/x_test.go", "5"),
+		)
+		env.RegexpReplace("p/p.go", "s string", "i int")
+		env.Await(
+			EmptyDiagnostics("main.go"),
+			EmptyDiagnostics("p/p_test.go"),
+			EmptyDiagnostics("p/x_test.go"),
+		)
+	})
+}
diff --git a/internal/lsp/regtest/wrappers.go b/internal/lsp/regtest/wrappers.go
index 827dcdb..4ed36dd 100644
--- a/internal/lsp/regtest/wrappers.go
+++ b/internal/lsp/regtest/wrappers.go
@@ -51,6 +51,13 @@
 	}
 }
 
+func (e *Env) OpenFileWithContent(name, content string) {
+	e.T.Helper()
+	if err := e.Editor.OpenFileWithContent(e.Ctx, name, content); err != nil {
+		e.T.Fatal(err)
+	}
+}
+
 // CreateBuffer creates a buffer in the editor, calling t.Fatal on any error.
 func (e *Env) CreateBuffer(name string, content string) {
 	e.T.Helper()