Repository: urfave/cli
Pull request #1517 (2022-10-08 14:48:53)

Revert "Remove all flag interfaces"
This reverts commit 268cb973f8f7b1f38999f43122fb3e39b90b0522.

<!--
  This template provides some ideas of things to include in your PR description.
  To start, try providing a short summary of your changes in the Title above.
  If a section of the PR template does not apply to this PR, then delete that section.
 -->

## What type of PR is this?

_(REQUIRED)_

<!--
  Delete any of the following that do not apply:
 -->

- cleanup
- feature

## What this PR does / why we need it:

_(REQUIRED)_

<!--
  What goal is this change working towards?
  Provide a bullet pointed summary of how each file was changed.
  Briefly explain any decisions you made with respect to the changes.
  Include anything here that you didn't include in *Release Notes*
  above, such as changes to CI or changes to internal methods.
-->

## Which issue(s) this PR fixes:

_(REQUIRED)_

<!--
If this PR fixes one of more issues, list them here.
One line each, like so:

Fixes #123
Fixes #39
-->

Reverts earlier PR

## Special notes for your reviewer:

_(fill-in or delete this section)_

<!--
   Is there any particular feedback you would / wouldn't like?
   Which parts of the code should reviewers focus on?
-->

## Testing

_(fill-in or delete this section)_

<!--
  Describe how you tested this change.
-->

## Release Notes

_(REQUIRED)_
<!--
  If this PR makes user facing changes, please describe them here. This
  description will be copied into the release notes/changelog, whenever the
  next version is released. Keep this section short, and focus on high level
  changes.

  Put your text between the block. To omit notes, use NONE within the block.
-->

```release-note

```



Patch:
diff --git a/app.go b/app.go
index 7be6f76..9f72f1b 100644
--- a/app.go
+++ b/app.go
@@ -228,7 +228,9 @@ func (a *App) Setup() {
 
 	a.flagCategories = newFlagCategories()
 	for _, fl := range a.Flags {
-		a.flagCategories.AddFlag(fl.GetCategory(), fl)
+		if cf, ok := fl.(CategorizableFlag); ok {
+			a.flagCategories.AddFlag(cf.GetCategory(), cf)
+		}
 	}
 
 	if a.Metadata == nil {
@@ -662,8 +664,10 @@ func runFlagActions(c *Context, fs []Flag) error {
 			}
 		}
 		if isSet {
-			if err := f.RunAction(c); err != nil {
-				return err
+			if af, ok := f.(ActionableFlag); ok {
+				if err := af.RunAction(c); err != nil {
+					return err
+				}
 			}
 		}
 	}
diff --git a/category.go b/category.go
index 188937d..8bf325e 100644
--- a/category.go
+++ b/category.go
@@ -101,7 +101,9 @@ func newFlagCategories() FlagCategories {
 func newFlagCategoriesFromFlags(fs []Flag) FlagCategories {
 	fc := newFlagCategories()
 	for _, fl := range fs {
-		fc.AddFlag(fl.GetCategory(), fl)
+		if cf, ok := fl.(CategorizableFlag); ok {
+			fc.AddFlag(cf.GetCategory(), cf)
+		}
 	}
 
 	return fc
@@ -136,7 +138,7 @@ type VisibleFlagCategory interface {
 	// Name returns the category name string
 	Name() string
 	// Flags returns a slice of VisibleFlag sorted by name
-	Flags() []Flag
+	Flags() []VisibleFlag
 }
 
 type defaultVisibleFlagCategory struct {
@@ -148,19 +150,21 @@ func (fc *defaultVisibleFlagCategory) Name() string {
 	return fc.name
 }
 
-func (fc *defaultVisibleFlagCategory) Flags() []Flag {
+func (fc *defaultVisibleFlagCategory) Flags() []VisibleFlag {
 	vfNames := []string{}
 	for flName, fl := range fc.m {
-		if fl.IsVisible() {
-			vfNames = append(vfNames, flName)
+		if vf, ok := fl.(VisibleFlag); ok {
+			if vf.IsVisible() {
+				vfNames = append(vfNames, flName)
+			}
 		}
 	}
 
 	sort.Strings(vfNames)
 
-	ret := make([]Flag, len(vfNames))
+	ret := make([]VisibleFlag, len(vfNames))
 	for i, flName := range vfNames {
-		ret[i] = fc.m[flName]
+		ret[i] = fc.m[flName].(VisibleFlag)
 	}
 
 	return ret
diff --git a/command.go b/command.go
index decfb73..de78faa 100644
--- a/command.go
+++ b/command.go
@@ -300,7 +300,9 @@ func (c *Command) VisibleFlagCategories() []VisibleFlagCategory {
 	if c.flagCategories == nil {
 		c.flagCategories = newFlagCategories()
 		for _, fl := range c.Flags {
-			c.flagCategories.AddFlag(fl.GetCategory(), fl)
+			if cf, ok := fl.(CategorizableFlag); ok {
+				c.flagCategories.AddFlag(cf.GetCategory(), fl)
+			}
 		}
 	}
 	return c.flagCategories.VisibleCategories()
diff --git a/context.go b/context.go
index 81416da..315c2fb 100644
--- a/context.go
+++ b/context.go
@@ -180,7 +180,7 @@ func (cCtx *Context) lookupFlagSet(name string) *flag.FlagSet {
 func (cCtx *Context) checkRequiredFlags(flags []Flag) requiredFlagsErr {
 	var missingFlags []string
 	for _, f := range flags {
-		if f.IsRequired() {
+		if rf, ok := f.(RequiredFlag); ok && rf.IsRequired() {
 			var flagPresent bool
 			var flagName string
 
diff --git a/docs.go b/docs.go
index 5a8be0a..8b1c9c8 100644
--- a/docs.go
+++ b/docs.go
@@ -116,7 +116,11 @@ func prepareFlags(
 	addDetails bool,
 ) []string {
 	args := []string{}
-	for _, flag := range flags {
+	for _, f := range flags {
+		flag, ok := f.(DocGenerationFlag)
+		if !ok {
+			continue
+		}
 		modifiedArg := opener
 
 		for _, s := range flag.Names() {
@@ -147,7 +151,7 @@ func prepareFlags(
 }
 
 // flagDetails returns a string containing the flags metadata
-func flagDetails(flag Flag) string {
+func flagDetails(flag DocGenerationFlag) string {
 	description := flag.GetUsage()
 	value := flag.GetValue()
 	if value != "" {
diff --git a/fish.go b/fish.go
index 70946e1..eec3253 100644
--- a/fish.go
+++ b/fish.go
@@ -114,7 +114,12 @@ func (a *App) prepareFishCommands(commands []*Command, allCommands *[]string, pr
 
 func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string {
 	completions := []string{}
-	for _, flag := range flags {
+	for _, f := range flags {
+		flag, ok := f.(DocGenerationFlag)
+		if !ok {
+			continue
+		}
+
 		completion := &strings.Builder{}
 		completion.WriteString(fmt.Sprintf(
 			"complete -c %s -n '%s'",
@@ -122,7 +127,7 @@ func (a *App) prepareFishFlags(flags []Flag, previousCommands []string) []string
 			a.fishSubcommandHelper(previousCommands),
 		))
 
-		fishAddFileFlag(flag, completion)
+		fishAddFileFlag(f, completion)
 
 		for idx, opt := range flag.Names() {
 			if idx == 0 {
diff --git a/flag.go b/flag.go
index aade1de..68d882c 100644
--- a/flag.go
+++ b/flag.go
@@ -83,6 +83,12 @@ func (f FlagsByName) Swap(i, j int) {
 	f[i], f[j] = f[j], f[i]
 }
 
+// ActionableFlag is an interface that wraps Flag interface and RunAction operation.
+type ActionableFlag interface {
+	Flag
+	RunAction(*Context) error
+}
+
 // Flag is a common interface related to parsing flags in cli.
 // For more advanced flag parsing techniques, it is recommended that
 // this interface be implemented.
@@ -97,33 +103,36 @@ type Flag interface {
 
 	// Whether the flag has been set or not
 	IsSet() bool
+}
+
+// RequiredFlag is an interface that allows us to mark flags as required
+// it allows flags required flags to be backwards compatible with the Flag interface
+type RequiredFlag interface {
+	Flag
 
 	// whether the flag is a required flag or not
 	IsRequired() bool
+}
 
-	// IsVisible returns true if the flag is not hidden, otherwise false
-	IsVisible() bool
-
-	// Returns the category of the flag
-	GetCategory() string
-
-	// GetUsage returns the usage string for the flag
-	GetUsage() string
-
-	// GetEnvVars returns the env vars for this flag
-	GetEnvVars() []string
+// DocGenerationFlag is an interface that allows documentation generation for the flag
+type DocGenerationFlag interface {
+	Flag
 
 	// TakesValue returns true if the flag takes a value, otherwise false
 	TakesValue() bool
 
-	// GetDefaultText returns the default text for this flag
-	GetDefaultText() string
+	// GetUsage returns the usage string for the flag
+	GetUsage() string
 
 	// GetValue returns the flags value as string representation and an empty
 	// string if the flag takes no value at all.
 	GetValue() string
 
-	RunAction(*Context) error
+	// GetDefaultText returns the default text for this flag
+	GetDefaultText() string
+
+	// GetEnvVars returns the env vars for this flag
+	GetEnvVars() []string
 }
 
 // Countable is an interface to enable detection of flag values which support
@@ -132,6 +141,23 @@ type Countable interface {
 	Count() int
 }
 
+// VisibleFlag is an interface that allows to check if a flag is visible
+type VisibleFlag interface {
+	Flag
+
+	// IsVisible returns true if the flag is not hidden, otherwise false
+	IsVisible() bool
+}
+
+// CategorizableFlag is an interface that allows us to potentially
+// use a flag in a categorized representation.
+type CategorizableFlag interface {
+	VisibleFlag
+
+	// Returns the category of the flag
+	GetCategory() string
+}
+
 func flagSet(name string, flags []Flag) (*flag.FlagSet, error) {
 	set := flag.NewFlagSet(name, flag.ContinueOnError)
 
@@ -189,7 +215,7 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
 func visibleFlags(fl []Flag) []Flag {
 	var visible []Flag
 	for _, f := range fl {
-		if f.IsVisible() {
+		if vf, ok := f.(VisibleFlag); ok && vf.IsVisible() {
 			visible = append(visible, f)
 		}
 	}
@@ -285,8 +311,14 @@ func formatDefault(format string) string {
 }
 
 func stringifyFlag(f Flag) string {
-	placeholder, usage := unquoteUsage(f.GetUsage())
-	needsPlaceholder := f.TakesValue()
+	// enforce DocGeneration interface on flags to avoid reflection
+	df, ok := f.(DocGenerationFlag)
+	if !ok {
+		return ""
+	}
+
+	placeholder, usage := unquoteUsage(df.GetUsage())
+	needsPlaceholder := df.TakesValue()
 
 	if needsPlaceholder && placeholder == "" {
 		placeholder = defaultPlaceholder
@@ -294,14 +326,14 @@ func stringifyFlag(f Flag) string {
 
 	defaultValueString := ""
 
-	if s := f.GetDefaultText(); s != "" {
+	if s := df.GetDefaultText(); s != "" {
 		defaultValueString = fmt.Sprintf(formatDefault("%s"), s)
 	}
 
 	usageWithDefault := strings.TrimSpace(usage + defaultValueString)
 
-	return withEnvHint(f.GetEnvVars(),
-		fmt.Sprintf("%s\t%s", prefixedNames(f.Names(), placeholder), usageWithDefault))
+	return withEnvHint(df.GetEnvVars(),
+		fmt.Sprintf("%s\t%s", prefixedNames(df.Names(), placeholder), usageWithDefault))
 }
 
 func stringifySliceFlag(usage string, names, defaultVals []string) string {
diff --git a/godoc-current.txt b/godoc-current.txt
index 2c8b5e0..03b76f8 100644
--- a/godoc-current.txt
+++ b/godoc-current.txt
@@ -247,6 +247,13 @@ TYPES
 type ActionFunc func(*Context) error
     ActionFunc is the action to execute when no subcommands are specified
 
+type ActionableFlag interface {
+	Flag
+	RunAction(*Context) error
+}
+    ActionableFlag is an interface that wraps Flag interface and RunAction
+    operation.
+
 type AfterFunc func(*Context) error
     AfterFunc is an action to execute after any subcommands are run, but after
     the subcommand has finished it is run even if Action() panics
@@ -499,6 +506,15 @@ func (f *BoolFlag) String() string
 func (f *BoolFlag) TakesValue() bool
     TakesValue returns true if the flag takes a value, otherwise false
 
+type CategorizableFlag interface {
+	VisibleFlag
+
+	// Returns the category of the flag
+	GetCategory() string
+}
+    CategorizableFlag is an interface that allows us to potentially use a flag
+    in a categorized representation.
+
 type Command struct {
 	// The name of the command
 	Name string
@@ -716,6 +732,28 @@ type Countable interface {
     Countable is an interface to enable detection of flag values which support
     repetitive flags
 
+type DocGenerationFlag interface {
+	Flag
+
+	// TakesValue returns true if the flag takes a value, otherwise false
+	TakesValue() bool
+
+	// GetUsage returns the usage string for the flag
+	GetUsage() string
+
+	// GetValue returns the flags value as string representation and an empty
+	// string if the flag takes no value at all.
+	GetValue() string
+
+	// GetDefaultText returns the default text for this flag
+	GetDefaultText() string
+
+	// GetEnvVars returns the env vars for this flag
+	GetEnvVars() []string
+}
+    DocGenerationFlag is an interface that allows documentation generation for
+    the flag
+
 type DurationFlag struct {
 	Name string
 
@@ -823,33 +861,6 @@ type Flag interface {
 
 	// Whether the flag has been set or not
 	IsSet() bool
-
-	// whether the flag is a required flag or not
-	IsRequired() bool
-
-	// IsVisible returns true if the flag is not hidden, otherwise false
-	IsVisible() bool
-
-	// Returns the category of the flag
-	GetCategory() string
-
-	// GetUsage returns the usage string for the flag
-	GetUsage() string
-
-	// GetEnvVars returns the env vars for this flag
-	GetEnvVars() []string
-
-	// TakesValue returns true if the flag takes a value, otherwise false
-	TakesValue() bool
-
-	// GetDefaultText returns the default text for this flag
-	GetDefaultText() string
-
-	// GetValue returns the flags value as string representation and an empty
-	// string if the flag takes no value at all.
-	GetValue() string
-
-	RunAction(*Context) error
 }
     Flag is a common interface related to parsing flags in cli. For more
     advanced flag parsing techniques, it is recommended that this interface be
@@ -1586,6 +1597,16 @@ func (f *PathFlag) String() string
 func (f *PathFlag) TakesValue() bool
     TakesValue returns true if the flag takes a value, otherwise false
 
+type RequiredFlag interface {
+	Flag
+
+	// whether the flag is a required flag or not
+	IsRequired() bool
+}
+    RequiredFlag is an interface that allows us to mark flags as required
+    it allows flags required flags to be backwards compatible with the Flag
+    interface
+
 type Serializer interface {
 	Serialize() string
 }
@@ -1623,8 +1644,6 @@ func (x *SliceFlag[T, S, E]) IsVisible() bool
 
 func (x *SliceFlag[T, S, E]) Names() []string
 
-func (x *SliceFlag[T, S, E]) RunAction(c *Context) error
-
 func (x *SliceFlag[T, S, E]) SetDestination(slice S)
 
 func (x *SliceFlag[T, S, E]) SetValue(slice S)
@@ -1635,6 +1654,10 @@ func (x *SliceFlag[T, S, E]) TakesValue() bool
 
 type SliceFlagTarget[E any] interface {
 	Flag
+	RequiredFlag
+	DocGenerationFlag
+	VisibleFlag
+	CategorizableFlag
 
 	// SetValue should propagate the given slice to the target, ideally as a new value.
 	// Note that a nil slice should nil/clear any existing value (modelled as ~[]E).
@@ -2229,11 +2252,19 @@ func (f *UintSliceFlag) String() string
 func (f *UintSliceFlag) TakesValue() bool
     TakesValue returns true of the flag takes a value, otherwise false
 
+type VisibleFlag interface {
+	Flag
+
+	// IsVisible returns true if the flag is not hidden, otherwise false
+	IsVisible() bool
+}
+    VisibleFlag is an interface that allows to check if a flag is visible
+
 type VisibleFlagCategory interface {
 	// Name returns the category name string
 	Name() string
 	// Flags returns a slice of VisibleFlag sorted by name
-	Flags() []Flag
+	Flags() []VisibleFlag
 }
     VisibleFlagCategory is a category containing flags.
 
diff --git a/sliceflag.go b/sliceflag.go
index c8670cb..b2ca590 100644
--- a/sliceflag.go
+++ b/sliceflag.go
@@ -21,6 +21,10 @@ type (
 	// update).
 	SliceFlagTarget[E any] interface {
 		Flag
+		RequiredFlag
+		DocGenerationFlag
+		VisibleFlag
+		CategorizableFlag
 
 		// SetValue should propagate the given slice to the target, ideally as a new value.
 		// Note that a nil slice should nil/clear any existing value (modelled as ~[]E).
@@ -110,18 +114,17 @@ func (x *SliceFlag[T, S, E]) GetDestination() S {
 	return nil
 }
 
-func (x *SliceFlag[T, S, E]) String() string             { return x.Target.String() }
-func (x *SliceFlag[T, S, E]) Names() []string            { return x.Target.Names() }
-func (x *SliceFlag[T, S, E]) IsSet() bool                { return x.Target.IsSet() }
-func (x *SliceFlag[T, S, E]) IsRequired() bool           { return x.Target.IsRequired() }
-func (x *SliceFlag[T, S, E]) TakesValue() bool           { return x.Target.TakesValue() }
-func (x *SliceFlag[T, S, E]) GetUsage() string           { return x.Target.GetUsage() }
-func (x *SliceFlag[T, S, E]) GetValue() string           { return x.Target.GetValue() }
-func (x *SliceFlag[T, S, E]) GetDefaultText() string     { return x.Target.GetDefaultText() }
-func (x *SliceFlag[T, S, E]) GetEnvVars() []string       { return x.Target.GetEnvVars() }
-func (x *SliceFlag[T, S, E]) IsVisible() bool            { return x.Target.IsVisible() }
-func (x *SliceFlag[T, S, E]) GetCategory() string        { return x.Target.GetCategory() }
-func (x *SliceFlag[T, S, E]) RunAction(c *Context) error { return x.Target.RunAction(c) }
+func (x *SliceFlag[T, S, E]) String() string         { return x.Target.String() }
+func (x *SliceFlag[T, S, E]) Names() []string        { return x.Target.Names() }
+func (x *SliceFlag[T, S, E]) IsSet() bool            { return x.Target.IsSet() }
+func (x *SliceFlag[T, S, E]) IsRequired() bool       { return x.Target.IsRequired() }
+func (x *SliceFlag[T, S, E]) TakesValue() bool       { return x.Target.TakesValue() }
+func (x *SliceFlag[T, S, E]) GetUsage() string       { return x.Target.GetUsage() }
+func (x *SliceFlag[T, S, E]) GetValue() string       { return x.Target.GetValue() }
+func (x *SliceFlag[T, S, E]) GetDefaultText() string { return x.Target.GetDefaultText() }
+func (x *SliceFlag[T, S, E]) GetEnvVars() []string   { return x.Target.GetEnvVars() }
+func (x *SliceFlag[T, S, E]) IsVisible() bool        { return x.Target.IsVisible() }
+func (x *SliceFlag[T, S, E]) GetCategory() string    { return x.Target.GetCategory() }
 
 func (x *flagValueHook) Set(value string) error {
 	if err := x.value.Set(value); err != nil {


Test patch:
diff --git a/flag_test.go b/flag_test.go
index e7fa309..ddd3e67 100644
--- a/flag_test.go
+++ b/flag_test.go
@@ -223,7 +223,10 @@ func TestFlagsFromEnv(t *testing.T) {
 		defer resetEnv(os.Environ())
 		os.Clearenv()
 
-		f := test.flag
+		f, ok := test.flag.(DocGenerationFlag)
+		if !ok {
+			t.Errorf("flag %v needs to implement DocGenerationFlag to retrieve env vars", test.flag)
+		}
 		envVarSlice := f.GetEnvVars()
 		_ = os.Setenv(envVarSlice[0], test.input)
 
@@ -259,6 +262,12 @@ func TestFlagsFromEnv(t *testing.T) {
 	}
 }
 
+type nodocFlag struct {
+	Flag
+
+	Name string
+}
+
 func TestFlagStringifying(t *testing.T) {
 	for _, tc := range []struct {
 		name     string
@@ -435,6 +444,11 @@ func TestFlagStringifying(t *testing.T) {
 			fl:       &UintFlag{Name: "tubes", DefaultText: "13"},
 			expected: "--tubes value\t(default: 13)",
 		},
+		{
+			name:     "nodoc-flag",
+			fl:       &nodocFlag{Name: "scarecrow"},
+			expected: "",
+		},
 	} {
 		t.Run(tc.name, func(ct *testing.T) {
 			s := stringifyFlag(tc.fl)
diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt
index 2c8b5e0..03b76f8 100644
--- a/testdata/godoc-v2.x.txt
+++ b/testdata/godoc-v2.x.txt
@@ -247,6 +247,13 @@ TYPES
 type ActionFunc func(*Context) error
     ActionFunc is the action to execute when no subcommands are specified
 
+type ActionableFlag interface {
+	Flag
+	RunAction(*Context) error
+}
+    ActionableFlag is an interface that wraps Flag interface and RunAction
+    operation.
+
 type AfterFunc func(*Context) error
     AfterFunc is an action to execute after any subcommands are run, but after
     the subcommand has finished it is run even if Action() panics
@@ -499,6 +506,15 @@ func (f *BoolFlag) String() string
 func (f *BoolFlag) TakesValue() bool
     TakesValue returns true if the flag takes a value, otherwise false
 
+type CategorizableFlag interface {
+	VisibleFlag
+
+	// Returns the category of the flag
+	GetCategory() string
+}
+    CategorizableFlag is an interface that allows us to potentially use a flag
+    in a categorized representation.
+
 type Command struct {
 	// The name of the command
 	Name string
@@ -716,6 +732,28 @@ type Countable interface {
     Countable is an interface to enable detection of flag values which support
     repetitive flags
 
+type DocGenerationFlag interface {
+	Flag
+
+	// TakesValue returns true if the flag takes a value, otherwise false
+	TakesValue() bool
+
+	// GetUsage returns the usage string for the flag
+	GetUsage() string
+
+	// GetValue returns the flags value as string representation and an empty
+	// string if the flag takes no value at all.
+	GetValue() string
+
+	// GetDefaultText returns the default text for this flag
+	GetDefaultText() string
+
+	// GetEnvVars returns the env vars for this flag
+	GetEnvVars() []string
+}
+    DocGenerationFlag is an interface that allows documentation generation for
+    the flag
+
 type DurationFlag struct {
 	Name string
 
@@ -823,33 +861,6 @@ type Flag interface {
 
 	// Whether the flag has been set or not
 	IsSet() bool
-
-	// whether the flag is a required flag or not
-	IsRequired() bool
-
-	// IsVisible returns true if the flag is not hidden, otherwise false
-	IsVisible() bool
-
-	// Returns the category of the flag
-	GetCategory() string
-
-	// GetUsage returns the usage string for the flag
-	GetUsage() string
-
-	// GetEnvVars returns the env vars for this flag
-	GetEnvVars() []string
-
-	// TakesValue returns true if the flag takes a value, otherwise false
-	TakesValue() bool
-
-	// GetDefaultText returns the default text for this flag
-	GetDefaultText() string
-
-	// GetValue returns the flags value as string representation and an empty
-	// string if the flag takes no value at all.
-	GetValue() string
-
-	RunAction(*Context) error
 }
     Flag is a common interface related to parsing flags in cli. For more
     advanced flag parsing techniques, it is recommended that this interface be
@@ -1586,6 +1597,16 @@ func (f *PathFlag) String() string
 func (f *PathFlag) TakesValue() bool
     TakesValue returns true if the flag takes a value, otherwise false
 
+type RequiredFlag interface {
+	Flag
+
+	// whether the flag is a required flag or not
+	IsRequired() bool
+}
+    RequiredFlag is an interface that allows us to mark flags as required
+    it allows flags required flags to be backwards compatible with the Flag
+    interface
+
 type Serializer interface {
 	Serialize() string
 }
@@ -1623,8 +1644,6 @@ func (x *SliceFlag[T, S, E]) IsVisible() bool
 
 func (x *SliceFlag[T, S, E]) Names() []string
 
-func (x *SliceFlag[T, S, E]) RunAction(c *Context) error
-
 func (x *SliceFlag[T, S, E]) SetDestination(slice S)
 
 func (x *SliceFlag[T, S, E]) SetValue(slice S)
@@ -1635,6 +1654,10 @@ func (x *SliceFlag[T, S, E]) TakesValue() bool
 
 type SliceFlagTarget[E any] interface {
 	Flag
+	RequiredFlag
+	DocGenerationFlag
+	VisibleFlag
+	CategorizableFlag
 
 	// SetValue should propagate the given slice to the target, ideally as a new value.
 	// Note that a nil slice should nil/clear any existing value (modelled as ~[]E).
@@ -2229,11 +2252,19 @@ func (f *UintSliceFlag) String() string
 func (f *UintSliceFlag) TakesValue() bool
     TakesValue returns true of the flag takes a value, otherwise false
 
+type VisibleFlag interface {
+	Flag
+
+	// IsVisible returns true if the flag is not hidden, otherwise false
+	IsVisible() bool
+}
+    VisibleFlag is an interface that allows to check if a flag is visible
+
 type VisibleFlagCategory interface {
 	// Name returns the category name string
 	Name() string
 	// Flags returns a slice of VisibleFlag sorted by name
-	Flags() []Flag
+	Flags() []VisibleFlag
 }
     VisibleFlagCategory is a category containing flags.
 
diff --git a/zz_generated.flags_test.go b/zz_generated.flags_test.go
index be6dadf..6862afc 100644
--- a/zz_generated.flags_test.go
+++ b/zz_generated.flags_test.go
@@ -17,13 +17,13 @@ func TestFloat64SliceFlag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestFloat64SliceFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.Float64SliceFlag{}
+	var f cli.RequiredFlag = &cli.Float64SliceFlag{}
 
 	_ = f.IsRequired()
 }
 
 func TestFloat64SliceFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.Float64SliceFlag{}
+	var f cli.VisibleFlag = &cli.Float64SliceFlag{}
 
 	_ = f.IsVisible()
 }
@@ -36,13 +36,13 @@ func TestGenericFlag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestGenericFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.GenericFlag{}
+	var f cli.RequiredFlag = &cli.GenericFlag{}
 
 	_ = f.IsRequired()
 }
 
 func TestGenericFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.GenericFlag{}
+	var f cli.VisibleFlag = &cli.GenericFlag{}
 
 	_ = f.IsVisible()
 }
@@ -61,13 +61,13 @@ func TestInt64SliceFlag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestInt64SliceFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.Int64SliceFlag{}
+	var f cli.RequiredFlag = &cli.Int64SliceFlag{}
 
 	_ = f.IsRequired()
 }
 
 func TestInt64SliceFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.Int64SliceFlag{}
+	var f cli.VisibleFlag = &cli.Int64SliceFlag{}
 
 	_ = f.IsVisible()
 }
@@ -80,13 +80,13 @@ func TestIntSliceFlag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestIntSliceFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.IntSliceFlag{}
+	var f cli.RequiredFlag = &cli.IntSliceFlag{}
 
 	_ = f.IsRequired()
 }
 
 func TestIntSliceFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.IntSliceFlag{}
+	var f cli.VisibleFlag = &cli.IntSliceFlag{}
 
 	_ = f.IsVisible()
 }
@@ -99,13 +99,13 @@ func TestPathFlag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestPathFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.PathFlag{}
+	var f cli.RequiredFlag = &cli.PathFlag{}
 
 	_ = f.IsRequired()
 }
 
 func TestPathFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.PathFlag{}
+	var f cli.VisibleFlag = &cli.PathFlag{}
 
 	_ = f.IsVisible()
 }
@@ -124,13 +124,13 @@ func TestStringSliceFlag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestStringSliceFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.StringSliceFlag{}
+	var f cli.RequiredFlag = &cli.StringSliceFlag{}
 
 	_ = f.IsRequired()
 }
 
 func TestStringSliceFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.StringSliceFlag{}
+	var f cli.VisibleFlag = &cli.StringSliceFlag{}
 
 	_ = f.IsVisible()
 }
@@ -143,13 +143,13 @@ func TestTimestampFlag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestTimestampFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.TimestampFlag{}
+	var f cli.RequiredFlag = &cli.TimestampFlag{}
 
 	_ = f.IsRequired()
 }
 
 func TestTimestampFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.TimestampFlag{}
+	var f cli.VisibleFlag = &cli.TimestampFlag{}
 
 	_ = f.IsVisible()
 }
@@ -182,13 +182,13 @@ func TestBoolFlag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestBoolFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.BoolFlag{}
+	var f cli.RequiredFlag = &cli.BoolFlag{}
 
 	_ = f.IsRequired()
 }
 
 func TestBoolFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.BoolFlag{}
+	var f cli.VisibleFlag = &cli.BoolFlag{}
 
 	_ = f.IsVisible()
 }
@@ -207,13 +207,13 @@ func TestFloat64Flag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestFloat64Flag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.Float64Flag{}
+	var f cli.RequiredFlag = &cli.Float64Flag{}
 
 	_ = f.IsRequired()
 }
 
 func TestFloat64Flag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.Float64Flag{}
+	var f cli.VisibleFlag = &cli.Float64Flag{}
 
 	_ = f.IsVisible()
 }
@@ -232,13 +232,13 @@ func TestIntFlag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestIntFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.IntFlag{}
+	var f cli.RequiredFlag = &cli.IntFlag{}
 
 	_ = f.IsRequired()
 }
 
 func TestIntFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.IntFlag{}
+	var f cli.VisibleFlag = &cli.IntFlag{}
 
 	_ = f.IsVisible()
 }
@@ -257,13 +257,13 @@ func TestInt64Flag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestInt64Flag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.Int64Flag{}
+	var f cli.RequiredFlag = &cli.Int64Flag{}
 
 	_ = f.IsRequired()
 }
 
 func TestInt64Flag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.Int64Flag{}
+	var f cli.VisibleFlag = &cli.Int64Flag{}
 
 	_ = f.IsVisible()
 }
@@ -282,13 +282,13 @@ func TestStringFlag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestStringFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.StringFlag{}
+	var f cli.RequiredFlag = &cli.StringFlag{}
 
 	_ = f.IsRequired()
 }
 
 func TestStringFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.StringFlag{}
+	var f cli.VisibleFlag = &cli.StringFlag{}
 
 	_ = f.IsVisible()
 }
@@ -307,13 +307,13 @@ func TestDurationFlag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestDurationFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.DurationFlag{}
+	var f cli.RequiredFlag = &cli.DurationFlag{}
 
 	_ = f.IsRequired()
 }
 
 func TestDurationFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.DurationFlag{}
+	var f cli.VisibleFlag = &cli.DurationFlag{}
 
 	_ = f.IsVisible()
 }
@@ -332,13 +332,13 @@ func TestUintFlag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestUintFlag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.UintFlag{}
+	var f cli.RequiredFlag = &cli.UintFlag{}
 
 	_ = f.IsRequired()
 }
 
 func TestUintFlag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.UintFlag{}
+	var f cli.VisibleFlag = &cli.UintFlag{}
 
 	_ = f.IsVisible()
 }
@@ -357,13 +357,13 @@ func TestUint64Flag_SatisfiesFlagInterface(t *testing.T) {
 }
 
 func TestUint64Flag_SatisfiesRequiredFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.Uint64Flag{}
+	var f cli.RequiredFlag = &cli.Uint64Flag{}
 
 	_ = f.IsRequired()
 }
 
 func TestUint64Flag_SatisfiesVisibleFlagInterface(t *testing.T) {
-	var f cli.Flag = &cli.Uint64Flag{}
+	var f cli.VisibleFlag = &cli.Uint64Flag{}
 
 	_ = f.IsVisible()
 }