diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go index 6df9c334fe1d91..4023e5d6d4d7e9 100644 --- a/src/cmd/go/internal/list/list.go +++ b/src/cmd/go/internal/list/list.go @@ -936,11 +936,23 @@ func collectDeps(p *load.Package) { func collectDepsErrors(p *load.Package) { depsErrors := make(map[*load.PackageError]bool) - for _, p := range p.Internal.Imports { - if p.Error != nil { - depsErrors[p.Error] = true + for _, dep := range p.Internal.Imports { + if dep.Error != nil { + // The cached Package for dep may be shared by multiple importers. + // Clone the error and, if Pos refers to the importer side, + // replace it with p's own import position (#78183). + depErr := new(dep.Error) + if depErr.IsImporterPos() && p.Internal.Build != nil { + if pos := p.Internal.Build.ImportPos[dep.ImportPath]; len(pos) > 0 { + p0 := pos[0] + p0.Filename = base.ShortPath(p0.Filename) + depErr.Pos = p0.String() + } + } + depsErrors[&depErr] = true } - for _, q := range p.DepsErrors { + + for _, q := range dep.DepsErrors { depsErrors[q] = true } } diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index e2a77d7d7df85b..3449d435b15c52 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -343,6 +343,7 @@ func (p *Package) setLoadPackageDataError(err error, path string, stk *ImportSta top, ok := stk.Top() if ok && path != top.Pkg { p.Error.setPos(importPos) + p.Error.setImporterPos(importPos) } } @@ -457,6 +458,7 @@ type PackageError struct { Err error // the error itself IsImportCycle bool // the error is an import cycle alwaysPrintStack bool // whether to always print the ImportStack + importerPos bool // Pos refers to the importer, not the imported package } func (p *PackageError) Error() string { @@ -509,6 +511,18 @@ func (p *PackageError) setPos(posList []token.Position) { pos := posList[0] pos.Filename = base.ShortPath(pos.Filename) p.Pos = pos.String() + p.importerPos = true +} + +func (p *PackageError) setImporterPos(posList []token.Position) { + p.setPos(posList) + p.importerPos = true +} + +// IsImporterPos reports whether Pos refers to the import statement +// in the importing package, not to the imported package's source. +func (p *PackageError) IsImporterPos() bool { + return p.importerPos } // ImportPathError is a type of error that prevents a package from being loaded @@ -781,17 +795,20 @@ func loadImport(loaderstate *modload.State, ctx context.Context, opts PackageOpt } p.Incomplete = true p.Error.setPos(importPos) + p.Error.setImporterPos(importPos) } } // Checked on every import because the rules depend on the code doing the importing. if perr := disallowInternal(loaderstate, ctx, srcDir, parent, parentPath, p, stk); perr != nil { perr.setPos(importPos) + perr.setImporterPos(importPos) return p, perr } if mode&ResolveImport != 0 { if perr := disallowVendor(srcDir, path, parentPath, p, stk); perr != nil { perr.setPos(importPos) + perr.setImporterPos(importPos) return p, perr } } @@ -802,6 +819,7 @@ func loadImport(loaderstate *modload.State, ctx context.Context, opts PackageOpt Err: ImportErrorf(path, "import %q is a program, not an importable package", path), } perr.setPos(importPos) + perr.setImporterPos(importPos) return p, perr } @@ -817,6 +835,7 @@ func loadImport(loaderstate *modload.State, ctx context.Context, opts PackageOpt Err: err, } perr.setPos(importPos) + perr.setImporterPos(importPos) return p, perr } @@ -1802,6 +1821,7 @@ func (p *Package) load(loaderstate *modload.State, ctx context.Context, opts Pac top, ok := stk.Top() if ok && path != top.Pkg && len(importPos) > 0 { p.Error.setPos(importPos) + p.Error.setImporterPos(importPos) } } } diff --git a/src/cmd/go/testdata/script/golist_missingimport.txt b/src/cmd/go/testdata/script/golist_missingimport.txt new file mode 100644 index 00000000000000..436ad0fc4c06bd --- /dev/null +++ b/src/cmd/go/testdata/script/golist_missingimport.txt @@ -0,0 +1,26 @@ +# Test that go list reports correct Pos for each import of a missing package. +# When two different packages import the same missing package, each should +# report the position of its own import statement, not reuse the first one. +# See issue #78183. + +env GOWORK=off + +go list -e -f '{{range .DepsErrors}}package {{$.ImportPath}}: error at {{.Pos}}: {{.Err}}{{end}}' ./... + +stdout 'package example.com/test/p: error at p/p.go:3:8: no required module' +stdout 'package example.com/test/q: error at q/q.go:3:8: no required module' + +-- go.mod -- +module example.com/test + +go 1.22 + +-- p/p.go -- +package p + +import _ "example.com/missing" + +-- q/q.go -- +package q + +import _ "example.com/missing" \ No newline at end of file