internal/lsp/cache: fix incorrect detection of useless code

Our logic to detect whether suggested fixes delete unnecessary code did
not take into account suggested fixes that have a command.

Fixes golang/go#48938

Change-Id: I70d2bebbf7059236525117cc1f454ef726162a96
Reviewed-on: https://go-review.googlesource.com/c/tools/+/355769
Trust: Robert Findley <rfindley@google.com>
Trust: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
diff --git a/gopls/internal/regtest/diagnostics/undeclared_test.go b/gopls/internal/regtest/diagnostics/undeclared_test.go
new file mode 100644
index 0000000..79f7d42
--- /dev/null
+++ b/gopls/internal/regtest/diagnostics/undeclared_test.go
@@ -0,0 +1,67 @@
+// Copyright 2021 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 diagnostics
+
+import (
+	"testing"
+
+	"golang.org/x/tools/internal/lsp/protocol"
+	. "golang.org/x/tools/internal/lsp/regtest"
+)
+
+func TestUndeclaredDiagnostics(t *testing.T) {
+	src := `
+-- go.mod --
+module mod.com
+
+go 1.12
+-- a/a.go --
+package a
+
+func _() int {
+	return x
+}
+-- b/b.go --
+package b
+
+func _() int {
+	var y int
+	y = y
+	return y
+}
+`
+	Run(t, src, func(t *testing.T, env *Env) {
+		isUnnecessary := func(diag protocol.Diagnostic) bool {
+			for _, tag := range diag.Tags {
+				if tag == protocol.Unnecessary {
+					return true
+				}
+			}
+			return false
+		}
+
+		// 'x' is undeclared, but still necessary.
+		env.OpenFile("a/a.go")
+		env.Await(env.DiagnosticAtRegexp("a/a.go", "x"))
+		diags := env.DiagnosticsFor("a/a.go")
+		if got := len(diags.Diagnostics); got != 1 {
+			t.Errorf("len(Diagnostics) = %d, want 1", got)
+		}
+		if diag := diags.Diagnostics[0]; isUnnecessary(diag) {
+			t.Errorf("%v tagged unnecessary, want necessary", diag)
+		}
+
+		// 'y = y' is pointless, and should be detected as unnecessary.
+		env.OpenFile("b/b.go")
+		env.Await(env.DiagnosticAtRegexp("b/b.go", "y = y"))
+		diags = env.DiagnosticsFor("b/b.go")
+		if got := len(diags.Diagnostics); got != 1 {
+			t.Errorf("len(Diagnostics) = %d, want 1", got)
+		}
+		if diag := diags.Diagnostics[0]; !isUnnecessary(diag) {
+			t.Errorf("%v tagged necessary, want unnecessary", diag)
+		}
+	})
+}
diff --git a/internal/lsp/cache/errors.go b/internal/lsp/cache/errors.go
index 3f58d67..6790938 100644
--- a/internal/lsp/cache/errors.go
+++ b/internal/lsp/cache/errors.go
@@ -233,6 +233,9 @@
 // onlyDeletions returns true if all of the suggested fixes are deletions.
 func onlyDeletions(fixes []source.SuggestedFix) bool {
 	for _, fix := range fixes {
+		if fix.Command != nil {
+			return false
+		}
 		for _, edits := range fix.Edits {
 			for _, edit := range edits {
 				if edit.NewText != "" {