diff --git a/unmarshaler.go b/unmarshaler.go index 6f196e4..2c526e9 100644 --- a/unmarshaler.go +++ b/unmarshaler.go @@ -123,7 +123,7 @@ type decoder struct { stashedExpr bool // Skip expressions until a table is found. This is set to true when a - // table could not be create (missing field in map), so all KV expressions + // table could not be created (missing field in map), so all KV expressions // need to be skipped. skipUntilTable bool @@ -483,7 +483,7 @@ func (d *decoder) handleKeyPart(key ast.Iterator, v reflect.Value, nextFn handle d.errorContext.Struct = t d.errorContext.Field = path - f := v.FieldByIndex(path) + f := fieldByIndex(v, path) x, err := nextFn(key, f) if err != nil || d.skipUntilTable { return reflect.Value{}, err @@ -1071,7 +1071,7 @@ func (d *decoder) handleKeyValuePart(key ast.Iterator, value *ast.Node, v reflec d.errorContext.Struct = t d.errorContext.Field = path - f := v.FieldByIndex(path) + f := fieldByIndex(v, path) x, err := d.handleKeyValueInner(key, value, f) if err != nil { return reflect.Value{}, err @@ -1135,6 +1135,21 @@ func initAndDereferencePointer(v reflect.Value) reflect.Value { return elem } +// Same as reflect.Value.FieldByIndex, but creates pointers if needed. +func fieldByIndex(v reflect.Value, path []int) reflect.Value { + for i, x := range path { + v = v.Field(x) + + if i < len(path)-1 && v.Kind() == reflect.Pointer { + if v.IsNil() { + v.Set(reflect.New(v.Type().Elem())) + } + v = v.Elem() + } + } + return v +} + type fieldPathsMap = map[string][]int var globalFieldPathsCache atomic.Value // map[danger.TypeID]fieldPathsMap @@ -1192,7 +1207,11 @@ func forEachField(t reflect.Type, path []int, do func(name string, path []int)) } if f.Anonymous && name == "" { - forEachField(f.Type, fieldPath, do) + t2 := f.Type + if t2.Kind() == reflect.Pointer { + t2 = t2.Elem() + } + forEachField(t2, fieldPath, do) continue } diff --git a/unmarshaler_test.go b/unmarshaler_test.go index def6434..43a334e 100644 --- a/unmarshaler_test.go +++ b/unmarshaler_test.go @@ -2451,6 +2451,21 @@ answer = 42 require.Error(t, err) } +func TestIssue807(t *testing.T) { + type A struct { + Name string `toml:"name"` + } + + type M struct { + *A + } + + var m M + err := toml.Unmarshal([]byte(`name = 'foo'`), &m) + require.NoError(t, err) + require.Equal(t, "foo", m.Name) +} + func TestUnmarshalDecodeErrors(t *testing.T) { examples := []struct { desc string