From 021719d0cf63efc286160da178ee1c6c7603d80a Mon Sep 17 00:00:00 2001 From: "Brandon C. Irizarry" Date: Tue, 7 Jul 2026 21:28:47 -0400 Subject: [PATCH 1/2] test: add another flag to second mutex group This intentionally creates a failing test. We're trying to prove that, if we have mutually exclusive flags that look like: { { A, B }, { C, D } } It should be the case that any combination that draws from both sets should result in an error. However, currently this isn't the case; for example, the code considers the combination "--A --D" to be legal. --- flag_mutex_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/flag_mutex_test.go b/flag_mutex_test.go index 69ceec2328..b6c9a161c5 100644 --- a/flag_mutex_test.go +++ b/flag_mutex_test.go @@ -30,6 +30,9 @@ func newCommand() *Command { Aliases: []string{"ai"}, Sources: EnvVars("T_VAR"), }, + &BoolFlag{ + Name: "q", + }, }, }, }, @@ -73,6 +76,12 @@ func TestFlagMutuallyExclusiveFlags(t *testing.T) { errStr: "option i cannot be set along with option ai", required: true, }, + { + name: "required both set second member", + args: []string{"--i", "11", "--q"}, + errStr: "option i cannot be set along with option q", + required: true, + }, { name: "set env var", required: true, From 43b1bf1bcc0dea87cc4965945b3d05304a02646d Mon Sep 17 00:00:00 2001 From: "Brandon C. Irizarry" Date: Tue, 7 Jul 2026 21:58:51 -0400 Subject: [PATCH 2/2] refactor: use indexed for-loops to find mutex violations --- flag_mutex.go | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/flag_mutex.go b/flag_mutex.go index 247bcb569b..e11e975bc1 100644 --- a/flag_mutex.go +++ b/flag_mutex.go @@ -17,22 +17,38 @@ type MutuallyExclusiveFlags struct { } func (grp MutuallyExclusiveFlags) check(_ *Command) error { - oneSet := false e := &mutuallyExclusiveGroup{} - for _, grpf := range grp.Flags { - for _, f := range grpf { - if f.IsSet() { - if oneSet { - e.flag2Name = f.Names()[0] - return e - } - e.flag1Name = f.Names()[0] + // First, find the index of the group were the flag was set + // (if it exists.) + var i int + var oneSet bool + +flagGroupLoop: + for ; i < len(grp.Flags); i++ { + group := grp.Flags[i] + + // For each flag inside this group, check if it's set. + for _, flg := range group { + if flg.IsSet() { + e.flag1Name = flg.Names()[0] oneSet = true - break + break flagGroupLoop } - if oneSet { - break + } + } + + // Next, continue from the flag group just after the one we + // stopped at above, to see if another flag is set. If so, + // return an error. + i++ + for ; i < len(grp.Flags); i++ { + group := grp.Flags[i] + + for _, flg := range group { + if flg.IsSet() { + e.flag2Name = flg.Names()[0] + return e } } }