Skip to content
This repository was archived by the owner on Nov 5, 2024. It is now read-only.

Commit 3ae24b8

Browse files
committed
go/packages: allow types loading without NeedDeps
Before separating Load* into Need* we could use LoadSyntax to get types information by loading inital packages from source code and loading it's direct dependencies from export data. It was broken when separation was introduced and before this commit everything was loading from source code what resulted into slow loading times. This commit fixes it. Also, do backwards-incompatible fix of definition of deprecated LoadImports and LoadAllSyntax. Improve an internal error message "internal error: nil Pkg importing x from y": replace it with "internal error: package x without types was imported from y". Remove packages.NeedDeps request for loading in tests TestLoadTypesBits and TestContainsOverlayXTest. Fixes golang/go#31752, fixes golang/go#33077, fixes golang/go#32814, fixes golang/go#31699, golang/go#31930
1 parent 5b82db0 commit 3ae24b8

File tree

3 files changed

+132
-49
lines changed

3 files changed

+132
-49
lines changed

go/packages/golist.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -831,8 +831,7 @@ func golistargs(cfg *Config, words []string) []string {
831831
fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypesInfo|NeedTypesSizes) != 0),
832832
fmt.Sprintf("-test=%t", cfg.Tests),
833833
fmt.Sprintf("-export=%t", usesExportData(cfg)),
834-
fmt.Sprintf("-deps=%t", cfg.Mode&NeedDeps != 0 ||
835-
cfg.Mode&NeedTypesInfo != 0), // Dependencies are required to do typechecking on sources, which is required for the TypesInfo.
834+
fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0),
836835
// go list doesn't let you pass -test and -find together,
837836
// probably because you'd just get the TestMain.
838837
fmt.Sprintf("-find=%t", !cfg.Tests && cfg.Mode&findFlags == 0),

go/packages/packages.go

+36-35
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ const (
4848
// "placeholder" Packages with only the ID set.
4949
NeedImports
5050

51-
// NeedDeps adds the fields requested by the LoadMode in the packages in Imports. If NeedImports
52-
// is not set NeedDeps has no effect.
51+
// NeedDeps adds the fields requested by the LoadMode in the packages in Imports.
52+
// If NeedImports is not set, it will be added automatically.
5353
NeedDeps
5454

5555
// NeedExportsFile adds ExportsFile.
@@ -61,7 +61,7 @@ const (
6161
// NeedSyntax adds Syntax.
6262
NeedSyntax
6363

64-
// NeedTypesInfo adds TypesInfo.
64+
// NeedTypesInfo adds TypesInfo. If NeedImports is not set, it will be added automatically.
6565
NeedTypesInfo
6666

6767
// NeedTypesSizes adds TypesSizes.
@@ -75,7 +75,7 @@ const (
7575

7676
// Deprecated: LoadImports exists for historical compatibility
7777
// and should not be used. Please directly specify the needed fields using the Need values.
78-
LoadImports = LoadFiles | NeedImports | NeedDeps
78+
LoadImports = LoadFiles | NeedImports
7979

8080
// Deprecated: LoadTypes exists for historical compatibility
8181
// and should not be used. Please directly specify the needed fields using the Need values.
@@ -87,7 +87,7 @@ const (
8787

8888
// Deprecated: LoadAllSyntax exists for historical compatibility
8989
// and should not be used. Please directly specify the needed fields using the Need values.
90-
LoadAllSyntax = LoadSyntax
90+
LoadAllSyntax = LoadSyntax | NeedDeps
9191
)
9292

9393
// A Config specifies details about how packages should be loaded.
@@ -476,6 +476,8 @@ func newLoader(cfg *Config) *loader {
476476
}
477477
}
478478
}
479+
480+
ld.addDependingLoadModes()
479481
return ld
480482
}
481483

@@ -496,7 +498,7 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
496498
}
497499
lpkg := &loaderPackage{
498500
Package: pkg,
499-
needtypes: (ld.Mode&(NeedTypes|NeedTypesInfo) != 0 && rootIndex < 0) || rootIndex >= 0,
501+
needtypes: (ld.Mode&(NeedTypes|NeedTypesInfo) != 0 && ld.Mode&NeedDeps != 0 && rootIndex < 0) || rootIndex >= 0,
500502
needsrc: (ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && ld.Mode&NeedDeps != 0 && rootIndex < 0) || rootIndex >= 0 ||
501503
len(ld.Overlay) > 0 || // Overlays can invalidate export data. TODO(matloob): make this check fine-grained based on dependencies on overlaid files
502504
pkg.ExportFile == "" && pkg.PkgPath != "unsafe",
@@ -544,10 +546,8 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
544546
lpkg.color = grey
545547
stack = append(stack, lpkg) // push
546548
stubs := lpkg.Imports // the structure form has only stubs with the ID in the Imports
547-
// If NeedTypesInfo we need dependencies (at least for the roots) to typecheck the package.
548549
// If NeedImports isn't set, the imports fields will all be zeroed out.
549-
// If NeedDeps isn't also set we want to keep the stubs.
550-
if ld.Mode&NeedTypesInfo != 0 || (ld.Mode&NeedImports != 0 && ld.Mode&NeedDeps != 0) {
550+
if ld.Mode&NeedImports != 0 {
551551
lpkg.Imports = make(map[string]*Package, len(stubs))
552552
for importPath, ipkg := range stubs {
553553
var importErr error
@@ -566,11 +566,8 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
566566
continue
567567
}
568568

569-
// If !NeedDeps, just fill Imports for the root. No need to recurse further.
570-
if ld.Mode&NeedDeps != 0 {
571-
if visit(imp) {
572-
lpkg.needsrc = true
573-
}
569+
if visit(imp) {
570+
lpkg.needsrc = true
574571
}
575572
lpkg.Imports[importPath] = imp.Package
576573
}
@@ -587,7 +584,7 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
587584
return lpkg.needsrc
588585
}
589586

590-
if ld.Mode&(NeedImports|NeedDeps|NeedTypesInfo) == 0 {
587+
if ld.Mode&NeedImports == 0 {
591588
// We do this to drop the stub import packages that we are not even going to try to resolve.
592589
for _, lpkg := range initial {
593590
lpkg.Imports = nil
@@ -598,7 +595,7 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
598595
visit(lpkg)
599596
}
600597
}
601-
if ld.Mode&NeedDeps != 0 { // TODO(matloob): This is only the case if NeedTypes is also set, right?
598+
if ld.Mode&NeedImports != 0 && ld.Mode&NeedTypes != 0 {
602599
for _, lpkg := range srcPkgs {
603600
// Complete type information is required for the
604601
// immediate dependencies of each source package.
@@ -623,7 +620,6 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
623620
}
624621

625622
result := make([]*Package, len(initial))
626-
importPlaceholders := make(map[string]*Package)
627623
for i, lpkg := range initial {
628624
result[i] = lpkg.Package
629625
}
@@ -660,17 +656,8 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
660656
if ld.Mode&NeedTypesSizes == 0 {
661657
ld.pkgs[i].TypesSizes = nil
662658
}
663-
if ld.Mode&NeedDeps == 0 {
664-
for j, pkg := range ld.pkgs[i].Imports {
665-
ph, ok := importPlaceholders[pkg.ID]
666-
if !ok {
667-
ph = &Package{ID: pkg.ID}
668-
importPlaceholders[pkg.ID] = ph
669-
}
670-
ld.pkgs[i].Imports[j] = ph
671-
}
672-
}
673659
}
660+
674661
return result, nil
675662
}
676663

@@ -691,7 +678,6 @@ func (ld *loader) loadRecursive(lpkg *loaderPackage) {
691678
}(imp)
692679
}
693680
wg.Wait()
694-
695681
ld.loadPackage(lpkg)
696682
})
697683
}
@@ -818,7 +804,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
818804
if ipkg.Types != nil && ipkg.Types.Complete() {
819805
return ipkg.Types, nil
820806
}
821-
log.Fatalf("internal error: nil Pkg importing %q from %q", path, lpkg)
807+
log.Fatalf("internal error: package %q without types was imported from %q", path, lpkg)
822808
panic("unreachable")
823809
})
824810

@@ -829,7 +815,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
829815
// Type-check bodies of functions only in non-initial packages.
830816
// Example: for import graph A->B->C and initial packages {A,C},
831817
// we can ignore function bodies in B.
832-
IgnoreFuncBodies: (ld.Mode&(NeedDeps|NeedTypesInfo) == 0) && !lpkg.initial,
818+
IgnoreFuncBodies: ld.Mode&NeedDeps == 0 && !lpkg.initial,
833819

834820
Error: appendError,
835821
Sizes: ld.sizes,
@@ -1091,10 +1077,25 @@ func (ld *loader) loadFromExportData(lpkg *loaderPackage) (*types.Package, error
10911077
return tpkg, nil
10921078
}
10931079

1080+
// addDependingLoadModes adds dependencies for choosed LoadMode in ld.Mode
1081+
func (ld *loader) addDependingLoadModes() {
1082+
if ld.Mode&NeedTypesInfo != 0 && ld.Mode&NeedImports == 0 {
1083+
// If NeedTypesInfo, go/packages needs to do typechecking itself so it can
1084+
// associate type info with the AST. To do so, we need the export data
1085+
// for dependencies, which means we need to ask for the direct dependencies.
1086+
// NeedImports is used to ask for the direct dependencies.
1087+
ld.Mode |= NeedImports
1088+
ld.Logf("Added load mode dependency of NeedTypesInfo: NeedImports")
1089+
}
1090+
1091+
if ld.Mode&NeedDeps != 0 && ld.Mode&NeedImports == 0 {
1092+
// With NeedDeps we need to load at least direct dependencies.
1093+
// NeedImports is used to ask for the direct dependencies.
1094+
ld.Mode |= NeedImports
1095+
ld.Logf("Added load mode dependency of NeedDeps: NeedImports")
1096+
}
1097+
}
1098+
10941099
func usesExportData(cfg *Config) bool {
1095-
return cfg.Mode&NeedExportsFile != 0 ||
1096-
// If NeedTypes but not NeedTypesInfo we won't typecheck using sources, so we need export data.
1097-
(cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedTypesInfo == 0) ||
1098-
// If NeedTypesInfo but not NeedDeps, we're typechecking a package using its sources plus its dependencies' export data
1099-
(cfg.Mode&NeedTypesInfo != 0 && cfg.Mode&NeedDeps == 0)
1100+
return cfg.Mode&NeedExportsFile != 0 || cfg.Mode&NeedTypes != 0 && cfg.Mode&NeedDeps == 0
11001101
}

go/packages/packages_test.go

+95-12
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ func testLoadTypesBits(t *testing.T, exporter packagestest.Exporter) {
577577
}}})
578578
defer exported.Cleanup()
579579

580-
exported.Config.Mode = packages.NeedTypes | packages.NeedDeps | packages.NeedImports
580+
exported.Config.Mode = packages.NeedTypes | packages.NeedImports
581581
initial, err := packages.Load(exported.Config, "golang.org/fake/a", "golang.org/fake/c")
582582
if err != nil {
583583
t.Fatal(err)
@@ -678,14 +678,16 @@ func testLoadSyntaxOK(t *testing.T, exporter packagestest.Exporter) {
678678
}
679679

680680
for _, test := range []struct {
681-
id string
681+
id string
682+
wantSyntax bool
683+
wantComplete bool
682684
}{
683-
{"golang.org/fake/a"}, // source package
684-
{"golang.org/fake/b"}, // source package
685-
{"golang.org/fake/c"}, // source package
686-
{"golang.org/fake/d"}, // export data package
687-
{"golang.org/fake/e"}, // export data package
688-
{"golang.org/fake/f"}, // export data package
685+
{"golang.org/fake/a", true, true}, // source package
686+
{"golang.org/fake/b", true, true}, // source package because depends on initial package
687+
{"golang.org/fake/c", true, true}, // source package
688+
{"golang.org/fake/d", false, true}, // export data package
689+
{"golang.org/fake/e", false, false}, // export data package
690+
{"golang.org/fake/f", false, false}, // export data package
689691
} {
690692
// TODO(matloob): LoadSyntax and LoadAllSyntax are now equivalent, wantSyntax and wantComplete
691693
// are true for all packages in the transitive dependency set. Add test cases on the individual
@@ -698,9 +700,19 @@ func testLoadSyntaxOK(t *testing.T, exporter packagestest.Exporter) {
698700
if p.Types == nil {
699701
t.Errorf("missing types.Package for %s", p)
700702
continue
703+
} else if p.Types.Complete() != test.wantComplete {
704+
if test.wantComplete {
705+
t.Errorf("incomplete types.Package for %s", p)
706+
} else {
707+
t.Errorf("unexpected complete types.Package for %s", p)
708+
}
701709
}
702-
if p.Syntax == nil {
703-
t.Errorf("missing ast.Files for %s", p)
710+
if (p.Syntax != nil) != test.wantSyntax {
711+
if test.wantSyntax {
712+
t.Errorf("missing ast.Files for %s", p)
713+
} else {
714+
t.Errorf("unexpected ast.Files for for %s", p)
715+
}
704716
}
705717
if p.Errors != nil {
706718
t.Errorf("errors in package: %s: %s", p, p.Errors)
@@ -796,7 +808,7 @@ func testLoadSyntaxError(t *testing.T, exporter packagestest.Exporter) {
796808
{"golang.org/fake/c", true, true},
797809
{"golang.org/fake/d", true, true},
798810
{"golang.org/fake/e", true, true},
799-
{"golang.org/fake/f", true, false},
811+
{"golang.org/fake/f", false, false},
800812
} {
801813
p := all[test.id]
802814
if p == nil {
@@ -1364,7 +1376,7 @@ func testContainsOverlayXTest(t *testing.T, exporter packagestest.Exporter) {
13641376
}}})
13651377
defer exported.Cleanup()
13661378
bOverlayXTestFile := filepath.Join(filepath.Dir(exported.File("golang.org/fake", "b/b.go")), "b_overlay_x_test.go")
1367-
exported.Config.Mode = packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps
1379+
exported.Config.Mode = packages.NeedName | packages.NeedFiles | packages.NeedImports
13681380
exported.Config.Overlay = map[string][]byte{bOverlayXTestFile: []byte(`package b_test; import "golang.org/fake/b"`)}
13691381
initial, err := packages.Load(exported.Config, "file="+bOverlayXTestFile)
13701382
if err != nil {
@@ -2178,6 +2190,77 @@ func testIssue32814(t *testing.T, exporter packagestest.Exporter) {
21782190
}
21792191
}
21802192

2193+
func TestLoadTypesInfoWithoutNeedDeps(t *testing.T) {
2194+
packagestest.TestAll(t, testLoadTypesInfoWithoutNeedDeps)
2195+
}
2196+
func testLoadTypesInfoWithoutNeedDeps(t *testing.T, exporter packagestest.Exporter) {
2197+
exported := packagestest.Export(t, exporter, []packagestest.Module{{
2198+
Name: "golang.org/fake",
2199+
Files: map[string]interface{}{
2200+
"a/a.go": `package a; import _ "golang.org/fake/b"`,
2201+
"b/b.go": `package b`,
2202+
}}})
2203+
defer exported.Cleanup()
2204+
2205+
exported.Config.Mode = packages.NeedTypes | packages.NeedTypesInfo | packages.NeedImports
2206+
pkgs, err := packages.Load(exported.Config, "golang.org/fake/a")
2207+
if err != nil {
2208+
t.Fatal(err)
2209+
}
2210+
pkg := pkgs[0]
2211+
if pkg.IllTyped {
2212+
t.Fatal("Loaded package is ill typed")
2213+
}
2214+
const expectedImport = "golang.org/fake/b"
2215+
if _, ok := pkg.Imports[expectedImport]; !ok || len(pkg.Imports) != 1 {
2216+
t.Fatalf("Imports of loaded package: want [%s], got %v", expectedImport, pkg.Imports)
2217+
}
2218+
}
2219+
2220+
func TestLoadWithNeedDeps(t *testing.T) {
2221+
packagestest.TestAll(t, testLoadWithNeedDeps)
2222+
}
2223+
func testLoadWithNeedDeps(t *testing.T, exporter packagestest.Exporter) {
2224+
exported := packagestest.Export(t, exporter, []packagestest.Module{{
2225+
Name: "golang.org/fake",
2226+
Files: map[string]interface{}{
2227+
"a/a.go": `package a; import _ "golang.org/fake/b"`,
2228+
"b/b.go": `package b; import _ "golang.org/fake/c"`,
2229+
"c/c.go": `package c`,
2230+
}}})
2231+
defer exported.Cleanup()
2232+
2233+
exported.Config.Mode = packages.NeedTypes | packages.NeedTypesInfo | packages.NeedImports | packages.NeedDeps
2234+
pkgs, err := packages.Load(exported.Config, "golang.org/fake/a")
2235+
if err != nil {
2236+
t.Fatal(err)
2237+
}
2238+
if len(pkgs) != 1 {
2239+
t.Fatalf("Expected 1 package, got %d", len(pkgs))
2240+
}
2241+
2242+
pkgA := pkgs[0]
2243+
if pkgA.IllTyped {
2244+
t.Fatal("Loaded package is ill typed")
2245+
}
2246+
2247+
pkgB := pkgA.Imports["golang.org/fake/b"]
2248+
if pkgB == nil || len(pkgA.Imports) != 1 {
2249+
t.Fatalf("Imports of loaded package 'a' are invalid: %v", pkgA.Imports)
2250+
}
2251+
if pkgB.Types == nil || !pkgB.Types.Complete() || pkgB.TypesInfo == nil {
2252+
t.Fatalf("Types of package 'b' are nil or incomplete: %v, %v", pkgB.Types, pkgB.TypesInfo)
2253+
}
2254+
2255+
pkgC := pkgB.Imports["golang.org/fake/c"]
2256+
if pkgC == nil || len(pkgB.Imports) != 1 {
2257+
t.Fatalf("Imports of loaded package 'c' are invalid: %v", pkgB.Imports)
2258+
}
2259+
if pkgC.Types == nil || !pkgC.Types.Complete() || pkgC.TypesInfo == nil {
2260+
t.Fatalf("Types of package 'b' are nil or incomplete: %v, %v", pkgC.Types, pkgC.TypesInfo)
2261+
}
2262+
}
2263+
21812264
func errorMessages(errors []packages.Error) []string {
21822265
var msgs []string
21832266
for _, err := range errors {

0 commit comments

Comments
 (0)