fix(parser): reset dotted-key and array records per array-of-tables element
Assisted-by: GLM 5.3
This commit is contained in:
@@ -46,6 +46,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
check covered `[table]` headers and dotted keys but not the intermediate
|
||||
steps of an array-of-tables header, so such a document silently extended the
|
||||
inline table.
|
||||
- A new element of an array of tables starts a fresh scope for dotted-key paths
|
||||
and nested arrays of tables: `[[a]]`, `b.c = 1`, `[[a]]`, `[a.b]` parses, as
|
||||
the TOML examples in the spec shape it. The records of the previous element
|
||||
falsely rejected the same paths in the next one.
|
||||
- A float with an exponent marker but no digits (`1e`, `0.0E`) is rejected;
|
||||
the exponent requires at least one digit.
|
||||
- A date-time offset outside 00:00 through 23:59 is rejected; such offsets
|
||||
|
||||
@@ -498,6 +498,36 @@ func TestRejectsInlineTableExtension(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// A new element of an array of tables starts a fresh scope: sub-table headers,
|
||||
// nested arrays of tables, and dotted-key paths recorded for the previous
|
||||
// element must not block the same paths in the next one.
|
||||
func TestArrayOfTablesFreshScopePerElement(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"nested array of tables": "[[a]]\n[[a.b]]\nx = 1\n[[a]]\n[a.b]\ny = 2\n",
|
||||
"dotted key": "[[a]]\nb.c = 1\n[[a]]\n[a.b]\nd = 2\n",
|
||||
}
|
||||
for name, doc := range cases {
|
||||
tree, err := Parse([]byte(doc))
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", name, err)
|
||||
continue
|
||||
}
|
||||
elements := tree["a"].([]map[string]any)
|
||||
if len(elements) != 2 {
|
||||
t.Errorf("%s: len(a) = %d, want 2", name, len(elements))
|
||||
}
|
||||
}
|
||||
// Within one element the redefinition rules keep applying.
|
||||
for name, doc := range map[string]string{
|
||||
"header over dotted in one element": "[[a]]\nb.c = 1\n[a.b]\nd = 2\n",
|
||||
"table over nested array": "[[a]]\n[[a.b]]\n[a.b]\nx = 1\n",
|
||||
} {
|
||||
if _, err := Parse([]byte(doc)); err == nil {
|
||||
t.Errorf("%s: expected an error, got none", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRejectsSpecInvalid(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"single-digit hour": "a = 2023-10-01T1:32:00Z\n",
|
||||
|
||||
@@ -274,19 +274,18 @@ func (p *parser) freezeInline(path []string, val any) {
|
||||
}
|
||||
}
|
||||
|
||||
// resetScopeUnder forgets the header and freeze records nested under key, which
|
||||
// belong to the previous element of an array of tables.
|
||||
// resetScopeUnder forgets the definition records nested under key, which
|
||||
// belong to the previous element of an array of tables: headers, frozen
|
||||
// inline tables, dotted-key paths, and nested arrays of tables all start
|
||||
// fresh in the new element.
|
||||
func (p *parser) resetScopeUnder(key []string) {
|
||||
prefix := pathKey(key) + "\x00"
|
||||
for k := range p.headers {
|
||||
for _, m := range []map[string]bool{p.headers, p.frozen, p.dotted, p.arrays} {
|
||||
for k := range m {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
delete(p.headers, k)
|
||||
delete(m, k)
|
||||
}
|
||||
}
|
||||
for k := range p.frozen {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
delete(p.frozen, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user