Skip to content

Commit 1aadbdf

Browse files
committed
go/packages: make sure TypesSizes are requested when Types are
This code fixes a bug when a user specifies NeedTypes, which implicitly requires TypesSizes, but TypesSizes isn't fetched, which causes typechecking to explode. Also fix a similar issue where NeedImports isn't implicitly fetched for NeedDeps. I added a TODO for a better fix, which is to have an "implicitMode" in the loader type containing all the data that's needed as a prerequisite for other fields. Then we can use implicitMode when fetching data, and cfg.Mode to clear out the fields the user didn't request. Fixes golang/go#31163 Change-Id: If3506765470af43dfb24d06fcbd31b66a623f2e0 Reviewed-on: https://go-review.googlesource.com/c/tools/+/170342 Reviewed-by: Ian Cottrell <[email protected]>
1 parent 202502a commit 1aadbdf

File tree

3 files changed

+104
-18
lines changed

3 files changed

+104
-18
lines changed

go/packages/golist.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
7878
var sizes types.Sizes
7979
var sizeserr error
8080
var sizeswg sync.WaitGroup
81-
if cfg.Mode&NeedTypesSizes != 0 {
81+
if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
8282
sizeswg.Add(1)
8383
go func() {
8484
sizes, sizeserr = getSizes(cfg)

go/packages/packages.go

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,12 @@ type loader struct {
420420
Config
421421
sizes types.Sizes
422422
exportMu sync.Mutex // enforces mutual exclusion of exportdata operations
423+
424+
// TODO(matloob): Add an implied mode here and use that instead of mode.
425+
// Implied mode would contain all the fields we need the data for so we can
426+
// get the actually requested fields. We'll zero them out before returning
427+
// packages to the user. This will make it easier for us to get the conditions
428+
// where we need certain modes right.
423429
}
424430

425431
func newLoader(cfg *Config) *loader {
@@ -563,7 +569,7 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
563569
return lpkg.needsrc
564570
}
565571

566-
if ld.Mode&NeedImports == 0 {
572+
if ld.Mode&(NeedImports|NeedDeps) == 0 {
567573
// We do this to drop the stub import packages that we are not even going to try to resolve.
568574
for _, lpkg := range initial {
569575
lpkg.Imports = nil
@@ -574,7 +580,7 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
574580
visit(lpkg)
575581
}
576582
}
577-
if ld.Mode&NeedDeps != 0 {
583+
if ld.Mode&NeedDeps != 0 { // TODO(matloob): This is only the case if NeedTypes is also set, right?
578584
for _, lpkg := range srcPkgs {
579585
// Complete type information is required for the
580586
// immediate dependencies of each source package.
@@ -602,46 +608,48 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
602608
importPlaceholders := make(map[string]*Package)
603609
for i, lpkg := range initial {
604610
result[i] = lpkg.Package
611+
}
612+
for i := range ld.pkgs {
605613
// Clear all unrequested fields, for extra de-Hyrum-ization.
606614
if ld.Mode&NeedName == 0 {
607-
result[i].Name = ""
608-
result[i].PkgPath = ""
615+
ld.pkgs[i].Name = ""
616+
ld.pkgs[i].PkgPath = ""
609617
}
610618
if ld.Mode&NeedFiles == 0 {
611-
result[i].GoFiles = nil
612-
result[i].OtherFiles = nil
619+
ld.pkgs[i].GoFiles = nil
620+
ld.pkgs[i].OtherFiles = nil
613621
}
614622
if ld.Mode&NeedCompiledGoFiles == 0 {
615-
result[i].CompiledGoFiles = nil
623+
ld.pkgs[i].CompiledGoFiles = nil
616624
}
617625
if ld.Mode&NeedImports == 0 {
618-
result[i].Imports = nil
626+
ld.pkgs[i].Imports = nil
619627
}
620628
if ld.Mode&NeedExportsFile == 0 {
621-
result[i].ExportFile = ""
629+
ld.pkgs[i].ExportFile = ""
622630
}
623631
if ld.Mode&NeedTypes == 0 {
624-
result[i].Types = nil
625-
result[i].Fset = nil
626-
result[i].IllTyped = false
632+
ld.pkgs[i].Types = nil
633+
ld.pkgs[i].Fset = nil
634+
ld.pkgs[i].IllTyped = false
627635
}
628636
if ld.Mode&NeedSyntax == 0 {
629-
result[i].Syntax = nil
637+
ld.pkgs[i].Syntax = nil
630638
}
631639
if ld.Mode&NeedTypesInfo == 0 {
632-
result[i].TypesInfo = nil
640+
ld.pkgs[i].TypesInfo = nil
633641
}
634642
if ld.Mode&NeedTypesSizes == 0 {
635-
result[i].TypesSizes = nil
643+
ld.pkgs[i].TypesSizes = nil
636644
}
637645
if ld.Mode&NeedDeps == 0 {
638-
for j, pkg := range result[i].Imports {
646+
for j, pkg := range ld.pkgs[i].Imports {
639647
ph, ok := importPlaceholders[pkg.ID]
640648
if !ok {
641649
ph = &Package{ID: pkg.ID}
642650
importPlaceholders[pkg.ID] = ph
643651
}
644-
result[i].Imports[j] = ph
652+
ld.pkgs[i].Imports[j] = ph
645653
}
646654
}
647655
}

go/packages/packages_test.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,84 @@ func testLoadTypes(t *testing.T, exporter packagestest.Exporter) {
576576
}
577577
}
578578

579+
// TestLoadTypesBits is equivalent to TestLoadTypes except that it only requests
580+
// the types using the NeedTypes bit.
581+
func TestLoadTypesBits(t *testing.T) { packagestest.TestAll(t, testLoadTypesBits) }
582+
func testLoadTypesBits(t *testing.T, exporter packagestest.Exporter) {
583+
exported := packagestest.Export(t, exporter, []packagestest.Module{{
584+
Name: "golang.org/fake",
585+
Files: map[string]interface{}{
586+
"a/a.go": `package a; import "golang.org/fake/b"; const A = "a" + b.B`,
587+
"b/b.go": `package b; import "golang.org/fake/c"; const B = "b" + c.C`,
588+
"c/c.go": `package c; import "golang.org/fake/d"; const C = "c" + d.D`,
589+
"d/d.go": `package d; import "golang.org/fake/e"; const D = "d" + e.E`,
590+
"e/e.go": `package e; import "golang.org/fake/f"; const E = "e" + f.F`,
591+
"f/f.go": `package f; const F = "f"`,
592+
}}})
593+
defer exported.Cleanup()
594+
595+
exported.Config.Mode = packages.NeedTypes | packages.NeedDeps | packages.NeedImports
596+
initial, err := packages.Load(exported.Config, "golang.org/fake/a", "golang.org/fake/c")
597+
if err != nil {
598+
t.Fatal(err)
599+
}
600+
601+
graph, all := importGraph(initial)
602+
wantGraph := `
603+
* golang.org/fake/a
604+
golang.org/fake/b
605+
* golang.org/fake/c
606+
golang.org/fake/d
607+
golang.org/fake/e
608+
golang.org/fake/f
609+
golang.org/fake/a -> golang.org/fake/b
610+
golang.org/fake/b -> golang.org/fake/c
611+
golang.org/fake/c -> golang.org/fake/d
612+
golang.org/fake/d -> golang.org/fake/e
613+
golang.org/fake/e -> golang.org/fake/f
614+
`[1:]
615+
if graph != wantGraph {
616+
t.Errorf("wrong import graph: got <<%s>>, want <<%s>>", graph, wantGraph)
617+
}
618+
619+
for _, test := range []struct {
620+
id string
621+
}{
622+
{"golang.org/fake/a"},
623+
{"golang.org/fake/b"},
624+
{"golang.org/fake/c"},
625+
{"golang.org/fake/d"},
626+
{"golang.org/fake/e"},
627+
{"golang.org/fake/f"},
628+
} {
629+
p := all[test.id]
630+
if p == nil {
631+
t.Errorf("missing package: %s", test.id)
632+
continue
633+
}
634+
if p.Types == nil {
635+
t.Errorf("missing types.Package for %s", p)
636+
continue
637+
}
638+
// We don't request the syntax, so we shouldn't get it.
639+
if p.Syntax != nil {
640+
t.Errorf("Syntax unexpectedly provided for %s", p)
641+
}
642+
if p.Errors != nil {
643+
t.Errorf("errors in package: %s: %s", p, p.Errors)
644+
}
645+
}
646+
647+
// Check value of constant.
648+
aA := constant(all["golang.org/fake/a"], "A")
649+
if aA == nil {
650+
t.Fatalf("a.A: got nil")
651+
}
652+
if got, want := fmt.Sprintf("%v %v", aA, aA.Val()), `const golang.org/fake/a.A untyped string "abcdef"`; got != want {
653+
t.Errorf("a.A: got %s, want %s", got, want)
654+
}
655+
}
656+
579657
func TestLoadSyntaxOK(t *testing.T) { packagestest.TestAll(t, testLoadSyntaxOK) }
580658
func testLoadSyntaxOK(t *testing.T, exporter packagestest.Exporter) {
581659
exported := packagestest.Export(t, exporter, []packagestest.Module{{

0 commit comments

Comments
 (0)