Skip to content

Commit 79faf92

Browse files
author
Bryan C. Mills
committed
cmd/go/internal/load: pass the importer's package path when checking visibility
A module like "gopkg.in/macaroon.v2" might have a test with a "_test" package suffix (see https://golang.org/cmd/go/#hdr-Test_packages). When we compile that test, its ImportStack entry includes the "_test" suffix even though nothing else can actually import it via that path. When we look up the module containing such a package, we must use the original path, not the suffixed one. On the other hand, an actual importable package may also be named with the suffix "_test", so we need to be careful not to strip the suffix if it is legitimately part of the path. We cannot distinguish that case by examining srcDir or the ImportStack: the srcDir contaning a module doesn't necessarily bear any relationship to its import path, and the ImportStack doesn't tell us whether the suffix is part of the original path. Fortunately, LoadImport usually has more information that we can use: it receives a parent *Package that includes the original import path. Fixes #26722 Change-Id: I1f7a4b37dbcb70e46af1caf9a496dfdd59ae8b17 Reviewed-on: https://go-review.googlesource.com/127796 Reviewed-by: Russ Cox <[email protected]>
1 parent d893573 commit 79faf92

File tree

3 files changed

+116
-28
lines changed

3 files changed

+116
-28
lines changed

src/cmd/go/internal/load/pkg.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,8 @@ func (p *PackageError) Error() string {
319319
}
320320

321321
// An ImportStack is a stack of import paths, possibly with the suffix " (test)" appended.
322-
// TODO(bcmills): When the tree opens for 1.12, replace the suffixed string with a struct.
322+
// The import path of a test package is the import path of the corresponding
323+
// non-test package with the suffix "_test" added.
323324
type ImportStack []string
324325

325326
func (s *ImportStack) Push(p string) {
@@ -468,6 +469,11 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
468469
}
469470
}
470471

472+
parentPath := ""
473+
if parent != nil {
474+
parentPath = parent.ImportPath
475+
}
476+
471477
// Determine canonical identifier for this package.
472478
// For a local import the identifier is the pseudo-import path
473479
// we create from the full directory to the package.
@@ -481,10 +487,6 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
481487
if isLocal {
482488
importPath = dirToImportPath(filepath.Join(srcDir, path))
483489
} else if cfg.ModulesEnabled {
484-
parentPath := ""
485-
if parent != nil {
486-
parentPath = parent.ImportPath
487-
}
488490
var p string
489491
modDir, p, modErr = ModLookup(parentPath, path)
490492
if modErr == nil {
@@ -557,11 +559,11 @@ func LoadImport(path, srcDir string, parent *Package, stk *ImportStack, importPo
557559
}
558560

559561
// Checked on every import because the rules depend on the code doing the importing.
560-
if perr := disallowInternal(srcDir, p, stk); perr != p {
562+
if perr := disallowInternal(srcDir, parentPath, p, stk); perr != p {
561563
return setErrorPos(perr, importPos)
562564
}
563565
if mode&ResolveImport != 0 {
564-
if perr := disallowVendor(srcDir, origPath, p, stk); perr != p {
566+
if perr := disallowVendor(srcDir, parentPath, origPath, p, stk); perr != p {
565567
return setErrorPos(perr, importPos)
566568
}
567569
}
@@ -922,10 +924,11 @@ func reusePackage(p *Package, stk *ImportStack) *Package {
922924
return p
923925
}
924926

925-
// disallowInternal checks that srcDir is allowed to import p.
927+
// disallowInternal checks that srcDir (containing package importerPath, if non-empty)
928+
// is allowed to import p.
926929
// If the import is allowed, disallowInternal returns the original package p.
927930
// If not, it returns a new package containing just an appropriate error.
928-
func disallowInternal(srcDir string, p *Package, stk *ImportStack) *Package {
931+
func disallowInternal(srcDir, importerPath string, p *Package, stk *ImportStack) *Package {
929932
// golang.org/s/go14internal:
930933
// An import of a path containing the element “internal”
931934
// is disallowed if the importing code is outside the tree
@@ -969,7 +972,6 @@ func disallowInternal(srcDir string, p *Package, stk *ImportStack) *Package {
969972
i-- // rewind over slash in ".../internal"
970973
}
971974

972-
var where string
973975
if p.Module == nil {
974976
parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)]
975977

@@ -987,18 +989,16 @@ func disallowInternal(srcDir string, p *Package, stk *ImportStack) *Package {
987989
// p is in a module, so make it available based on the import path instead
988990
// of the file path (https://golang.org/issue/23970).
989991
parent := p.ImportPath[:i]
990-
importer := strings.TrimSuffix((*stk)[len(*stk)-2], " (test)")
991-
if str.HasPathPrefix(importer, parent) {
992+
if str.HasPathPrefix(importerPath, parent) {
992993
return p
993994
}
994-
where = " in " + importer
995995
}
996996

997997
// Internal is present, and srcDir is outside parent's tree. Not allowed.
998998
perr := *p
999999
perr.Error = &PackageError{
10001000
ImportStack: stk.Copy(),
1001-
Err: "use of internal package " + p.ImportPath + " not allowed" + where,
1001+
Err: "use of internal package " + p.ImportPath + " not allowed",
10021002
}
10031003
perr.Incomplete = true
10041004
return &perr
@@ -1023,10 +1023,11 @@ func findInternal(path string) (index int, ok bool) {
10231023
return 0, false
10241024
}
10251025

1026-
// disallowVendor checks that srcDir is allowed to import p as path.
1026+
// disallowVendor checks that srcDir (containing package importerPath, if non-empty)
1027+
// is allowed to import p as path.
10271028
// If the import is allowed, disallowVendor returns the original package p.
10281029
// If not, it returns a new package containing just an appropriate error.
1029-
func disallowVendor(srcDir, path string, p *Package, stk *ImportStack) *Package {
1030+
func disallowVendor(srcDir, importerPath, path string, p *Package, stk *ImportStack) *Package {
10301031
// The stack includes p.ImportPath.
10311032
// If that's the only thing on the stack, we started
10321033
// with a name given on the command line, not an
@@ -1035,13 +1036,12 @@ func disallowVendor(srcDir, path string, p *Package, stk *ImportStack) *Package
10351036
return p
10361037
}
10371038

1038-
if p.Standard && ModPackageModuleInfo != nil {
1039+
if p.Standard && ModPackageModuleInfo != nil && importerPath != "" {
10391040
// Modules must not import vendor packages in the standard library,
10401041
// but the usual vendor visibility check will not catch them
10411042
// because the module loader presents them with an ImportPath starting
10421043
// with "golang_org/" instead of "vendor/".
1043-
importer := strings.TrimSuffix((*stk)[len(*stk)-2], " (test)")
1044-
if mod := ModPackageModuleInfo(importer); mod != nil {
1044+
if mod := ModPackageModuleInfo(importerPath); mod != nil {
10451045
dir := p.Dir
10461046
if relDir, err := filepath.Rel(p.Root, p.Dir); err == nil {
10471047
dir = relDir

src/cmd/go/testdata/script/mod_internal.txt

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,22 @@ rm go.mod
55
go mod init golang.org/x/anything
66
go build .
77

8+
# ...and their tests...
9+
go test
10+
stdout PASS
11+
812
# ...but that should not leak into other modules.
913
! go build ./baddep
10-
stderr 'use of internal package golang.org/x/.* not allowed in golang.org/notx/useinternal$'
14+
stderr golang.org[/\\]notx[/\\]useinternal
15+
stderr 'use of internal package golang.org/x/.* not allowed'
1116

1217
# Internal packages in the standard library should not leak into modules.
1318
! go build ./fromstd
14-
stderr 'use of internal package internal/testenv not allowed$'
19+
stderr 'use of internal package internal/testenv not allowed'
1520

1621
# Packages found via standard-library vendoring should not leak.
1722
! go build ./fromstdvendor
18-
stderr 'use of vendored package golang_org/x/net/http/httpguts not allowed$'
23+
stderr 'use of vendored package golang_org/x/net/http/httpguts not allowed'
1924

2025

2126
# Dependencies should be able to use their own internal modules...
@@ -25,12 +30,12 @@ go build ./throughdep
2530

2631
# ... but other modules should not, even if they have transitive dependencies.
2732
! go build .
28-
stderr 'use of internal package golang.org/x/.* not allowed in golang.org/notx$'
33+
stderr 'use of internal package golang.org/x/.* not allowed'
2934

3035
# And transitive dependencies still should not leak.
3136
! go build ./baddep
32-
stderr 'use of internal package golang.org/x/.* not allowed in golang.org/notx/useinternal$'
33-
37+
stderr golang.org[/\\]notx[/\\]useinternal
38+
stderr 'use of internal package golang.org/x/.* not allowed'
3439

3540
# Replacing an internal module should keep it internal to the same paths.
3641
rm go.mod
@@ -39,18 +44,29 @@ go mod edit -replace golang.org/x/internal=./replace/golang.org/notx/internal
3944
go build ./throughdep
4045

4146
! go build ./baddep
42-
stderr 'use of internal package golang.org/x/.* not allowed in golang.org/notx/useinternal$'
47+
stderr golang.org[/\\]notx[/\\]useinternal
48+
stderr 'use of internal package golang.org/x/.* not allowed'
4349

4450
go mod edit -replace golang.org/x/internal=./vendor/golang.org/x/internal
4551
go build ./throughdep
4652

4753
! go build ./baddep
48-
stderr 'use of internal package golang.org/x/.* not allowed in golang.org/notx/useinternal$'
54+
stderr golang.org[/\\]notx[/\\]useinternal
55+
stderr 'use of internal package golang.org/x/.* not allowed'
4956

5057
-- useinternal.go --
5158
package useinternal
5259
import _ "golang.org/x/internal/subtle"
5360

61+
-- useinternal_test.go --
62+
package useinternal_test
63+
import (
64+
"testing"
65+
_ "golang.org/x/internal/subtle"
66+
)
67+
68+
func Test(*testing.T) {}
69+
5470
-- throughdep/useinternal.go --
5571
package throughdep
5672
import _ "golang.org/x/useinternal"

src/cmd/go/testdata/script/mod_test.txt

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
11
env GO111MODULE=on
22

3+
# A test in the module's root package should work.
34
cd a/
45
go test
56
stdout PASS
67

8+
# A test with the "_test" suffix in the module root should also work.
9+
cd ../b/
10+
go test
11+
stdout PASS
12+
13+
# A test with the "_test" suffix of a *package* with a "_test" suffix should
14+
# even work (not that you should ever do that).
15+
cd ../c_test
16+
go test
17+
stdout PASS
18+
19+
cd ../d_test
20+
go test
21+
stdout PASS
22+
723
-- a/go.mod --
8-
module github.com/user/a
24+
module example.com/user/a
925

1026
-- a/a.go --
1127
package a
@@ -16,3 +32,59 @@ package a
1632
import "testing"
1733

1834
func Test(t *testing.T) {}
35+
36+
-- b/go.mod --
37+
module example.com/user/b
38+
39+
-- b/b.go --
40+
package b
41+
42+
-- b/b_test.go --
43+
package b_test
44+
45+
import "testing"
46+
47+
func Test(t *testing.T) {}
48+
49+
-- c_test/go.mod --
50+
module example.com/c_test
51+
52+
-- c_test/umm.go --
53+
// Package c_test is the non-test package for its import path!
54+
package c_test
55+
56+
-- c_test/c_test_test.go --
57+
package c_test_test
58+
59+
import "testing"
60+
61+
func Test(t *testing.T) {}
62+
63+
-- d_test/go.mod --
64+
// Package d is an ordinary package in a deceptively-named directory.
65+
module example.com/d
66+
67+
-- d_test/d.go --
68+
package d
69+
70+
-- d_test/d_test.go --
71+
package d_test
72+
73+
import "testing"
74+
75+
func Test(t *testing.T) {}
76+
77+
-- e/go.mod --
78+
module example.com/e_test
79+
80+
-- e/wat.go --
81+
// Package e_test is the non-test package for its import path,
82+
// in a deceptively-named directory!
83+
package e_test
84+
85+
-- e/e_test.go --
86+
package e_test_test
87+
88+
import "testing"
89+
90+
func Test(t *testing.T) {}

0 commit comments

Comments
 (0)