internal/lsp: handle nil pointer with import shortcut = link

It makes more sense to handle the import shortcut behavior at a higher
level anyway, so pull it out of findIdentifier and add a test for the
configuration.

Fixes golang/go#44189

Change-Id: I96f08c7def154f6761efa727d693fdfb2fb722ab
Reviewed-on: https://go-review.googlesource.com/c/tools/+/290789
Trust: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
(cherry picked from commit fca8992500401369a4ef891b85f7570d77d080e0)
Reviewed-on: https://go-review.googlesource.com/c/tools/+/292729
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/gopls/go.mod b/gopls/go.mod
index 50dc2a4..93a91a0 100644
--- a/gopls/go.mod
+++ b/gopls/go.mod
@@ -14,3 +14,5 @@
 	mvdan.cc/gofumpt v0.1.0
 	mvdan.cc/xurls/v2 v2.2.0
 )
+
+replace golang.org/x/tools => ../
diff --git a/gopls/internal/regtest/misc/definition_test.go b/gopls/internal/regtest/misc/definition_test.go
index a5e220c..48b7617 100644
--- a/gopls/internal/regtest/misc/definition_test.go
+++ b/gopls/internal/regtest/misc/definition_test.go
@@ -11,6 +11,7 @@
 
 	. "golang.org/x/tools/gopls/internal/regtest"
 
+	"golang.org/x/tools/internal/lsp/fake"
 	"golang.org/x/tools/internal/lsp/tests"
 )
 
@@ -134,3 +135,47 @@
 		}
 	})
 }
+
+func TestImportShortcut(t *testing.T) {
+	const mod = `
+-- go.mod --
+module mod.com
+
+go 1.12
+-- main.go --
+package main
+
+import "fmt"
+
+func main() {}
+`
+	for _, tt := range []struct {
+		wantLinks      int
+		wantDef        bool
+		importShortcut string
+	}{
+		{1, false, "Link"},
+		{0, true, "Definition"},
+		{1, true, "Both"},
+	} {
+		t.Run(tt.importShortcut, func(t *testing.T) {
+			WithOptions(
+				EditorConfig{
+					ImportShortcut: tt.importShortcut,
+				},
+			).Run(t, mod, func(t *testing.T, env *Env) {
+				env.OpenFile("main.go")
+				file, pos := env.GoToDefinition("main.go", env.RegexpSearch("main.go", `"fmt"`))
+				if !tt.wantDef && (file != "" || pos != (fake.Pos{})) {
+					t.Fatalf("expected no definition, got one: %s:%v", file, pos)
+				} else if tt.wantDef && file == "" && pos == (fake.Pos{}) {
+					t.Fatalf("expected definition, got none")
+				}
+				links := env.DocumentLink("main.go")
+				if len(links) != tt.wantLinks {
+					t.Fatalf("expected %v links, got %v", tt.wantLinks, len(links))
+				}
+			})
+		})
+	}
+}
diff --git a/internal/lsp/definition.go b/internal/lsp/definition.go
index acd5ac2..46643e1 100644
--- a/internal/lsp/definition.go
+++ b/internal/lsp/definition.go
@@ -21,7 +21,9 @@
 	if err != nil {
 		return nil, err
 	}
-
+	if !snapshot.View().Options().ImportShortcut.ShowDefinition() {
+		return nil, nil
+	}
 	var locations []protocol.Location
 	for _, ref := range ident.Declaration.MappedRange {
 		decRange, err := ref.Range()
diff --git a/internal/lsp/fake/editor.go b/internal/lsp/fake/editor.go
index 0764e6e..96410d9 100644
--- a/internal/lsp/fake/editor.go
+++ b/internal/lsp/fake/editor.go
@@ -109,6 +109,8 @@
 	DirectoryFilters []string
 
 	VerboseOutput bool
+
+	ImportShortcut string
 }
 
 // NewEditor Creates a new Editor.
@@ -238,6 +240,10 @@
 		config["verboseOutput"] = true
 	}
 
+	if e.Config.ImportShortcut != "" {
+		config["importShortcut"] = e.Config.ImportShortcut
+	}
+
 	// TODO(rFindley): change to the new settings name once it is no longer
 	// designated experimental.
 	config["experimentalDiagnosticsDelay"] = "10ms"
diff --git a/internal/lsp/source/identifier.go b/internal/lsp/source/identifier.go
index 362604e..e648893 100644
--- a/internal/lsp/source/identifier.go
+++ b/internal/lsp/source/identifier.go
@@ -98,10 +98,7 @@
 	// Handle import specs separately, as there is no formal position for a
 	// package declaration.
 	if result, err := importSpec(snapshot, pkg, file, pos); result != nil || err != nil {
-		if snapshot.View().Options().ImportShortcut.ShowDefinition() {
-			return result, err
-		}
-		return nil, nil
+		return result, err
 	}
 	path := pathEnclosingObjNode(file, pos)
 	if path == nil {