diff --git a/internal/tracker/seen.go b/internal/tracker/seen.go index 4c2153e..167790d 100644 --- a/internal/tracker/seen.go +++ b/internal/tracker/seen.go @@ -145,6 +145,11 @@ func (s *SeenTracker) checkTable(node *ast.Node) error { if idx < 0 { idx = s.create(parentIdx, k, tableKind, false) + } else { + entry := s.entries[idx] + if entry.kind == valueKind { + return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind) + } } parentIdx = idx } @@ -186,7 +191,13 @@ func (s *SeenTracker) checkArrayTable(node *ast.Node) error { if idx < 0 { idx = s.create(parentIdx, k, tableKind, false) + } else { + entry := s.entries[idx] + if entry.kind == valueKind { + return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind) + } } + parentIdx = idx } @@ -281,6 +292,14 @@ func (s *SeenTracker) checkInlineTable(parentIdx int, node *ast.Node) error { return err } } + + // As inline tables are self-contained, the tracker does not + // need to retain the details of what they contain. The + // keyValue element that creates the inline table is kept to + // mark the presence of the inline table and prevent + // redefinition of its keys: check* functions cannot walk into + // a value. + s.clear(parentIdx) return nil } diff --git a/unmarshaler_test.go b/unmarshaler_test.go index 39915e7..5339b6a 100644 --- a/unmarshaler_test.go +++ b/unmarshaler_test.go @@ -2270,7 +2270,34 @@ Program = "hugo" require.Equal(t, expected, p) } -//nolint:funlen +func TestIssue701(t *testing.T) { + // Expected behavior: + // Return an error since a cannot be modified. From the TOML spec: + // + // > Inline tables are fully self-contained and define all + // keys and sub-tables within them. Keys and sub-tables cannot + // be added outside the braces. + + docs := []string{ + ` +a={} +[a.b] +z=0 +`, + ` +a={} +[[a.b]] +z=0 +`, + } + + for _, doc := range docs { + var v interface{} + err := toml.Unmarshal([]byte(doc), &v) + assert.Error(t, err) + } +} + func TestUnmarshalDecodeErrors(t *testing.T) { examples := []struct { desc string