Skip to content

Commit 180f71e

Browse files
authored
fix: indirection through nil pointer to embedded struct (#211)
The provided test `TestUnmarshallToEmbeddedNoData` shows that the decoder panics when `setDefaults` encounters a nil pointer to an embedded struct. The fix replaces `FieldByName` with `FieldByIndexErr` to catch this kind of situation and continue with the next field.
1 parent a377fd6 commit 180f71e

File tree

2 files changed

+81
-4
lines changed

2 files changed

+81
-4
lines changed

decoder.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ func (d *Decoder) setDefaults(t reflect.Type, v reflect.Value) MultiError {
104104

105105
errs := MultiError{}
106106

107+
if v.Type().Kind() == reflect.Struct {
108+
for i := 0; i < v.NumField(); i++ {
109+
field := v.Field(i)
110+
if field.Type().Kind() == reflect.Ptr && field.IsNil() && v.Type().Field(i).Anonymous {
111+
field.Set(reflect.New(field.Type().Elem()))
112+
}
113+
}
114+
}
115+
107116
for _, f := range struc.fields {
108117
vCurrent := v.FieldByName(f.name)
109118

@@ -121,15 +130,15 @@ func (d *Decoder) setDefaults(t reflect.Type, v reflect.Value) MultiError {
121130
} else if f.typ.Kind() == reflect.Slice {
122131
vals := strings.Split(f.defaultValue, "|")
123132

124-
//check if slice has one of the supported types for defaults
133+
// check if slice has one of the supported types for defaults
125134
if _, ok := builtinConverters[f.typ.Elem().Kind()]; !ok {
126135
errs.merge(MultiError{"default-" + f.name: errors.New("default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers or slices")})
127136
continue
128137
}
129138

130139
defaultSlice := reflect.MakeSlice(f.typ, 0, cap(vals))
131140
for _, val := range vals {
132-
//this check is to handle if the wrong value is provided
141+
// this check is to handle if the wrong value is provided
133142
convertedVal := builtinConverters[f.typ.Elem().Kind()](val)
134143
if !convertedVal.IsValid() {
135144
errs.merge(MultiError{"default-" + f.name: fmt.Errorf("failed setting default: %s is not compatible with field %s type", val, f.name)})
@@ -145,12 +154,12 @@ func (d *Decoder) setDefaults(t reflect.Type, v reflect.Value) MultiError {
145154
errs.merge(MultiError{"default-" + f.name: errors.New("default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers or slices")})
146155
}
147156

148-
//this check is to handle if the wrong value is provided
157+
// this check is to handle if the wrong value is provided
149158
if convertedVal := convertPointer(t1.Kind(), f.defaultValue); convertedVal.IsValid() {
150159
vCurrent.Set(convertedVal)
151160
}
152161
} else {
153-
//this check is to handle if the wrong value is provided
162+
// this check is to handle if the wrong value is provided
154163
if convertedVal := builtinConverters[f.typ.Kind()](f.defaultValue); convertedVal.IsValid() {
155164
vCurrent.Set(builtinConverters[f.typ.Kind()](f.defaultValue))
156165
}

decoder_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2057,6 +2057,74 @@ func TestUnmashalPointerToEmbedded(t *testing.T) {
20572057
}
20582058
}
20592059

2060+
type S24 struct {
2061+
F1 string `schema:"F1"`
2062+
}
2063+
2064+
type S24e struct {
2065+
*S24
2066+
F2 string `schema:"F2"`
2067+
}
2068+
2069+
func TestUnmarshallToEmbeddedNoData(t *testing.T) {
2070+
data := map[string][]string{
2071+
"F3": {"raw a"},
2072+
}
2073+
2074+
s := &S24e{}
2075+
2076+
decoder := NewDecoder()
2077+
err := decoder.Decode(s, data);
2078+
2079+
expectedErr := `schema: invalid path "F3"`
2080+
if err.Error() != expectedErr {
2081+
t.Fatalf("got %q, want %q", err, expectedErr)
2082+
}
2083+
}
2084+
type S25ee struct {
2085+
F3 string `schema:"F3"`
2086+
}
2087+
2088+
type S25e struct {
2089+
S25ee
2090+
F2 string `schema:"F2"`
2091+
}
2092+
2093+
type S25 struct {
2094+
S25e
2095+
F1 string `schema:"F1"`
2096+
}
2097+
2098+
func TestDoubleEmbedded(t *testing.T){
2099+
data := map[string][]string{
2100+
"F1": {"raw a"},
2101+
"F2": {"raw b"},
2102+
"F3": {"raw c"},
2103+
}
2104+
2105+
2106+
s := S25{}
2107+
decoder := NewDecoder()
2108+
2109+
if err := decoder.Decode(&s, data); err != nil {
2110+
t.Fatal("Error while decoding:", err)
2111+
}
2112+
2113+
expected := S25{
2114+
F1: "raw a",
2115+
S25e: S25e{
2116+
F2: "raw b",
2117+
S25ee: S25ee{
2118+
F3: "raw c",
2119+
},
2120+
},
2121+
}
2122+
if !reflect.DeepEqual(expected, s) {
2123+
t.Errorf("Expected %v errors, got %v", expected, s)
2124+
}
2125+
2126+
}
2127+
20602128
func TestDefaultValuesAreSet(t *testing.T) {
20612129
type N struct {
20622130
S1 string `schema:"s1,default:test1"`

0 commit comments

Comments
 (0)