gopls/internal/lsp/cache: limit module scan to 100K files

When no go.work or go.mod file is found, gopls searches to see if there
is exactly one module in a nested directory, in which case it narrows
the workspace to this one module. This is a legacy workaround for
polyglot repositories, and will be made obsolete by golang/go#57979.

However, in the meantime this feature is still necessary, and is the
last remaining place where we walk the workspace looking for modules. As
reported in golang/go#56496, this search can be expensive in very large
directories.

Reduce the search limit 10x, from 1M->100K, and use the more efficient
filepath.WalkDir.

Fixes golang/go#56496

Change-Id: Ia46dd90ac2220b09debc68742dd882885c38eb42
Reviewed-on: https://go-review.googlesource.com/c/tools/+/496880
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
diff --git a/gopls/internal/lsp/cache/workspace.go b/gopls/internal/lsp/cache/workspace.go
index de36da6..28179f5 100644
--- a/gopls/internal/lsp/cache/workspace.go
+++ b/gopls/internal/lsp/cache/workspace.go
@@ -8,6 +8,7 @@
 	"context"
 	"errors"
 	"fmt"
+	"io/fs"
 	"os"
 	"path/filepath"
 	"sort"
@@ -127,7 +128,10 @@
 
 // Limit go.mod search to 1 million files. As a point of reference,
 // Kubernetes has 22K files (as of 2020-11-24).
-const fileLimit = 1000000
+//
+// Note: per golang/go#56496, the previous limit of 1M files was too slow, at
+// which point this limit was decreased to 100K.
+const fileLimit = 100_000
 
 // findModules recursively walks the root directory looking for go.mod files,
 // returning the set of modules it discovers. If modLimit is non-zero,
@@ -139,7 +143,7 @@
 	modFiles := make(map[span.URI]struct{})
 	searched := 0
 	errDone := errors.New("done")
-	err := filepath.Walk(root.Filename(), func(path string, info os.FileInfo, err error) error {
+	err := filepath.WalkDir(root.Filename(), func(path string, info fs.DirEntry, err error) error {
 		if err != nil {
 			// Probably a permission error. Keep looking.
 			return filepath.SkipDir