Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decode: don't crash on embedded nil pointers #808

Merged
merged 1 commit into from Aug 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 23 additions & 4 deletions unmarshaler.go
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand Down
15 changes: 15 additions & 0 deletions unmarshaler_test.go
Expand Up @@ -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
Expand Down