Skip to content

Let a field tag replace the signature's value, not add to it - #657

Open
youdie006 wants to merge 1 commit into
alecthomas:masterfrom
youdie006:field-tag-overlays-signature
Open

youdie006 wants to merge 1 commit into
alecthomas:masterfrom
youdie006:field-tag-overlays-signature

Conversation

@youdie006

Copy link
Copy Markdown

parseTag merges the field's struct tags over the ones Signature() supplied. The comment there says what it means to do:

// Next overlay the field's tags.
...
for key, value := range fieldItems {
    // Prepend field tag values
    items[key] = append(value, items[key]...)
}

Appending only looks like an overlay through Tag.Get, which returns values[0]. Seventeen of the twenty-two tags hydrateTag reads go through Get/GetBool/GetRune, so name, help, type, default, group, prefix, envprefix, enum, short and the rest all override correctly — that is what TestSignatureFieldTagOverrides pins, with its own comment saying "The signature name should NOT work because the field tag overrode it."

The other five go through Tag.GetAll, which returns the whole slice: env, xor, lastwins, and, set. For those the signature's value survives next to the field's.

Two things that come out of it, both driven through kong.New + Parse:

A field that overrides env is still resolved from the signature's variable.

signature: env:"SIG_ENV"      field: env:"FIELD_ENV"
before: value comes from $SIG_ENV; help renders ($FIELD_ENV, $SIG_ENV)
after:  $FIELD_ENV only

A field cannot move itself out of the signature's xor group. With A carrying xor:"mygroup" over a signature that says xor:"siggroup", and a B in siggroup, kong rejects a command line that should be legal:

before: --a and --b can't be used together
after:  <nil>

The change is items[key] = value.

Scope, stated plainly

This makes all five GetAll tags replace rather than accumulate. I wrote the test for env and hand-probed xor; lastwins, and and set change the same way by construction but I have not exercised them individually. set is a Vars map, so its visible change is the smallest. If you consider accumulation deliberate for any of these, I am happy to narrow the fix to the ones you want.

I also left parseStructTagItems (tag.go:185-187) alone, where bare tags meet the kong:"..." tag — both sides there belong to the same field, so the override question does not arise.

Testing

TestSignatureFieldTagOverridesEnv added to signature_test.go in the file's existing style.

  • On master it is the only failure in the package: -signatureEnvFlag("") / +signatureEnvFlag("from-signature").
  • Mutation-checked both directions, failing disjoint sets. Restoring the append fails only the new test. Over-correcting so that any field tag discards the whole signature fails five pre-existing tests (TestSignatureCommand, TestSignatureCommandHelp, TestSignaturePointerReceiver, TestSignaturePointerReceiverHelp, TestSignatureFieldTagOverridesHelp) while the new test passes — so the "help still comes from the signature" side is pinned too.
  • go test ./... and go test -race ./... pass, gofmt is clean, and golangci-lint at the pinned v1.64.5 exits 0. I checked the linter was actually running by planting an unused function, which it caught.
  • I did not run the Go 1.23/1.24 or windows-latest legs; the diff has no version- or platform-sensitive constructs.

git log -S puts the append at 95675de (signature defaults, #581), last touched by b73962b (#602) — nothing has pinned the accumulate behaviour since.


AI assistance disclosure: this patch was found and written with Claude Code. Every quoted output above is verbatim from running it against master and against this branch.

parseTag says it overlays the field's tags over the signature's, but merged
them with append. Tags read through Tag.Get see values[0] and override fine;
the five read through Tag.GetAll -- env, xor, lastwins, and, set -- kept the
signature's value alongside the field's.

So a field that overrode env was still resolved from the signature's variable,
and a field that moved itself to a different xor group stayed in both.

    env:"SIG_ENV" on the signature, env:"FIELD_ENV" on the field
    before: resolved from $SIG_ENV
    after:  resolved from $FIELD_ENV only
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-09T00:25:42.555832Z 2b20289 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2b202890f0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tag.go
// Prepend field tag values
items[key] = append(value, items[key]...)
// A key set on the field replaces the signature's values for that key, rather than adding to them.
items[key] = value

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve unrelated signature variables when overriding set

When a Signature() supplies multiple set defaults and the field supplies another set value, replacing the entire slice discards every signature variable, including keys the field did not override. For example, a signature containing set:"a=A" set:"b=B" help:"${a}/${b}/${c}" combined with a field tagged set:"c=C" now makes kong.New fail with undefined variable ${a}. Since set represents a multi-entry variable map, merge this key by variable name with field values winning rather than replacing the whole collection.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I measured this one and it does not reproduce — set behaves identically before and after the change.

Driving kong.New and reading Tag.Vars off the model, with a signature of set:"a=1" set:"b=2":

                                     master        this branch
signature only                       {a:1 b:2}     {a:1 b:2}
signature + field `set:"a=9"`        {a:9}         {a:9}
signature `set:"a=1"` + field `set:"b=9"`   {b:9}  {b:9}

So the signature's unrelated set variables are already dropped on master as soon as the field carries any set tag; this patch does not change that either way. If that existing behaviour is itself wrong it is a separate fix, and I am happy to open one — but it is not a regression introduced here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant