diff --git a/ci.sh b/ci.sh index 05c76f29..9ae8b753 100755 --- a/ci.sh +++ b/ci.sh @@ -79,6 +79,7 @@ cover() { go test -covermode=atomic -coverpkg=./... -coverprofile=coverage.out.tmp ./... cat coverage.out.tmp | grep -v fuzz | grep -v testsuite | grep -v tomltestgen | grep -v gotoml-test-decoder > coverage.out go tool cover -func=coverage.out + echo "Coverage profile for ${branch}: ${dir}/coverage.out" >&2 popd if [ "${branch}" != "HEAD" ]; then diff --git a/unmarshaler.go b/unmarshaler.go index 39350343..5cede081 100644 --- a/unmarshaler.go +++ b/unmarshaler.go @@ -1170,10 +1170,10 @@ func initAndDereferencePointer(v reflect.Value) reflect.Value { // Same as reflect.Value.FieldByIndex, but creates pointers if needed. func fieldByIndex(v reflect.Value, path []int) reflect.Value { - for i, x := range path { + for _, x := range path { v = v.Field(x) - if i < len(path)-1 && v.Kind() == reflect.Ptr { + if v.Kind() == reflect.Ptr { if v.IsNil() { v.Set(reflect.New(v.Type().Elem())) } diff --git a/unmarshaler_test.go b/unmarshaler_test.go index 021ab1b5..4db58f7c 100644 --- a/unmarshaler_test.go +++ b/unmarshaler_test.go @@ -1277,6 +1277,64 @@ B = "data"`, } }, }, + { + desc: "array table into maps with pointer on last key", + input: `[[foo]] + bar = "hello"`, + gen: func() test { + type doc struct { + Foo **[]interface{} + } + x := &[]interface{}{ + map[string]interface{}{ + "bar": "hello", + }, + } + return test{ + target: &doc{}, + expected: &doc{ + Foo: &x, + }, + } + }, + }, + { + desc: "array table into maps with pointer on intermediate key", + input: `[[foo.foo2]] + bar = "hello"`, + gen: func() test { + type doc struct { + Foo **map[string]interface{} + } + x := &map[string]interface{}{ + "foo2": []interface{}{ + map[string]interface{}{ + "bar": "hello", + }, + }, + } + return test{ + target: &doc{}, + expected: &doc{ + Foo: &x, + }, + } + }, + }, + { + desc: "array table into maps with pointer on last key with invalid leaf type", + input: `[[foo]] + bar = "hello"`, + gen: func() test { + type doc struct { + Foo **[]map[string]int + } + return test{ + target: &doc{}, + err: true, + } + }, + }, { desc: "unexported struct fields are ignored", input: `foo = "bar"`, @@ -3511,3 +3569,47 @@ func TestUnmarshalEmbedNonString(t *testing.T) { require.NoError(t, err) require.Nil(t, d.Foo) } + +func TestUnmarshal_Nil(t *testing.T) { + type Foo struct { + Foo *Foo `toml:"foo,omitempty"` + Bar *Foo `toml:"bar,omitempty"` + } + + examples := []struct { + desc string + input string + expected string + err bool + }{ + { + desc: "empty", + input: ``, + expected: ``, + }, + { + desc: "simplest", + input: ` + [foo] + [foo.foo] + `, + expected: "[foo]\n[foo.foo]\n", + }, + } + + for _, ex := range examples { + e := ex + t.Run(e.desc, func(t *testing.T) { + foo := Foo{} + err := toml.Unmarshal([]byte(e.input), &foo) + if e.err { + require.Error(t, err) + } else { + require.NoError(t, err) + j, err := toml.Marshal(foo) + require.NoError(t, err) + assert.Equal(t, e.expected, string(j)) + } + }) + } +}