Update module dev.gaijin.team/go/exhaustruct/v4 to v5 - #451
red-hat-konflux-kflux-prd-rh02[bot] wants to merge 1 commit into
Conversation
|
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: red-hat-konflux-kflux-prd-rh02[bot] The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
93cc004 to
546a92c
Compare
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configurationConfiguration used: Repository: openshift-online/coderabbit/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
9154f4a to
bf96f5f
Compare
a7728b4 to
8560e8d
Compare
da66770 to
afbfa8b
Compare
Signed-off-by: red-hat-konflux-kflux-prd-rh02 <190377777+red-hat-konflux-kflux-prd-rh02[bot]@users.noreply.github.com>
afbfa8b to
b5d1424
Compare
This PR contains the following updates:
v4.0.0→v5.2.0Release Notes
GaijinEntertainment/go-exhaustruct (dev.gaijin.team/go/exhaustruct/v4)
v5.2.0Compare Source
A minor release adding one option.
-allow-empty-blank-assignmentsexempts an empty struct literal the blank identifier receives, so a compile-time interface check such asvar _ Iface = T{}is no longer asked to fill fields nothing reads. The option is off by default; with it off, nothing changes.Added
-allow-empty-blank-assignments(#153).var _ Iface = T{}exists to prove thatTimplementsIface, and the analyzer required every field of a value that is discarded on the spot. The only allowance that reached the shape,-allow-empty-declarations, let every declared literal through with it. The new option keys on what receives the value: the blank identifier reads no field, so no field is required. It coversvar _ Iface = T{},var _ = T{}and_ = T{}, with&T{}and parentheses on either side, at package or function level.A value is matched to its name by position, so in the last line the literal bound to
sis still reported. A literal nested in the value (_ = []Server{{}}), a converted one (_ = Handler(Server{})) and a partial one stay reported: the blank identifier receives the slice, the conversion result, or a value with fields already named.The programmatic name is
Config.AllowEmptyBlankAssignments.Upgrading
CLI users reinstall and add the flag where they want it. Existing configuration is unchanged and no finding moves until the option is turned on.
golangci-lint users need a matching
allow-empty-blank-assignmentskey underexhaustruct_v5settings before they can turn it on. That key lives in the golangci-lint repository and is not part of this release.v5.1.0Compare Source
A release for Go 1.27 code. Literals that name promoted fields no longer crash the analyzer or get reported as incomplete,
-fixwrites source that compiles and keeps every field's optionality, and a set of resolution gaps that predate Go 1.27 are closed. The module now requires Go 1.26.Breaking changes
x/toolsv0.40.0 cannot read the export data Go 1.27 writes, so every run on that toolchain failed withinternal error: package "sync/atomic" without types. The v0.49.0 release that reads it needs go1.25; the floor goes to 1.26, which golangci-lint v2.13 already requires for this analyzer. Consumers on Go 1.24 or 1.25 cannot build this version.Fixes
makeslice: cap out of rangeon promoted keys (#168). Go 1.27 lets a composite literal name a promoted field in place of the embedded field carrying it (golang/go#77245). A literal naming more promoted fields than the struct has direct ones drove a capacity estimate negative and took golangci-lint down with it.Complete literals reported as incomplete (#161). Every promoted key counted as a key the struct did not have, so
A{b: "foo", a: 1, c: "f"}was reported as missing the embedded field it filled through. Promoted keys now resolve through the embedded field tree: a literal that names nothing under an embedded field is missing that field, one that fills it partly is missing the fields under it, each nameable from the same literal.The Go version of the file holding the literal decides whether promoted keys are possible, not the version the module declares, so a
//go:build go1.26file in a go1.27 module is read as the older one.Struct metadata cached by position. Export data drops the column and clamps lines past 64Ki to 1, so two types declared in one dependency file collapsed onto one cache entry and a literal was checked against another type's fields. The cache is keyed by the
go/typesobjects, which are unique per declaration, and each key is filled once across concurrently analysed packages.Pattern alternations that matched nothing. Go's regexp is leftmost-first, so
.*\.(Config|ConfigOption)committed toConfigagainstpkg.ConfigOption, stopped short of the end, and the pattern was silently ignored. Patterns now match leftmost-longest, compiled exactly as written.-fixoutput that did not compile or changed what is required. Migrating anexhaustruct:"optional"tag could comment out a single-line struct's closing brace, write a directive over one the author had already placed, or turn an enforced field optional. Tags are read and removed by the grammarreflect.StructTagreads, other entries in the tag survive, an interpreted-string tag migrates like a raw one, and the directive lands where the directive scanner resolves it to the field. Every deprecated tag is migrated, including one on a type the configuration excludes from checking.Directives in block comments and with trailing prose.
/*exhaustruct:optional*/now applies, which is the one form that annotates a field name inline, and prose after a directive is no longer reported as an unknown directive named"". A directive is matched to code by every line its comment covers, so a block comment reaches the code beside either end of it.Literal types that fell through. Map keys resolve against the key type rather than the value type,
[]PtrAlias{{...}}and[]PtrDefined{{...}}are checked, a literal of a type parameter is checked against the struct its constraint's terms share, and an alias to an anonymous struct carries its own directives.Directives above a statement reach the literals inside it, as they did in v4. A
//exhaustruct:ignoreabove a slice, map, call or return covers the literals nested in it, and redundant parentheses no longer end the walk.Diagnostics for a dependency's directives reach the package that owns the file, not the first importer to resolve a type from it.
Blank fields (
_) are never reported for a keyed literal, since no key can name them.Changed behaviour
A field no literal can write is required by nothing, whatever a directive or a pattern says about it: an unexported field of another package's struct, a promoted field a shallower one shadows, and a name two embedded fields promote at one depth.
A field marked
//exhaustruct:enforceunder an embedded field that the enclosing type left unrequired is still required. From Go 1.27 the enforced field itself is reported; below it the embedded field is, since that is the one key that reaches it.type P = *Configandtype Q *Configcarry their own type-level directives and patterns, and a literal eliding&Configunder one of them is reported as that type. A plain*Configstill answers asConfig.A field pattern (
Type#Field) is a rule for that field even when another pattern names the type holding it. Only a single pattern broad enough to match both names no field in particular.In either comment form the directive opens the comment, as
//go:builddoes:/* exhaustruct:optional */with a space is prose. A directive name written after a separator and a space, as in//exhaustruct:optional, enforce, is reported instead of being dropped in silence.Performance
Resolving a type opens each embedded struct once, at the depth it is first reached, where the walk previously cost one subtree per path through the embedding graph and twenty shared layers took 2.8 GB. A literal's keys are arranged once for the whole descent, instantiations of a generic type share their declaration's metadata, a constraint's interfaces are resolved once per walk, filled cache keys are answered under the read lock, and a file's directive scan builds only the lines a directive comment can share with code.
Internal
--issues-exit-code 0, so it could not fail. Lint now gates the build against a pinned golangci-lint, the suite runs under the race detector, and a dogfood job runs the analyzer built from the checkout over the checkout.IsAlias/IsDerivedresults nothing read, is gone.Upgrading
Toolchain 1.26 or newer, then reinstall. Configuration is unchanged. Findings move only where the analyzer was wrong before: promoted keys, blank fields, the newly resolved literal types, pattern alternations, use-site directive scope, and the enforced-under-optional case above.
v5.0.3Compare Source
A patch release removing spurious read errors reported for every standard library struct literal. No configuration, flag, or diagnostic changes otherwise.
Fixes
No more
$GOROOTread errors (#166). Every run reported one positionless diagnostic per standard library struct literal:$GOROOThere is not an unexpanded environment variable. It is a literal placeholder the compiler substitutes for the real GOROOT when recording positions of files in the Go distribution (cmd/internal/objabi.AbsFile). Those paths travel in export data, so any driver that type-checks dependencies from export data —singlechecker,go vet -vettool, golangci-lint — saw every standard library type positioned at a path that cannot be opened on any machine. Resolving a struct literal reads the file that declares its type in order to pick up directives, and for standard library types that read always failed.Files of the Go distribution are now resolved as directive-free without being read at all, since their sources carry no exhaustruct directives.
Standard library types are still checked. Fields come from
go/types, which this change does not touch — only directive resolution is skipped:A run over a package using standard library types produces an identical set of findings before and after, minus the read errors.
Internal
$GOROOTpositions thatanalysistestcannot produce (it type-checks dependencies from source, where the same definitions have real, readable paths).GOROOTof/cannot match every path.Upgrading
CLI users reinstall. There is nothing to change in configuration — the only difference is the absence of the spurious diagnostics.
v5.0.2Compare Source
A cleanup release that separates the analyzer's two construction lifecycles and trims internal surface. CLI behavior, flags, and diagnostics are unchanged. The only externally visible change is to the programmatic constructor API, which has no consumers on the
dev.gaijin.team/go/exhaustruct/v5module path yet — hence a patch bump rather than a major one.Breaking (programmatic API)
NewAnalyzeris split into two constructors (#160). A singleNewAnalyzer(config Config) (*analysis.Analyzer, error)served two conflicting lifecycles — CLI drivers that mutateConfigthrough flags after construction, and library callers that pass a finishedConfigand expect validation up front. It is now:NewAnalyzer() *analysis.Analyzer— flag-driven, for CLI drivers (singlechecker,go vet -vettool). Configuration is consumed on the first run, after the driver has parsed flags.NewAnalyzerWithConfig(config Config) (*analysis.Analyzer, error)— programmatic, for library consumers such as golangci-lint. TheConfigis copied and its patterns validated immediately; the analyzer exposes no flags, and later mutations of the passedConfighave no effect.Changes
Configpattern fields (EnforcePatterns,IgnorePatterns,OptionalPatterns,AllowEmptyPatterns) are now the namedPatternstype.[]string{...}literals remain assignable, so existing field assignments compile unchanged.Internal
Visitors decoupled from the analyzer struct; the
enginewrapper struct removed; lazy initialization confined to the flag-driven path; pattern validation unified underinternal/pattern; dead methods and an unusedflag.Valueimplementation dropped. No behavior change.Upgrading
CLI users reinstall; flags and output are unchanged. Library callers rename
NewAnalyzer(cfg)toNewAnalyzerWithConfig(cfg).v5.0.1Compare Source
Two v5 API bugs traced to the same root cause:
NewAnalyzercaptured patterns into the processor before the analysis driver parsed flags, so flag-driven values never reached analysis, and theConfigfields had to be typed againstinternal/patternto be writable at all.Fixes
-enforce-rx,-ignore-rx,-optional-rx, and-allow-empty-rxhad no effect under the standalone CLI (#155). Processor construction now happens lazily on first analyzer run, after flags are populated. golangci-lint integrations were unaffected, since they populateConfigprogrammatically before callingNewAnalyzer.analyzer.Configno longer leaks the internalpattern.Listtype (#158). The four pattern fields (EnforcePatterns,IgnorePatterns,OptionalPatterns,AllowEmptyPatterns) are now plain[]string; pattern compilation has moved inside the analyzer. Library callers populating these fields directly drop thepattern.NewListcall:Upgrading
CLI users reinstall. Library callers update
Configfield assignments as shown above.v5.0.0Compare Source
exhaustructv5 reshapes how the linter is configured. Struct tags give way to comment directives, opt-in checking lands as a first-class mode, and patterns now reach down to individual fields. The full reference is in the README; this is the highlight reel.What's new
Comment directives replace struct tags. Three directives —
enforce,ignore,optional— now work at three levels of granularity: type definitions, struct literals, and individual fields. A documented priority order keeps the interaction between them predictable.Explicit mode (
-explicit) flips the default. Instead of checking every struct literal in the project, the linter only checks types you've explicitly marked — either with//exhaustruct:enforceor via-enforce-rx. The right pick for large codebases where exhaustruct should be a precision tool, not a blanket policy.Field-level regex patterns. The pattern flags now accept
Type#Fieldsyntax, so a single configuration line can express optionality across a codebase without touching the type definitions:Optional patterns (
-optional-rx). Regex-based optionality for whole types or specific fields. Closes a long-standing gap for third-party types you can't annotate directly.Derived types and aliases now follow a clearer rule. Field-level directives carry through
type T = Uandtype T U— so a field marked optional stays optional on every alias and derived type. Type-level directives stay attached to the original type, which means you can promote a derived type to "enforced" independently.Better diagnostics.
-report-full-type-pathreportsnet/http.Cookieinstead ofhttp.Cookie, removing ambiguity when import aliases are in play (#146).-debug-cache-metricsprints per-package cache statistics, useful for tuning patterns on large projects.Auto-migration.
exhaustruct -fix ./...rewrites the legacyexhaustruct:"optional"tags to comment directives in one pass — no manual sweep required.Breaking changes
exhaustruct:"optional"is no longer recognized. The-fixflag migrates existing tags automatically; the manual equivalent is a one-line move from the field's struct tag to a//exhaustruct:optionalcomment above (or beside) the field.include/excludevocabulary suggested filtering, but the linter has always been about enforcement. The new names say what they do:-include-rx/-i→-enforce-rx-exclude-rx/-e→-ignore-rx/v5. Update imports and reinstall:Fixes
&T{}returned where the function signature expects*T) are now flagged — previously they slipped past the checker (#147).types.Unaliasso their fields are inspected even behind an alias chain (#149).Migrating from v4
/v4to/v5.exhaustruct -fix ./...to rewrite struct tags to comment directives.-include-rx/-iwith-enforce-rx, and-exclude-rx/-ewith-ignore-rxin CI configs and.golangci.yaml.-explicitif you'd rather opt specific types into checking than opt the rest out.Configuration
📅 Schedule: (in timezone UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
To execute skipped test pipelines write comment
/ok-to-test.Documentation
Find out how to configure dependency updates in MintMaker documentation or see all available configuration options in Renovate documentation.