internal/imports: use cache of mod cache pkgs in find packages

To check if a package is in a module that is in scope, the module
resolver checks if there are Go files that would be included in a
package in the directory matching the import path in scope.

If this directory is in the module cache and we have saved it as a
package, we know this directory contains Go files, and do not have to
read the directory.

Change-Id: I7c9365ce42c760ab95bc68b036212120895c89fb
Reviewed-on: https://go-review.googlesource.com/c/tools/+/186922
Run-TryBot: Suzy Mueller <suzmue@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/imports/mod.go b/internal/imports/mod.go
index c9797be..98d5ff3 100644
--- a/internal/imports/mod.go
+++ b/internal/imports/mod.go
@@ -121,6 +121,17 @@
 			continue
 		}
 
+		if info, ok := r.moduleCacheInfo.Load(pkgDir); ok {
+			if packageScanned, err := info.reachedStatus(directoryScanned); packageScanned {
+				if err != nil {
+					// There was some error with scanning this directory.
+					// It does not contain a valid package.
+					continue
+				}
+				return m, pkgDir
+			}
+		}
+
 		pkgFiles, err := ioutil.ReadDir(pkgDir)
 		if err != nil {
 			continue
diff --git a/internal/imports/mod_cache.go b/internal/imports/mod_cache.go
index 884706a..ad28483 100644
--- a/internal/imports/mod_cache.go
+++ b/internal/imports/mod_cache.go
@@ -51,6 +51,18 @@
 	needsReplace bool
 }
 
+// reachedStatus returns true when info has a status at least target and any error associated with
+// an attempt to reach target.
+func (info *directoryPackageInfo) reachedStatus(target directoryPackageStatus) (bool, error) {
+	if info.err == nil {
+		return info.status >= target, nil
+	}
+	if info.status == target {
+		return true, info.err
+	}
+	return true, nil
+}
+
 // moduleCacheInfo is a concurrency safe map for storing information about
 // the directories in the module cache.
 //
diff --git a/internal/imports/mod_cache_test.go b/internal/imports/mod_cache_test.go
new file mode 100644
index 0000000..7d04560
--- /dev/null
+++ b/internal/imports/mod_cache_test.go
@@ -0,0 +1,52 @@
+package imports
+
+import (
+	"fmt"
+	"testing"
+)
+
+func TestDirectoryPackageInfoReachedStatus(t *testing.T) {
+	tests := []struct {
+		info       directoryPackageInfo
+		target     directoryPackageStatus
+		wantStatus bool
+		wantError  bool
+	}{
+		{
+			info: directoryPackageInfo{
+				status: directoryScanned,
+				err:    nil,
+			},
+			target:     directoryScanned,
+			wantStatus: true,
+		},
+		{
+			info: directoryPackageInfo{
+				status: directoryScanned,
+				err:    fmt.Errorf("error getting to directory scanned"),
+			},
+			target:     directoryScanned,
+			wantStatus: true,
+			wantError:  true,
+		},
+		{
+			info:       directoryPackageInfo{},
+			target:     directoryScanned,
+			wantStatus: false,
+		},
+	}
+
+	for _, tt := range tests {
+		gotStatus, gotErr := tt.info.reachedStatus(tt.target)
+		if gotErr != nil {
+			if !tt.wantError {
+				t.Errorf("unexpected error: %s", gotErr)
+			}
+			continue
+		}
+
+		if tt.wantStatus != gotStatus {
+			t.Errorf("reached status expected: %v, got: %v", tt.wantStatus, gotStatus)
+		}
+	}
+}