gopls/internal/lsp/source: Package clarifications

Some preparatory simplifications:
- Group the fields of Package into metadata, parse info, type info.
- Derive TypesSizes from metadata; eliminate field.
- Remove MissingDependencies method by specializing it for its sole use,
  avoiding the need to invert the go/types heuristic.

Also, clarify sort in codelens.

Change-Id: Iecd5b49046207034c64acd5b0240a9fed7761538
Reviewed-on: https://go-review.googlesource.com/c/tools/+/452835
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Alan Donovan <adonovan@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/gopls/internal/lsp/cache/check.go b/gopls/internal/lsp/cache/check.go
index a22e285..663cf25 100644
--- a/gopls/internal/lsp/cache/check.go
+++ b/gopls/internal/lsp/cache/check.go
@@ -457,7 +457,6 @@
 			Selections: make(map[*ast.SelectorExpr]*types.Selection),
 			Scopes:     make(map[ast.Node]*types.Scope),
 		},
-		typesSizes: m.TypesSizes,
 	}
 	typeparams.InitInstanceInfo(pkg.typesInfo)
 
diff --git a/gopls/internal/lsp/cache/pkg.go b/gopls/internal/lsp/cache/pkg.go
index 5c43a82..6466b8e 100644
--- a/gopls/internal/lsp/cache/pkg.go
+++ b/gopls/internal/lsp/cache/pkg.go
@@ -10,7 +10,6 @@
 	"go/scanner"
 	"go/token"
 	"go/types"
-	"sort"
 
 	"golang.org/x/mod/module"
 	"golang.org/x/tools/gopls/internal/lsp/source"
@@ -40,7 +39,6 @@
 	typeErrors      []types.Error
 	types           *types.Package
 	typesInfo       *types.Info
-	typesSizes      types.Sizes
 	hasFixedFiles   bool // if true, AST was sufficiently mangled that we should hide type errors
 
 	analyses memoize.Store // maps analyzer.Name to Promise[actionResult]
@@ -113,7 +111,7 @@
 }
 
 func (p *pkg) GetTypesSizes() types.Sizes {
-	return p.typesSizes
+	return p.m.TypesSizes
 }
 
 func (p *pkg) ForTest() string {
@@ -144,41 +142,6 @@
 	return nil, fmt.Errorf("package does not import %s", importPath)
 }
 
-func (p *pkg) MissingDependencies() []ImportPath {
-	// We don't invalidate metadata for import deletions,
-	// so check the package imports via the *types.Package.
-	//
-	// rfindley says: it looks like this is intending to implement
-	// a heuristic "if go list couldn't resolve import paths to
-	// packages, then probably you're not in GOPATH or a module".
-	// This is used to determine if we need to show a warning diagnostic.
-	// It looks like this logic is implementing the heuristic that
-	// "even if the metadata has a MissingDep, if the types.Package
-	// doesn't need that dep anymore we shouldn't show the warning".
-	// But either we're outside of GOPATH/Module, or we're not...
-	//
-	// adonovan says: I think this effectively reverses the
-	// heuristic used by the type checker when Importer.Import
-	// returns an error---go/types synthesizes a package whose
-	// Path is the import path (sans "vendor/")---hence the
-	// dubious ImportPath() conversion. A blank DepsByImpPath
-	// entry means a missing import.
-	//
-	// If we invalidate the metadata for import deletions (which
-	// should be fast) then we can simply return the blank entries
-	// in DepsByImpPath. (They are PackageIDs not PackagePaths,
-	// but the caller only cares whether the set is empty!)
-	var missing []ImportPath
-	for _, pkg := range p.types.Imports() {
-		importPath := ImportPath(pkg.Path())
-		if id, ok := p.m.DepsByImpPath[importPath]; ok && id == "" {
-			missing = append(missing, importPath)
-		}
-	}
-	sort.Slice(missing, func(i, j int) bool { return missing[i] < missing[j] })
-	return missing
-}
-
 func (p *pkg) Imports() []source.Package {
 	var result []source.Package // unordered
 	for _, dep := range p.deps {
diff --git a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snapshot.go
index 9a4c5b3..369235f 100644
--- a/gopls/internal/lsp/cache/snapshot.go
+++ b/gopls/internal/lsp/cache/snapshot.go
@@ -1430,13 +1430,43 @@
 		return ""
 	}
 	for _, pkg := range pkgs {
-		if len(pkg.MissingDependencies()) > 0 {
+		if hasMissingDependencies(pkg) {
 			return adHocPackagesWarning
 		}
 	}
 	return ""
 }
 
+func hasMissingDependencies(pkg source.Package) bool {
+	// We don't invalidate metadata for import deletions,
+	// so check the package imports via the syntax tree
+	// (not via types.Package.Imports, since it contains packages
+	// synthesized under the vendoring-hostile assumption that
+	// ImportPath equals PackagePath).
+	//
+	// rfindley says: it looks like this is intending to implement
+	// a heuristic "if go list couldn't resolve import paths to
+	// packages, then probably you're not in GOPATH or a module".
+	// This is used to determine if we need to show a warning diagnostic.
+	// It looks like this logic is implementing the heuristic that
+	// "even if the metadata has a MissingDep, if the types.Package
+	// doesn't need that dep anymore we shouldn't show the warning".
+	// But either we're outside of GOPATH/Module, or we're not...
+	//
+	// If we invalidate the metadata for import deletions (which
+	// should be fast) then we can simply return the blank entries
+	// in DepsByImpPath.
+	for _, f := range pkg.GetSyntax() {
+		for _, imp := range f.Imports {
+			importPath := source.UnquoteImportPath(imp)
+			if _, err := pkg.ResolveImportPath(importPath); err != nil {
+				return true
+			}
+		}
+	}
+	return false
+}
+
 func containsCommandLineArguments(pkgs []source.Package) bool {
 	for _, pkg := range pkgs {
 		if source.IsCommandLineArguments(pkg.ID()) {
diff --git a/gopls/internal/lsp/code_lens.go b/gopls/internal/lsp/code_lens.go
index 4bbe2bb..f554e79 100644
--- a/gopls/internal/lsp/code_lens.go
+++ b/gopls/internal/lsp/code_lens.go
@@ -9,11 +9,11 @@
 	"fmt"
 	"sort"
 
-	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/gopls/internal/lsp/command"
 	"golang.org/x/tools/gopls/internal/lsp/mod"
 	"golang.org/x/tools/gopls/internal/lsp/protocol"
 	"golang.org/x/tools/gopls/internal/lsp/source"
+	"golang.org/x/tools/internal/event"
 )
 
 func (s *Server) codeLens(ctx context.Context, params *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
@@ -48,10 +48,10 @@
 	}
 	sort.Slice(result, func(i, j int) bool {
 		a, b := result[i], result[j]
-		if protocol.CompareRange(a.Range, b.Range) == 0 {
-			return a.Command.Command < b.Command.Command
+		if cmp := protocol.CompareRange(a.Range, b.Range); cmp != 0 {
+			return cmp < 0
 		}
-		return protocol.CompareRange(a.Range, b.Range) < 0
+		return a.Command.Command < b.Command.Command
 	})
 	return result, nil
 }
diff --git a/gopls/internal/lsp/source/completion/completion.go b/gopls/internal/lsp/source/completion/completion.go
index 144d658..1eeabc9 100644
--- a/gopls/internal/lsp/source/completion/completion.go
+++ b/gopls/internal/lsp/source/completion/completion.go
@@ -1083,12 +1083,7 @@
 	// Is sel a qualified identifier?
 	if id, ok := sel.X.(*ast.Ident); ok {
 		if pkgName, ok := c.pkg.GetTypesInfo().Uses[id].(*types.PkgName); ok {
-			var pkg source.Package
-			for _, imp := range c.pkg.Imports() {
-				if imp.PkgPath() == source.PackagePath(pkgName.Imported().Path()) {
-					pkg = imp
-				}
-			}
+			pkg, _ := c.pkg.DirectDep(source.PackagePath(pkgName.Imported().Path()))
 			// If the package is not imported, try searching for unimported
 			// completions.
 			if pkg == nil && c.opts.unimported {
diff --git a/gopls/internal/lsp/source/view.go b/gopls/internal/lsp/source/view.go
index aed3f54..ad10129 100644
--- a/gopls/internal/lsp/source/view.go
+++ b/gopls/internal/lsp/source/view.go
@@ -676,28 +676,32 @@
 	ImportPath  string // path that appears in an import declaration (e.g. "example.com/foo")
 )
 
-// Package represents a Go package that has been type-checked. It maintains
-// only the relevant fields of a *go/packages.Package.
+// Package represents a Go package that has been parsed and type-checked.
+// It maintains only the relevant fields of a *go/packages.Package.
 type Package interface {
+	// Metadata:
 	ID() PackageID
 	Name() PackageName
 	PkgPath() PackagePath
-	CompiledGoFiles() []*ParsedGoFile
-	File(uri span.URI) (*ParsedGoFile, error)
-	FileSet() *token.FileSet
-	GetSyntax() []*ast.File
-	GetTypes() *types.Package
-	GetTypesInfo() *types.Info
 	GetTypesSizes() types.Sizes
 	ForTest() string
+	Version() *module.Version
+
+	// Results of parsing:
+	FileSet() *token.FileSet
+	ParseMode() ParseMode
+	CompiledGoFiles() []*ParsedGoFile // (borrowed)
+	File(uri span.URI) (*ParsedGoFile, error)
+	GetSyntax() []*ast.File // (borrowed)
+	HasListOrParseErrors() bool
+
+	// Results of type checking:
+	GetTypes() *types.Package
+	GetTypesInfo() *types.Info
 	DirectDep(path PackagePath) (Package, error)
 	ResolveImportPath(path ImportPath) (Package, error)
-	MissingDependencies() []ImportPath // unordered
-	Imports() []Package
-	Version() *module.Version
-	HasListOrParseErrors() bool
+	Imports() []Package // new slice of all direct dependencies, unordered
 	HasTypeErrors() bool
-	ParseMode() ParseMode
 }
 
 // A CriticalError is a workspace-wide error that generally prevents gopls from