cmd/go: 奇怪的缺失导入属性

hujrc8aj  于 6个月前  发布在  Go
关注(0)|答案(3)|浏览(44)

Go version

go version go1.21.6 linux/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/austin/.cache/go-build'
GOENV='/home/austin/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/austin/r/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/austin/r/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/home/austin/sdk/go1.21.6'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/home/austin/sdk/go1.21.6/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.21.6'
GCCGO='/usr/bin/gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/tmp/m/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2463259172=/tmp/go-build -gno-record-gcc-switches'

What did you do?

go install golang.org/x/exp/cmd/txtar@latest
mkdir m
cd m
txtar -x <<EOF
-- go.mod --
module m
-- a/a.go --
package a

import _ "x"
-- b/b.go --
package b

import _ "x"
EOF
go list -deps -json ./a ./b

What did you see happen?

Here's the full output:

a/a.go:3:8: package x is not in std (/home/austin/sdk/go1.21.6/src/x)
{
	"ImportPath": "x",
	"DepOnly": true,
	"Incomplete": true,
	"Stale": true,
	"StaleReason": "build ID mismatch",
	"Error": {
		"ImportStack": [
			"m/a"
		],
		"Pos": "a/a.go:3:8",
		"Err": "package x is not in std (/home/austin/sdk/go1.21.6/src/x)"
	}
}
{
	"Dir": "/tmp/m/a",
	"ImportPath": "m/a",
	"Name": "a",
	"Root": "/tmp/m",
	"Module": {
		"Path": "m",
		"Main": true,
		"Dir": "/tmp/m",
		"GoMod": "/tmp/m/go.mod",
		"GoVersion": "1.16"
	},
	"Match": [
		"./a"
	],
	"Incomplete": true,
	"Stale": true,
	"StaleReason": "stale dependency: x",
	"GoFiles": [
		"a.go"
	],
	"Imports": [
		"x"
	],
	"Deps": [
		"x"
	],
	"DepsErrors": [
		{
			"ImportStack": [
				"m/a"
			],
			"Pos": "a/a.go:3:8",
			"Err": "package x is not in std (/home/austin/sdk/go1.21.6/src/x)"
		}
	]
}
{
	"Dir": "/tmp/m/b",
	"ImportPath": "m/b",
	"Name": "b",
	"Root": "/tmp/m",
	"Module": {
		"Path": "m",
		"Main": true,
		"Dir": "/tmp/m",
		"GoMod": "/tmp/m/go.mod",
		"GoVersion": "1.16"
	},
	"Match": [
		"./b"
	],
	"Incomplete": true,
	"Stale": true,
	"StaleReason": "stale dependency: x",
	"GoFiles": [
		"b.go"
	],
	"Imports": [
		"x"
	],
	"Deps": [
		"x"
	],
	"DepsErrors": [
		{
			"ImportStack": [
				"m/a"
			],
			"Pos": "a/a.go:3:8",
			"Err": "package x is not in std (/home/austin/sdk/go1.21.6/src/x)"
		}
	]
}

And just the bits I found surprising:

a/a.go:3:8: package x is not in std (/home/austin/sdk/go1.21.6/src/x)
{
	"ImportPath": "x",
	...
	"Error": {
		"ImportStack": [
			"m/a"
		],
		"Pos": "a/a.go:3:8",
		"Err": "package x is not in std (/home/austin/sdk/go1.21.6/src/x)"
	}
}
{
	"ImportPath": "m/a",
	...
	"DepsErrors": [
		{
			"ImportStack": [
				"m/a"
			],
			"Pos": "a/a.go:3:8",
			"Err": "package x is not in std (/home/austin/sdk/go1.21.6/src/x)"
		}
	]
}
{
	"ImportPath": "m/b",
	...
	"DepsErrors": [
		{
			"ImportStack": [
				"m/a"
			],
			"Pos": "a/a.go:3:8",
			"Err": "package x is not in std (/home/austin/sdk/go1.21.6/src/x)"
		}
	]
}

Notably:

  1. Only the error in a is reported in the text output, not the bad import in b.
  2. The errors are reported as DepsErrors on packages a and b, rather than just Errors
  3. The error directly attributed to "package" x reports a position in package a (of the import of x)
  4. We see the same error in package b, attributed to the import line in a, even though a and b have no relationship

What did you expect to see?

I expected go list to report errors on packages a and b attributed to the bad import statements. That is, one error on package a at a/a.go:3 and another on package b at b/b.go:3. Something like:

a/a.go:3:8: package x is not in std (/home/austin/sdk/go1.21.6/src/x)
b/b.go:3:8: package x is not in std (/home/austin/sdk/go1.21.6/src/x)
{
	"ImportPath": "m/a",
	...
	"Error": {
		"ImportStack": null,
		"Pos": "a/a.go:3:8",
		"Err": "package x is not in std (/home/austin/sdk/go1.21.6/src/x)"
	}
}
{
	"ImportPath": "m/b",
	...
	"Error": {
		"ImportStack": null,
		"Pos": "b/b.go:3:8",
		"Err": "package x is not in std (/home/austin/sdk/go1.21.6/src/x)"
	}
}

I'm not sure whether I expected it to report a package for "x" at all. If it did, that would presumably also need an error. The "x" package it currently reports is pretty weird because it's missing so many fields (like Dir). If it didn't report a package "x", it would probably have to omit "x" from the Imports and Deps lists, which I think would be fine.

ut6juiuv

ut6juiuv1#

我花了相当长的时间来弄清楚为什么会这样。显然,cmd/go将这个错误对象表示为一个附加到包x的单个错误对象,因此只能给它附加一行号(而"a"恰好赢了)。
modload 层, modload.(*loader).resolveMissingImports 调用 modload.queryImport("x") ,它返回一个 modload.ImportMissingError ,resolveMissingImports 将它保存到 x 的 modload.loadPkg.err 字段中。这个错误是最终变成珍珠的沙粒之一,报告在包 "x" 上,并作为 "a" 和 "b" 上的 deps 错误由 go list 报告。包 "x" 的 modload.loadPkg 最终存储在 modload.loaded.pkgCache 中。
load 层, load.PackagesAndErrors -> load.loadImport(..., "./a", ...) -> ... -> load.loadImport(..., "x", ...)。这调用 load.loadPackageData -> modload.Lookup(..., "x"),它从 load.loaded.pkgCache 获取 "x" 的 load.loadPkg 并返回 err 字段。 load.loadImport 将此错误传递给 load.(*Package).load -> load.setLoadPackageDataError,它通过 importPos 堆栈找到 "x" 的导入位置,并将 modload.ImportMissingErrorload.PackageError Package 。在这个例子中,由于我们首先通过 "a" 来到 "x",所以 setLoadPackageDataErrorload.PackageError.Pos 字段设置为 "a" 的导入,尽管 PackageError 本身附加到 x 的 load.Package 对象上。
后来,我们看到 load.loadImport(..., "./b", ...) -> ... -> load.loadImport(..., "x", ...),它在 load.Package 中找到 "x" 的 load.Package,因此不会进入 load.(*Package).load,所以 "a" 中的位仍然附加到 "x" 的 load.Package 上。
这一切都足够复杂,以至于我不确定应该在哪里更改错误如何附加到包。如果我们不想在 go list 输出中表示 "x",那么我认为 modload 层应该将 modload.ImportMissingError 附加到 "a" 和 "b" 的 modload.loadPkg 上,而不是为 "x" 创建一个完全独立的 loadPkg。然后我认为剩下的部分就会“正常工作”。

t2a7ltrp

t2a7ltrp2#

modload.(*loader).resolveMissingImports 之前,我错过了一个步骤。在 modload.loadFromRoots 期间,modload.(*loader).pkgmodload.(*loader).load 一起遍历导入图。最终,pkg 被调用来构造一个 "x" 的 loadPkg 并调用 loadload 调用 importFromModules,在该函数的末尾有一个 if mg != nil 检查,实际上构造并返回了 "x" 的 ImportMissingError。这是 resolveMissingImports 最终选择的 ImportMissingError

d4so4syb

d4so4syb3#

这可能是#26909的重复。

相关问题