@@ -11,7 +11,6 @@ import (
11
11
"fmt"
12
12
"go/ast"
13
13
"go/types"
14
- "path"
15
14
"path/filepath"
16
15
"regexp"
17
16
"strings"
@@ -99,9 +98,13 @@ func (s *snapshot) buildPackageHandle(ctx context.Context, id PackageID, mode so
99
98
// TODO(adonovan): use a promise cache to ensure that the key
100
99
// for each package is computed by at most one thread, then do
101
100
// the recursive key building of dependencies in parallel.
102
- deps := make (map [PackagePath ]* packageHandle )
103
- depKeys := make ([]packageHandleKey , len (m .Deps ))
104
- for i , depID := range m .Deps {
101
+ deps := make (map [PackageID ]* packageHandle )
102
+ var depKey source.Hash // XOR of all unique deps
103
+ for _ , depID := range m .Imports {
104
+ depHandle , ok := deps [depID ]
105
+ if ok {
106
+ continue // e.g. duplicate import
107
+ }
105
108
depHandle , err := s .buildPackageHandle (ctx , depID , s .workspaceParseMode (depID ))
106
109
// Don't use invalid metadata for dependencies if the top-level
107
110
// metadata is valid. We only load top-level packages, so if the
@@ -125,8 +128,9 @@ func (s *snapshot) buildPackageHandle(ctx context.Context, id PackageID, mode so
125
128
continue
126
129
}
127
130
128
- deps [depHandle .m .PkgPath ] = depHandle
129
- depKeys [i ] = depHandle .key
131
+ depKey .XORWith (source .Hash (depHandle .key ))
132
+
133
+ deps [depID ] = depHandle
130
134
}
131
135
132
136
// Read both lists of files of this package, in parallel.
@@ -144,7 +148,7 @@ func (s *snapshot) buildPackageHandle(ctx context.Context, id PackageID, mode so
144
148
// All the file reading has now been done.
145
149
// Create a handle for the result of type checking.
146
150
experimentalKey := s .View ().Options ().ExperimentalPackageCacheKey
147
- phKey := computePackageKey (m .ID , compiledGoFiles , m , depKeys , mode , experimentalKey )
151
+ phKey := computePackageKey (m .ID , compiledGoFiles , m , depKey , mode , experimentalKey )
148
152
promise , release := s .store .Promise (phKey , func (ctx context.Context , arg interface {}) interface {} {
149
153
150
154
pkg , err := typeCheckImpl (ctx , arg .(* snapshot ), goFiles , compiledGoFiles , m .Metadata , mode , deps )
@@ -225,8 +229,8 @@ func (s *snapshot) workspaceParseMode(id PackageID) source.ParseMode {
225
229
226
230
// computePackageKey returns a key representing the act of type checking
227
231
// a package named id containing the specified files, metadata, and
228
- // dependency hashes .
229
- func computePackageKey (id PackageID , files []source.FileHandle , m * KnownMetadata , deps [] packageHandleKey , mode source.ParseMode , experimentalKey bool ) packageHandleKey {
232
+ // combined dependency hash .
233
+ func computePackageKey (id PackageID , files []source.FileHandle , m * KnownMetadata , depsKey source. Hash , mode source.ParseMode , experimentalKey bool ) packageHandleKey {
230
234
// TODO(adonovan): opt: no need to materalize the bytes; hash them directly.
231
235
// Also, use field separators to avoid spurious collisions.
232
236
b := bytes .NewBuffer (nil )
@@ -243,9 +247,7 @@ func computePackageKey(id PackageID, files []source.FileHandle, m *KnownMetadata
243
247
b .Write (hc [:])
244
248
}
245
249
b .WriteByte (byte (mode ))
246
- for _ , dep := range deps {
247
- b .Write (dep [:])
248
- }
250
+ b .Write (depsKey [:])
249
251
for _ , file := range files {
250
252
b .WriteString (file .FileIdentity ().String ())
251
253
}
@@ -311,7 +313,7 @@ func (ph *packageHandle) cached() (*pkg, error) {
311
313
// typeCheckImpl type checks the parsed source files in compiledGoFiles.
312
314
// (The resulting pkg also holds the parsed but not type-checked goFiles.)
313
315
// deps holds the future results of type-checking the direct dependencies.
314
- func typeCheckImpl (ctx context.Context , snapshot * snapshot , goFiles , compiledGoFiles []source.FileHandle , m * Metadata , mode source.ParseMode , deps map [PackagePath ]* packageHandle ) (* pkg , error ) {
316
+ func typeCheckImpl (ctx context.Context , snapshot * snapshot , goFiles , compiledGoFiles []source.FileHandle , m * Metadata , mode source.ParseMode , deps map [PackageID ]* packageHandle ) (* pkg , error ) {
315
317
// Start type checking of direct dependencies,
316
318
// in parallel and asynchronously.
317
319
// As the type checker imports each of these
@@ -446,15 +448,15 @@ func typeCheckImpl(ctx context.Context, snapshot *snapshot, goFiles, compiledGoF
446
448
447
449
var goVersionRx = regexp .MustCompile (`^go([1-9][0-9]*)\.(0|[1-9][0-9]*)$` )
448
450
449
- func doTypeCheck (ctx context.Context , snapshot * snapshot , goFiles , compiledGoFiles []source.FileHandle , m * Metadata , mode source.ParseMode , deps map [PackagePath ]* packageHandle , astFilter * unexportedFilter ) (* pkg , error ) {
451
+ func doTypeCheck (ctx context.Context , snapshot * snapshot , goFiles , compiledGoFiles []source.FileHandle , m * Metadata , mode source.ParseMode , deps map [PackageID ]* packageHandle , astFilter * unexportedFilter ) (* pkg , error ) {
450
452
ctx , done := event .Start (ctx , "cache.typeCheck" , tag .Package .Of (string (m .ID )))
451
453
defer done ()
452
454
453
455
pkg := & pkg {
454
- m : m ,
455
- mode : mode ,
456
- imports : make (map [PackagePath ]* pkg ),
457
- types : types .NewPackage (string (m .PkgPath ), string (m .Name )),
456
+ m : m ,
457
+ mode : mode ,
458
+ depsByPkgPath : make (map [PackagePath ]* pkg ),
459
+ types : types .NewPackage (string (m .PkgPath ), string (m .Name )),
458
460
typesInfo : & types.Info {
459
461
Types : make (map [ast.Expr ]types.TypeAndValue ),
460
462
Defs : make (map [* ast.Ident ]types.Object ),
@@ -522,23 +524,30 @@ func doTypeCheck(ctx context.Context, snapshot *snapshot, goFiles, compiledGoFil
522
524
Error : func (e error ) {
523
525
pkg .typeErrors = append (pkg .typeErrors , e .(types.Error ))
524
526
},
525
- Importer : importerFunc (func (pkgPath string ) (* types.Package , error ) {
526
- // If the context was cancelled, we should abort.
527
- if ctx .Err () != nil {
528
- return nil , ctx .Err ()
527
+ Importer : importerFunc (func (path string ) (* types.Package , error ) {
528
+ // While all of the import errors could be reported
529
+ // based on the metadata before we start type checking,
530
+ // reporting them via types.Importer places the errors
531
+ // at the correct source location.
532
+ id , ok := pkg .m .Imports [ImportPath (path )]
533
+ if ! ok {
534
+ // If the import declaration is broken,
535
+ // go list may fail to report metadata about it.
536
+ // See TestFixImportDecl for an example.
537
+ return nil , fmt .Errorf ("missing metadata for import of %q" , path )
529
538
}
530
- dep := resolveImportPath ( pkgPath , pkg , deps )
531
- if dep == nil {
532
- return nil , snapshot .missingPkgError (ctx , pkgPath )
539
+ dep , ok := deps [ id ]
540
+ if ! ok {
541
+ return nil , snapshot .missingPkgError (ctx , path )
533
542
}
534
543
if ! source .IsValidImport (string (m .PkgPath ), string (dep .m .PkgPath )) {
535
- return nil , fmt .Errorf ("invalid use of internal package %s" , pkgPath )
544
+ return nil , fmt .Errorf ("invalid use of internal package %s" , path )
536
545
}
537
546
depPkg , err := dep .await (ctx , snapshot )
538
547
if err != nil {
539
548
return nil , err
540
549
}
541
- pkg .imports [depPkg .m .PkgPath ] = depPkg
550
+ pkg .depsByPkgPath [depPkg .m .PkgPath ] = depPkg
542
551
return depPkg .types , nil
543
552
}),
544
553
}
@@ -840,41 +849,6 @@ func expandErrors(errs []types.Error, supportsRelatedInformation bool) []extende
840
849
return result
841
850
}
842
851
843
- // resolveImportPath resolves an import path in pkg to a package from deps.
844
- // It should produce the same results as resolveImportPath:
845
- // https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/load/pkg.go;drc=641918ee09cb44d282a30ee8b66f99a0b63eaef9;l=990.
846
- func resolveImportPath (importPath string , pkg * pkg , deps map [PackagePath ]* packageHandle ) * packageHandle {
847
- if dep := deps [PackagePath (importPath )]; dep != nil {
848
- return dep
849
- }
850
- // We may be in GOPATH mode, in which case we need to check vendor dirs.
851
- searchDir := path .Dir (pkg .PkgPath ())
852
- for {
853
- vdir := PackagePath (path .Join (searchDir , "vendor" , importPath ))
854
- if vdep := deps [vdir ]; vdep != nil {
855
- return vdep
856
- }
857
-
858
- // Search until Dir doesn't take us anywhere new, e.g. "." or "/".
859
- next := path .Dir (searchDir )
860
- if searchDir == next {
861
- break
862
- }
863
- searchDir = next
864
- }
865
-
866
- // Vendor didn't work. Let's try minimal module compatibility mode.
867
- // In MMC, the packagePath is the canonical (.../vN/...) path, which
868
- // is hard to calculate. But the go command has already resolved the ID
869
- // to the non-versioned path, and we can take advantage of that.
870
- for _ , dep := range deps {
871
- if dep .ID () == importPath {
872
- return dep
873
- }
874
- }
875
- return nil
876
- }
877
-
878
852
// An importFunc is an implementation of the single-method
879
853
// types.Importer interface based on a function value.
880
854
type importerFunc func (path string ) (* types.Package , error )
0 commit comments