From fa12c02e81bdff1e9c11ee8a70b938b8c02da6b6 Mon Sep 17 00:00:00 2001 From: Alex Flint Date: Mon, 24 May 2021 21:45:11 -0700 Subject: [PATCH] recurse into unexported embedded structs --- parse.go | 13 ++++++++++--- parse_test.go | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/parse.go b/parse.go index 13e8195..d76ef0f 100644 --- a/parse.go +++ b/parse.go @@ -257,17 +257,24 @@ func cmdFromStruct(name string, dest path, t reflect.Type) (*command, error) { var errs []string walkFields(t, func(field reflect.StructField, t reflect.Type) bool { - // Check for the ignore switch in the tag + // check for the ignore switch in the tag tag := field.Tag.Get("arg") - if tag == "-" || !isExported(field.Name) { + if tag == "-" { return false } - // If this is an embedded struct then recurse into its fields + // if this is an embedded struct then recurse into its fields, even if + // it is unexported, because exported fields on unexported embedded + // structs are still writable if field.Anonymous && field.Type.Kind() == reflect.Struct { return true } + // ignore any other unexported field + if !isExported(field.Name) { + return false + } + // duplicate the entire path to avoid slice overwrites subdest := dest.Child(field) spec := spec{ diff --git a/parse_test.go b/parse_test.go index 695b1d8..e78b4d4 100644 --- a/parse_test.go +++ b/parse_test.go @@ -1095,6 +1095,29 @@ func TestEmbeddedWithDuplicateField2(t *testing.T) { assert.Equal(t, "", args.U.A) } +func TestUnexportedEmbedded(t *testing.T) { + type embeddedArgs struct { + Foo string + } + var args struct { + embeddedArgs + } + err := parse("--foo bar", &args) + require.NoError(t, err) + assert.Equal(t, "bar", args.Foo) +} + +func TestIgnoredEmbedded(t *testing.T) { + type embeddedArgs struct { + Foo string + } + var args struct { + embeddedArgs `arg:"-"` + } + err := parse("--foo bar", &args) + require.Error(t, err) +} + func TestEmptyArgs(t *testing.T) { origArgs := os.Args