-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Welcome
- Yes, I'm using a binary release within 2 latest releases. Only such installations are supported.
- Yes, I've searched similar issues on GitHub and didn't find any.
- Yes, I've read the
typecheck
section of the FAQ. - Yes, I've tried with the standalone linter if available (e.g., gocritic, go vet, etc.).
- I agree to follow this project's Code of Conduct
Description of the problem
The exhaustruct
linter has false positives ever since golangci-lint
upgraded to Go 1.23 in the v1.65.0 release. That release replaced the go 1.22
directive in go.mod
with go 1.23
, causing the linter's behaviour to change.
I used git bisect
to identify PR #5224 as the one that introduced the change. Further investigation confirmed that changing the go 1.22
directive to go 1.23
is what causes the change in behaviour.
The issue is that in golangci-lint
v1.63.x, the exhaustruct
linter reported missing fields in structs where the type was a type alias. In v1.64.x, the linter no longer reports missing fields in structs where the type is an alias. See below for an example of code that reports an error with v1.64 but not with v1.65.
The exhaustruct
linter was not upgraded in the v1.64 release of golangci-lint
.
Running the linter directly does not suffer from the same issue, because that project's go.mod
contains the go 1.21
directive. The issue only occurs when the analyzer is imported as a library into a project with go 1.23
or go 1.24
in its go.mod
.
I want to be clear: I don't think the buggy code is in golangci-lint
. But the bug only appears in golangci-lint
. I've opened an issue on the linter's repository to fix this: GaijinEntertainment/go-exhaustruct#129. I am opening this issue here to track the problem, in case other analyzers are also affected.
Version of golangci-lint
Version with incorrect linter behaviour:
$ golangci-lint --version
golangci-lint has version 1.64.4 built with go1.24.0 from 04aec4f7 on 2025-02-12T21:58:21Z
Version with correct linter behaviour:
$ golangci-lint --version
golangci-lint has version 1.63.4 built with go1.23.4 from c1149695 on 2025-01-03T19:49:42Z
Configuration
$ golangci-lint run --enable-only=exhaustruct
Go environment
$ go version && go env
go version go1.24.0 darwin/arm64
AR='ar'
CC='cc'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='c++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/Users/arthur/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/arthur/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/80/tqlcnm9d5_jbbmvnx9zst7rr0000gn/T/go-build2107305147=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/Users/arthur/workspace/pigment/monorepo/go.mod'
GOMODCACHE='/Users/arthur/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/arthur/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/Cellar/go/1.24.0/libexec'
GOSUMDB='sum.golang.org'
GOTELEMETRY='on'
GOTELEMETRYDIR='/Users/arthur/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/opt/homebrew/Cellar/go/1.24.0/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.24.0'
GOWORK=''
PKG_CONFIG='pkg-config'
Verbose output of running
With golangci-lint
v1.64.4:
$ golangci-lint cache clean
$ golangci-lint run -v
INFO golangci-lint has version 1.64.4 built with go1.24.0 from 04aec4f7 on 2025-02-12T21:58:21Z
INFO [config_reader] Config search paths: [./ /Users/arthur/workspace/pigment/tmp /Users/arthur/workspace/pigment /Users/arthur/workspace /Users/arthur /Users /]
INFO [goenv] Read go env for 9.668583ms: map[string]string{"GOCACHE":"/Users/arthur/Library/Caches/go-build", "GOROOT":"/opt/homebrew/Cellar/go/1.24.0/libexec"}
INFO [lintersdb] Active 1 linters: [exhaustruct]
INFO [loader] Go packages loading at mode 8767 (compiled_files|deps|imports|name|exports_file|files|types_sizes) took 34.408083ms
INFO [runner/filename_unadjuster] Pre-built 0 adjustments in 420.959µs
INFO [linters_context/goanalysis] analyzers took 29.583µs with top 10 stages: inspect: 18.708µs, typecheck: 7.5µs, exhaustruct: 3.375µs
INFO [runner] processing took 1.167µs with stages: max_same_issues: 292ns, exclusion_paths: 166ns, exclusion_rules: 125ns, max_from_linter: 83ns, diff: 42ns, path_absoluter: 42ns, source_code: 42ns, fixer: 42ns, path_shortener: 42ns, severity-rules: 42ns, path_relativity: 42ns, invalid_issue: 42ns, sort_results: 42ns, generated_file_filter: 41ns, skip_dirs: 41ns, path_prettifier: 41ns, uniq_by_line: 0s, nolint_filter: 0s, identifier_marker: 0s, filename_unadjuster: 0s, cgo: 0s, skip_files: 0s, max_per_file_from_linter: 0s
INFO [runner] linters took 1.3655ms with stages: goanalysis_metalinter: 1.330625ms
INFO File cache stats: 0 entries of total size 0B
INFO Memory: 2 samples, avg is 32.6MB, max is 34.6MB
INFO Execution took 46.725458ms
With golangci-lint
v1.63.4:
$ golangci-lint cache clean
$ golangci-lint run -v --enable-only=exhaustruct
INFO golangci-lint has version 1.63.4 built with go1.23.4 from c1149695 on 2025-01-03T19:49:42Z
INFO [config_reader] Config search paths: [./ /Users/arthur/workspace/pigment/tmp /Users/arthur/workspace/pigment /Users/arthur/workspace /Users/arthur /Users /]
INFO [lintersdb] Active 1 linters: [exhaustruct]
INFO [loader] Go packages loading at mode 8767 (compiled_files|deps|imports|name|exports_file|files|types_sizes) took 32.116125ms
INFO [runner/filename_unadjuster] Pre-built 0 adjustments in 187.875µs
INFO [linters_context/goanalysis] analyzers took 64.043µs with top 10 stages: inspect: 35.834µs, exhaustruct: 20.792µs, typecheck: 7.417µs
INFO [runner] Processors filtering stat (in/out): path_prettifier: 1/1, nolint: 1/1, diff: 1/1, fixer: 1/1, sort_results: 1/1, cgo: 1/1, skip_dirs: 1/1, max_per_file_from_linter: 1/1, max_same_issues: 1/1, invalid_issue: 1/1, source_code: 1/1, path_shortener: 1/1, severity-rules: 1/1, path_prefixer: 1/1, autogenerated_exclude: 1/1, skip_files: 1/1, identifier_marker: 1/1, exclude: 1/1, exclude-rules: 1/1, uniq_by_line: 1/1, max_from_linter: 1/1, filename_unadjuster: 1/1
INFO [runner] processing took 348.159µs with stages: nolint: 95.792µs, source_code: 81.875µs, autogenerated_exclude: 70.999µs, identifier_marker: 56.208µs, path_prettifier: 21.917µs, exclude-rules: 11.875µs, skip_dirs: 4.083µs, uniq_by_line: 957ns, max_same_issues: 874ns, invalid_issue: 791ns, path_shortener: 625ns, cgo: 583ns, max_from_linter: 416ns, max_per_file_from_linter: 250ns, fixer: 208ns, filename_unadjuster: 207ns, exclude: 167ns, skip_files: 166ns, sort_results: 42ns, diff: 42ns, path_prefixer: 41ns, severity-rules: 41ns
INFO [runner] linters took 21.386792ms with stages: goanalysis_metalinter: 20.962125ms
foo.go:11:6: foo.Foo is missing field B (exhaustruct)
_ = FooAlias{
^
INFO File cache stats: 1 entries of total size 113B
INFO Memory: 2 samples, avg is 26.6MB, max is 26.6MB
INFO Execution took 66.264875ms
A minimal reproducible example or link to a public repository
package foo
type Foo struct {
A int
B int
}
type FooAlias = Foo
func MyFunc() {
_ = FooAlias{
A: 1,
}
}
Validation
- Yes, I've included all information above (version, config, etc.).
Supporter
- I am a sponsor/backer through GitHub or OpenCollective