mirror of
https://github.com/golang/go
synced 2024-11-24 09:30:07 -07:00
cmd/go/internal/modload: prevent tidy downgrading disambiguating modules
If an indirectly required module does not provide any packages needed to build packages in the main module but is needed to disambiguate imports, 'go mod tidy' may keep an indirect requirement on that module to prevent it from being downgraded. This can prevent the introduction of new ambiguities. This also ensures tidy keeps sums needed to load all packages. Fixes #47738 Change-Id: Ib8e33422b95394707894cda7cfbb71a4b111e0ed Reviewed-on: https://go-review.googlesource.com/c/go/+/344572 Trust: Jay Conrod <jayconrod@google.com> Run-TryBot: Jay Conrod <jayconrod@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
This commit is contained in:
parent
72bb8185b5
commit
6196979365
@ -591,7 +591,7 @@ func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements,
|
|||||||
// selected at the same version or is upgraded by the dependencies of a
|
// selected at the same version or is upgraded by the dependencies of a
|
||||||
// root.
|
// root.
|
||||||
//
|
//
|
||||||
// If any module that provided a package has been upgraded above its previous,
|
// If any module that provided a package has been upgraded above its previous
|
||||||
// version, the caller may need to reload and recompute the package graph.
|
// version, the caller may need to reload and recompute the package graph.
|
||||||
//
|
//
|
||||||
// To ensure that the loading process eventually converges, the caller should
|
// To ensure that the loading process eventually converges, the caller should
|
||||||
@ -980,17 +980,37 @@ func spotCheckRoots(ctx context.Context, rs *Requirements, mods map[module.Versi
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// tidyUnprunedRoots returns a minimal set of root requirements that maintains the
|
// tidyUnprunedRoots returns a minimal set of root requirements that maintains
|
||||||
// selected version of every module that provided a package in pkgs, and
|
// the selected version of every module that provided or lexically could have
|
||||||
// includes the selected version of every such module in direct as a root.
|
// provided a package in pkgs, and includes the selected version of every such
|
||||||
|
// module in direct as a root.
|
||||||
func tidyUnprunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
|
func tidyUnprunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
|
||||||
var (
|
var (
|
||||||
|
// keep is a set of of modules that provide packages or are needed to
|
||||||
|
// disambiguate imports.
|
||||||
keep []module.Version
|
keep []module.Version
|
||||||
keptPath = map[string]bool{}
|
keptPath = map[string]bool{}
|
||||||
)
|
|
||||||
var (
|
// rootPaths is a list of module paths that provide packages directly
|
||||||
rootPaths []string // module paths that should be included as roots
|
// imported from the main module. They should be included as roots.
|
||||||
|
rootPaths []string
|
||||||
inRootPaths = map[string]bool{}
|
inRootPaths = map[string]bool{}
|
||||||
|
|
||||||
|
// altMods is a set of paths of modules that lexically could have provided
|
||||||
|
// imported packages. It may be okay to remove these from the list of
|
||||||
|
// explicit requirements if that removes them from the module graph. If they
|
||||||
|
// are present in the module graph reachable from rootPaths, they must not
|
||||||
|
// be at a lower version. That could cause a missing sum error or a new
|
||||||
|
// import ambiguity.
|
||||||
|
//
|
||||||
|
// For example, suppose a developer rewrites imports from example.com/m to
|
||||||
|
// example.com/m/v2, then runs 'go mod tidy'. Tidy may delete the
|
||||||
|
// requirement on example.com/m if there is no other transitive requirement
|
||||||
|
// on it. However, if example.com/m were downgraded to a version not in
|
||||||
|
// go.sum, when package example.com/m/v2/p is loaded, we'd get an error
|
||||||
|
// trying to disambiguate the import, since we can't check example.com/m
|
||||||
|
// without its sum. See #47738.
|
||||||
|
altMods = map[string]string{}
|
||||||
)
|
)
|
||||||
for _, pkg := range pkgs {
|
for _, pkg := range pkgs {
|
||||||
if !pkg.fromExternalModule() {
|
if !pkg.fromExternalModule() {
|
||||||
@ -1004,12 +1024,44 @@ func tidyUnprunedRoots(ctx context.Context, mainModule module.Version, direct ma
|
|||||||
inRootPaths[m.Path] = true
|
inRootPaths[m.Path] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, m := range pkg.altMods {
|
||||||
|
altMods[m.Path] = m.Version
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
min, err := mvs.Req(mainModule, rootPaths, &mvsReqs{roots: keep})
|
// Construct a build list with a minimal set of roots.
|
||||||
|
// This may remove or downgrade modules in altMods.
|
||||||
|
reqs := &mvsReqs{roots: keep}
|
||||||
|
min, err := mvs.Req(mainModule, rootPaths, reqs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
buildList, err := mvs.BuildList([]module.Version{mainModule}, reqs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if modules in altMods were downgraded but not removed.
|
||||||
|
// If so, add them to roots, which will retain an "// indirect" requirement
|
||||||
|
// in go.mod. See comment on altMods above.
|
||||||
|
keptAltMod := false
|
||||||
|
for _, m := range buildList {
|
||||||
|
if v, ok := altMods[m.Path]; ok && semver.Compare(m.Version, v) < 0 {
|
||||||
|
keep = append(keep, module.Version{Path: m.Path, Version: v})
|
||||||
|
keptAltMod = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if keptAltMod {
|
||||||
|
// We must run mvs.Req again instead of simply adding altMods to min.
|
||||||
|
// It's possible that a requirement in altMods makes some other
|
||||||
|
// explicit indirect requirement unnecessary.
|
||||||
|
reqs.roots = keep
|
||||||
|
min, err = mvs.Req(mainModule, rootPaths, reqs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return newRequirements(unpruned, min, direct), nil
|
return newRequirements(unpruned, min, direct), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,20 +243,24 @@ func (e *invalidImportError) Unwrap() error {
|
|||||||
//
|
//
|
||||||
// If the package is not present in any module selected from the requirement
|
// If the package is not present in any module selected from the requirement
|
||||||
// graph, importFromModules returns an *ImportMissingError.
|
// graph, importFromModules returns an *ImportMissingError.
|
||||||
func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, err error) {
|
//
|
||||||
|
// If the package is present in exactly one module, importFromModules will
|
||||||
|
// return the module, its root directory, and a list of other modules that
|
||||||
|
// lexically could have provided the package but did not.
|
||||||
|
func importFromModules(ctx context.Context, path string, rs *Requirements, mg *ModuleGraph) (m module.Version, dir string, altMods []module.Version, err error) {
|
||||||
if strings.Contains(path, "@") {
|
if strings.Contains(path, "@") {
|
||||||
return module.Version{}, "", fmt.Errorf("import path should not have @version")
|
return module.Version{}, "", nil, fmt.Errorf("import path should not have @version")
|
||||||
}
|
}
|
||||||
if build.IsLocalImport(path) {
|
if build.IsLocalImport(path) {
|
||||||
return module.Version{}, "", fmt.Errorf("relative import not supported")
|
return module.Version{}, "", nil, fmt.Errorf("relative import not supported")
|
||||||
}
|
}
|
||||||
if path == "C" {
|
if path == "C" {
|
||||||
// There's no directory for import "C".
|
// There's no directory for import "C".
|
||||||
return module.Version{}, "", nil
|
return module.Version{}, "", nil, nil
|
||||||
}
|
}
|
||||||
// Before any further lookup, check that the path is valid.
|
// Before any further lookup, check that the path is valid.
|
||||||
if err := module.CheckImportPath(path); err != nil {
|
if err := module.CheckImportPath(path); err != nil {
|
||||||
return module.Version{}, "", &invalidImportError{importPath: path, err: err}
|
return module.Version{}, "", nil, &invalidImportError{importPath: path, err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is the package in the standard library?
|
// Is the package in the standard library?
|
||||||
@ -265,14 +269,14 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
|
|||||||
for _, mainModule := range MainModules.Versions() {
|
for _, mainModule := range MainModules.Versions() {
|
||||||
if MainModules.InGorootSrc(mainModule) {
|
if MainModules.InGorootSrc(mainModule) {
|
||||||
if dir, ok, err := dirInModule(path, MainModules.PathPrefix(mainModule), MainModules.ModRoot(mainModule), true); err != nil {
|
if dir, ok, err := dirInModule(path, MainModules.PathPrefix(mainModule), MainModules.ModRoot(mainModule), true); err != nil {
|
||||||
return module.Version{}, dir, err
|
return module.Version{}, dir, nil, err
|
||||||
} else if ok {
|
} else if ok {
|
||||||
return mainModule, dir, nil
|
return mainModule, dir, nil, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dir := filepath.Join(cfg.GOROOT, "src", path)
|
dir := filepath.Join(cfg.GOROOT, "src", path)
|
||||||
return module.Version{}, dir, nil
|
return module.Version{}, dir, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// -mod=vendor is special.
|
// -mod=vendor is special.
|
||||||
@ -283,19 +287,19 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
|
|||||||
mainDir, mainOK, mainErr := dirInModule(path, MainModules.PathPrefix(mainModule), modRoot, true)
|
mainDir, mainOK, mainErr := dirInModule(path, MainModules.PathPrefix(mainModule), modRoot, true)
|
||||||
vendorDir, vendorOK, _ := dirInModule(path, "", filepath.Join(modRoot, "vendor"), false)
|
vendorDir, vendorOK, _ := dirInModule(path, "", filepath.Join(modRoot, "vendor"), false)
|
||||||
if mainOK && vendorOK {
|
if mainOK && vendorOK {
|
||||||
return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}}
|
return module.Version{}, "", nil, &AmbiguousImportError{importPath: path, Dirs: []string{mainDir, vendorDir}}
|
||||||
}
|
}
|
||||||
// Prefer to return main directory if there is one,
|
// Prefer to return main directory if there is one,
|
||||||
// Note that we're not checking that the package exists.
|
// Note that we're not checking that the package exists.
|
||||||
// We'll leave that for load.
|
// We'll leave that for load.
|
||||||
if !vendorOK && mainDir != "" {
|
if !vendorOK && mainDir != "" {
|
||||||
return mainModule, mainDir, nil
|
return mainModule, mainDir, nil, nil
|
||||||
}
|
}
|
||||||
if mainErr != nil {
|
if mainErr != nil {
|
||||||
return module.Version{}, "", mainErr
|
return module.Version{}, "", nil, mainErr
|
||||||
}
|
}
|
||||||
readVendorList(mainModule)
|
readVendorList(mainModule)
|
||||||
return vendorPkgModule[path], vendorDir, nil
|
return vendorPkgModule[path], vendorDir, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check each module on the build list.
|
// Check each module on the build list.
|
||||||
@ -316,7 +320,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
|
|||||||
// already non-nil, then we attempt to load the package using the full
|
// already non-nil, then we attempt to load the package using the full
|
||||||
// requirements in mg.
|
// requirements in mg.
|
||||||
for {
|
for {
|
||||||
var sumErrMods []module.Version
|
var sumErrMods, altMods []module.Version
|
||||||
for prefix := path; prefix != "."; prefix = pathpkg.Dir(prefix) {
|
for prefix := path; prefix != "."; prefix = pathpkg.Dir(prefix) {
|
||||||
var (
|
var (
|
||||||
v string
|
v string
|
||||||
@ -350,13 +354,15 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
|
|||||||
// continue the loop and find the package in some other module,
|
// continue the loop and find the package in some other module,
|
||||||
// we need to look at this module to make sure the import is
|
// we need to look at this module to make sure the import is
|
||||||
// not ambiguous.
|
// not ambiguous.
|
||||||
return module.Version{}, "", err
|
return module.Version{}, "", nil, err
|
||||||
}
|
}
|
||||||
if dir, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil {
|
if dir, ok, err := dirInModule(path, m.Path, root, isLocal); err != nil {
|
||||||
return module.Version{}, "", err
|
return module.Version{}, "", nil, err
|
||||||
} else if ok {
|
} else if ok {
|
||||||
mods = append(mods, m)
|
mods = append(mods, m)
|
||||||
dirs = append(dirs, dir)
|
dirs = append(dirs, dir)
|
||||||
|
} else {
|
||||||
|
altMods = append(altMods, m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,7 +375,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
|
|||||||
mods[i], mods[j] = mods[j], mods[i]
|
mods[i], mods[j] = mods[j], mods[i]
|
||||||
dirs[i], dirs[j] = dirs[j], dirs[i]
|
dirs[i], dirs[j] = dirs[j], dirs[i]
|
||||||
}
|
}
|
||||||
return module.Version{}, "", &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods}
|
return module.Version{}, "", nil, &AmbiguousImportError{importPath: path, Dirs: dirs, Modules: mods}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sumErrMods) > 0 {
|
if len(sumErrMods) > 0 {
|
||||||
@ -377,7 +383,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
|
|||||||
j := len(sumErrMods) - 1 - i
|
j := len(sumErrMods) - 1 - i
|
||||||
sumErrMods[i], sumErrMods[j] = sumErrMods[j], sumErrMods[i]
|
sumErrMods[i], sumErrMods[j] = sumErrMods[j], sumErrMods[i]
|
||||||
}
|
}
|
||||||
return module.Version{}, "", &ImportMissingSumError{
|
return module.Version{}, "", nil, &ImportMissingSumError{
|
||||||
importPath: path,
|
importPath: path,
|
||||||
mods: sumErrMods,
|
mods: sumErrMods,
|
||||||
found: len(mods) > 0,
|
found: len(mods) > 0,
|
||||||
@ -385,7 +391,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(mods) == 1 {
|
if len(mods) == 1 {
|
||||||
return mods[0], dirs[0], nil
|
return mods[0], dirs[0], altMods, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if mg != nil {
|
if mg != nil {
|
||||||
@ -395,7 +401,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
|
|||||||
if !HasModRoot() {
|
if !HasModRoot() {
|
||||||
queryErr = ErrNoModRoot
|
queryErr = ErrNoModRoot
|
||||||
}
|
}
|
||||||
return module.Version{}, "", &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd}
|
return module.Version{}, "", nil, &ImportMissingError{Path: path, QueryErr: queryErr, isStd: pathIsStd}
|
||||||
}
|
}
|
||||||
|
|
||||||
// So far we've checked the root dependencies.
|
// So far we've checked the root dependencies.
|
||||||
@ -406,7 +412,7 @@ func importFromModules(ctx context.Context, path string, rs *Requirements, mg *M
|
|||||||
// the module graph, so we can't return an ImportMissingError here — one
|
// the module graph, so we can't return an ImportMissingError here — one
|
||||||
// of the missing modules might actually contain the package in question,
|
// of the missing modules might actually contain the package in question,
|
||||||
// in which case we shouldn't go looking for it in some new dependency.
|
// in which case we shouldn't go looking for it in some new dependency.
|
||||||
return module.Version{}, "", err
|
return module.Version{}, "", nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -862,6 +862,7 @@ type loadPkg struct {
|
|||||||
imports []*loadPkg // packages imported by this one
|
imports []*loadPkg // packages imported by this one
|
||||||
testImports []string // test-only imports, saved for use by pkg.test.
|
testImports []string // test-only imports, saved for use by pkg.test.
|
||||||
inStd bool
|
inStd bool
|
||||||
|
altMods []module.Version // modules that could have contained the package but did not
|
||||||
|
|
||||||
// Populated by (*loader).pkgTest:
|
// Populated by (*loader).pkgTest:
|
||||||
testOnce sync.Once
|
testOnce sync.Once
|
||||||
@ -1184,8 +1185,7 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// updateRequirements ensures that ld.requirements is consistent with the
|
// updateRequirements ensures that ld.requirements is consistent with the
|
||||||
// information gained from ld.pkgs and includes the modules in add as roots at
|
// information gained from ld.pkgs.
|
||||||
// at least the given versions.
|
|
||||||
//
|
//
|
||||||
// In particular:
|
// In particular:
|
||||||
//
|
//
|
||||||
@ -1343,7 +1343,7 @@ func (ld *loader) updateRequirements(ctx context.Context) (changed bool, err err
|
|||||||
//
|
//
|
||||||
// In some sense, we can think of this as ‘upgraded the module providing
|
// In some sense, we can think of this as ‘upgraded the module providing
|
||||||
// pkg.path from "none" to a version higher than "none"’.
|
// pkg.path from "none" to a version higher than "none"’.
|
||||||
if _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil {
|
if _, _, _, err = importFromModules(ctx, pkg.path, rs, nil); err == nil {
|
||||||
changed = true
|
changed = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1554,7 +1554,7 @@ func (ld *loader) preloadRootModules(ctx context.Context, rootPkgs []string) (ch
|
|||||||
// If the main module is tidy and the package is in "all" — or if we're
|
// If the main module is tidy and the package is in "all" — or if we're
|
||||||
// lucky — we can identify all of its imports without actually loading the
|
// lucky — we can identify all of its imports without actually loading the
|
||||||
// full module graph.
|
// full module graph.
|
||||||
m, _, err := importFromModules(ctx, path, ld.requirements, nil)
|
m, _, _, err := importFromModules(ctx, path, ld.requirements, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var missing *ImportMissingError
|
var missing *ImportMissingError
|
||||||
if errors.As(err, &missing) && ld.ResolveMissingImports {
|
if errors.As(err, &missing) && ld.ResolveMissingImports {
|
||||||
@ -1659,7 +1659,7 @@ func (ld *loader) load(ctx context.Context, pkg *loadPkg) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pkg.mod, pkg.dir, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg)
|
pkg.mod, pkg.dir, pkg.altMods, pkg.err = importFromModules(ctx, pkg.path, ld.requirements, mg)
|
||||||
if pkg.dir == "" {
|
if pkg.dir == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1918,7 +1918,7 @@ func (ld *loader) checkTidyCompatibility(ctx context.Context, rs *Requirements)
|
|||||||
|
|
||||||
pkg := pkg
|
pkg := pkg
|
||||||
ld.work.Add(func() {
|
ld.work.Add(func() {
|
||||||
mod, _, err := importFromModules(ctx, pkg.path, rs, mg)
|
mod, _, _, err := importFromModules(ctx, pkg.path, rs, mg)
|
||||||
if mod != pkg.mod {
|
if mod != pkg.mod {
|
||||||
mismatches := <-mismatchMu
|
mismatches := <-mismatchMu
|
||||||
mismatches[pkg] = mismatch{mod: mod, err: err}
|
mismatches[pkg] = mismatch{mod: mod, err: err}
|
||||||
|
58
src/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt
vendored
Normal file
58
src/cmd/go/testdata/script/mod_tidy_downgrade_ambiguous.txt
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Verifies golang.org/issue/47738.
|
||||||
|
|
||||||
|
# In this test, the user has rewritten their imports to use rsc.io/quote/v3,
|
||||||
|
# but their go.mod still requires rsc.io/quote@v1.5.2, and they indirectly
|
||||||
|
# require rsc.io/quote@v1.5.1 but don't import anything from it.
|
||||||
|
go list -m -f '{{.Path}}@{{.Version}}{{if .Indirect}} indirect{{end}}' all
|
||||||
|
stdout '^rsc.io/quote@v1.5.2$'
|
||||||
|
! stdout 'rsc.io/quote/v3'
|
||||||
|
go list -e all
|
||||||
|
! stdout '^rsc.io/quote$'
|
||||||
|
|
||||||
|
# 'go mod tidy' should preserve the requirement on rsc.io/quote but mark it
|
||||||
|
# indirect. This prevents a downgrade to v1.5.1, which could introduce
|
||||||
|
# an ambiguity.
|
||||||
|
go mod tidy
|
||||||
|
go list -m -f '{{.Path}}@{{.Version}}{{if .Indirect}} indirect{{end}}' all
|
||||||
|
stdout '^rsc.io/quote@v1.5.2 indirect$'
|
||||||
|
stdout '^rsc.io/quote/v3@v3.0.0$'
|
||||||
|
|
||||||
|
-- go.mod --
|
||||||
|
module use
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
old-indirect v0.0.0
|
||||||
|
rsc.io/quote v1.5.2
|
||||||
|
)
|
||||||
|
|
||||||
|
replace old-indirect v0.0.0 => ./old-indirect
|
||||||
|
-- go.sum --
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:pvCbr/wm8HzDD3fVywevekufpn6tCGPY3spdHeZJEsw=
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
rsc.io/quote v1.5.1/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
|
||||||
|
rsc.io/quote v1.5.2 h1:3fEykkD9k7lYzXqCYrwGAf7iNhbk4yCjHmKBN9td4L0=
|
||||||
|
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
|
||||||
|
rsc.io/sampler v1.3.0 h1:HLGR/BgEtI3r0uymSP/nl2uPLsUnNJX8toRyhfpBTII=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
-- use.go --
|
||||||
|
package use
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "old-indirect/empty"
|
||||||
|
|
||||||
|
_ "rsc.io/quote/v3"
|
||||||
|
)
|
||||||
|
-- old-indirect/empty/empty.go --
|
||||||
|
package empty
|
||||||
|
-- old-indirect/go.mod --
|
||||||
|
module old-indirect
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require rsc.io/quote v1.5.1
|
||||||
|
-- old-indirect/go.sum --
|
||||||
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
|
||||||
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
Loading…
Reference in New Issue
Block a user