internal/lsp: improve handling of non-Go folders

CL 244117 introduced a bug when modFile == os.DevNull: v.root is left
uninitialized, resulting in a view that appears to own all files. Fixing
that exposes a problem where opening a folder with no Go files and
GO111MODULE=on shows a popup. Skip the popup when no Go files are found.

Change-Id: I7f8b2d6fd2f954af64c3a65156ff44c649f3a5b2
Reviewed-on: https://go-review.googlesource.com/c/tools/+/248620
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go
index 083ec97..a55c770 100644
--- a/internal/lsp/cache/session.go
+++ b/internal/lsp/cache/session.go
@@ -170,6 +170,7 @@
 		cancel:             cancel,
 		name:               name,
 		folder:             folder,
+		root:               folder,
 		filesByURI:         make(map[span.URI]*fileBase),
 		filesByBase:        make(map[string][]*fileBase),
 	}
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index 48b3148..5239047 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -734,7 +734,6 @@
 		v.sumURI = span.URIFromPath(sumFilename)
 	}
 
-	v.root = v.folder
 	if options.ExpandWorkspaceToModule && v.modURI != "" {
 		v.root = span.URIFromPath(filepath.Dir(v.modURI.Filename()))
 	}
diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go
index 5fe9e06..17fd1da 100644
--- a/internal/lsp/diagnostics.go
+++ b/internal/lsp/diagnostics.go
@@ -8,6 +8,7 @@
 	"context"
 	"crypto/sha256"
 	"fmt"
+	"os"
 	"path/filepath"
 	"strings"
 	"sync"
@@ -317,7 +318,20 @@
 func (s *Server) handleFatalErrors(ctx context.Context, snapshot source.Snapshot, modErr, loadErr error) bool {
 	modURI := snapshot.View().ModFile()
 
-	// We currently only have workarounds for errors associated with modules.
+	// If the folder has no Go code in it, we shouldn't spam the user with a warning.
+	var hasGo bool
+	_ = filepath.Walk(snapshot.View().Folder().Filename(), func(path string, info os.FileInfo, err error) error {
+		if !strings.HasSuffix(info.Name(), ".go") {
+			return nil
+		}
+		hasGo = true
+		return errors.New("done")
+	})
+	if !hasGo {
+		return true
+	}
+
+	// All other workarounds are for errors associated with modules.
 	if modURI == "" {
 		return false
 	}
diff --git a/internal/lsp/regtest/diagnostics_test.go b/internal/lsp/regtest/diagnostics_test.go
index a200fbd..57c355c 100644
--- a/internal/lsp/regtest/diagnostics_test.go
+++ b/internal/lsp/regtest/diagnostics_test.go
@@ -541,14 +541,36 @@
 				CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1),
 				NoDiagnostics("a.go"),
 			),
-			EmptyShowMessage(""),
+			NoShowMessage(),
 		)
 		// introduce an error, expect no Show Message
 		env.RegexpReplace("a.go", "func", "fun")
-		env.Await(env.DiagnosticAtRegexp("a.go", "fun"), EmptyShowMessage(""))
+		env.Await(env.DiagnosticAtRegexp("a.go", "fun"), NoShowMessage())
 	})
 }
 
+func TestNonGoFolder(t *testing.T) {
+	const files = `
+-- hello.txt --
+hi mom
+`
+	for _, go111module := range []string{"on", "off", ""} {
+		t.Run(fmt.Sprintf("GO111MODULE_%v", go111module), func(t *testing.T) {
+			withOptions(WithEditorConfig(fake.EditorConfig{
+				Env: map[string]string{"GO111MODULE": go111module},
+			})).run(t, files, func(t *testing.T, env *Env) {
+				env.OpenFile("hello.txt")
+				env.Await(
+					OnceMet(
+						CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidOpen), 1),
+						NoShowMessage(),
+					),
+				)
+			})
+		})
+	}
+}
+
 // Tests golang/go#38602.
 func TestNonexistentFileDiagnostics_Issue38602(t *testing.T) {
 	const collision = `
diff --git a/internal/lsp/regtest/env.go b/internal/lsp/regtest/env.go
index 081d92a..a341986 100644
--- a/internal/lsp/regtest/env.go
+++ b/internal/lsp/regtest/env.go
@@ -375,11 +375,11 @@
 	}
 }
 
-// EmptyShowMessage asserts that the editor has not received a ShowMessage.
-func EmptyShowMessage(title string) SimpleExpectation {
+// NoShowMessage asserts that the editor has not received a ShowMessage.
+func NoShowMessage() SimpleExpectation {
 	check := func(s State) (Verdict, interface{}) {
 		if len(s.showMessage) == 0 {
-			return Met, title
+			return Met, "no ShowMessage"
 		}
 		return Unmeetable, nil
 	}