Repository: BurntSushi/toml (go)

Issue:
metadata.Keys (and thus metadata.Undecoded) is invalid in some cases
**Sample code:**
```
package main

import (
	"fmt"

	"github.com/BurntSushi/toml"
)

func main() {
	content := `[table.subtable.subsubtable]
a = 1
b = 2
`
	var v interface{}
	metadata, err := toml.Decode(content, &v)
	if err != nil {
		panic(err)
	}
	fmt.Printf("%#v\n", metadata.Keys())
}
```

Playground: https://go.dev/play/p/Rr5bm5DbEql

**Output:**
```
[]toml.Key{toml.Key{"table", "subtable", "subsubtable"}, toml.Key{"table", "subtable", "subsubtable", "b"}, toml.Key{"table", "subtable", "subsubtable", "b"}}
```

Notice how the second and third value in that slice both point to `b`.

**Expected output:**
```
[]toml.Key{toml.Key{"table", "subtable", "subsubtable"}, toml.Key{"table", "subtable", "subsubtable", "a"}, toml.Key{"table", "subtable", "subsubtable", "b"}}
```

The root cause seems to be somewhere near this [append call](https://github.com/BurntSushi/toml/blob/eb727477b3f7e4aa878635d39257753f0840811b/meta.go#L139). The result of `append` can return a slice backed by the same array if the capacity is available. See Go example code: https://go.dev/play/p/2FGKuCvCUKz.

Pull request:
Fix metadata - do not reuse result of append
[These lines](https://github.com/BurntSushi/toml/blob/eb727477b3f7e4aa878635d39257753f0840811b/meta.go#L138-L140) added in #400 caused the problem since the result of multiple `append` calls can return two slices backed by the same backing array. See demo here: https://go.dev/play/p/2FGKuCvCUKz. The bug only surfaces under specific conditions when the capacity of `parser.context` is increased by more than 1 due to [this append call](https://github.com/BurntSushi/toml/blob/eb727477b3f7e4aa878635d39257753f0840811b/parse.go#L606).

Fixes #417 

Patch:
diff --git a/meta.go b/meta.go
index e614537..0d33702 100644
--- a/meta.go
+++ b/meta.go
@@ -135,9 +135,6 @@ func (k Key) maybeQuoted(i int) string {
 
 // Like append(), but only increase the cap by 1.
 func (k Key) add(piece string) Key {
-	if cap(k) > len(k) {
-		return append(k, piece)
-	}
 	newKey := make(Key, len(k)+1)
 	copy(newKey, k)
 	newKey[len(k)] = piece


Test patch:
diff --git a/internal/toml-test/tests/valid/table/keyword-with-values.json b/internal/toml-test/tests/valid/table/keyword-with-values.json
new file mode 100644
index 0000000..4642aac
--- /dev/null
+++ b/internal/toml-test/tests/valid/table/keyword-with-values.json
@@ -0,0 +1,14 @@
+{
+    "false": {
+        "k": {"type": "integer", "value": "2"}
+    },
+    "inf": {
+        "k": {"type": "integer", "value": "3"}
+    },
+    "nan": {
+        "k": {"type": "integer", "value": "4"}
+    },
+    "true": {
+        "k": {"type": "integer", "value": "1"}
+    }
+}
diff --git a/internal/toml-test/tests/valid/table/keyword-with-values.toml b/internal/toml-test/tests/valid/table/keyword-with-values.toml
new file mode 100644
index 0000000..d154d60
--- /dev/null
+++ b/internal/toml-test/tests/valid/table/keyword-with-values.toml
@@ -0,0 +1,11 @@
+[true]
+k = 1
+
+[false]
+k = 2
+
+[inf]
+k = 3
+
+[nan]
+k = 4
diff --git a/internal/toml-test/tests/valid/table/names-with-values.json b/internal/toml-test/tests/valid/table/names-with-values.json
new file mode 100644
index 0000000..7892371
--- /dev/null
+++ b/internal/toml-test/tests/valid/table/names-with-values.json
@@ -0,0 +1,46 @@
+{
+    "a": {
+        " x ": {
+            "key": {"type": "integer", "value": "4"}
+        },
+        "b": {
+            "c": {
+                "key": {"type": "integer", "value": "1"}
+            }
+        },
+        "b.c": {
+            "key": {"type": "integer", "value": "2"}
+        },
+        "d.e": {
+            "key": {"type": "integer", "value": "3"}
+        }
+    },
+    "d": {
+        "e": {
+            "f": {
+                "key": {"type": "integer", "value": "5"}
+            }
+        }
+    },
+    "g": {
+        "h": {
+            "i": {
+                "key": {"type": "integer", "value": "6"}
+            }
+        }
+    },
+    "j": {
+        "ʞ": {
+            "l": {
+                "key": {"type": "integer", "value": "7"}
+            }
+        }
+    },
+    "x": {
+        "1": {
+            "2": {
+                "key": {"type": "integer", "value": "8"}
+            }
+        }
+    }
+}
diff --git a/internal/toml-test/tests/valid/table/names-with-values.toml b/internal/toml-test/tests/valid/table/names-with-values.toml
new file mode 100644
index 0000000..c297d16
--- /dev/null
+++ b/internal/toml-test/tests/valid/table/names-with-values.toml
@@ -0,0 +1,23 @@
+[a.b.c]
+key = 1
+
+[a."b.c"]
+key = 2
+
+[a.'d.e']
+key = 3
+
+[a.' x ']
+key = 4
+
+[ d.e.f ]
+key = 5
+
+[ g . h . i ]
+key = 6
+
+[ j . "ʞ" . 'l' ]
+key = 7
+
+[x.1.2]
+key = 8
diff --git a/internal/toml-test/tests/valid/table/without-super-with-values.json b/internal/toml-test/tests/valid/table/without-super-with-values.json
new file mode 100644
index 0000000..e295139
--- /dev/null
+++ b/internal/toml-test/tests/valid/table/without-super-with-values.json
@@ -0,0 +1,14 @@
+{
+    "x": {
+        "c": {"type": "integer", "value": "3"},
+        "d": {"type": "integer", "value": "4"},
+        "y": {
+            "z": {
+                "w": {
+                    "a": {"type": "integer", "value": "1"},
+                    "b": {"type": "integer", "value": "2"}
+                }
+            }
+        }
+    }
+}
diff --git a/internal/toml-test/tests/valid/table/without-super-with-values.toml b/internal/toml-test/tests/valid/table/without-super-with-values.toml
new file mode 100644
index 0000000..0bb5196
--- /dev/null
+++ b/internal/toml-test/tests/valid/table/without-super-with-values.toml
@@ -0,0 +1,9 @@
+# [x] you
+# [x.y] don't
+# [x.y.z] need these
+[x.y.z.w] # for this to work
+a = 1
+b = 2
+[x] # defining a super-table afterwards is ok
+c = 3
+d = 4
diff --git a/toml_test.go b/toml_test.go
index 915a51f..bbf54e4 100644
--- a/toml_test.go
+++ b/toml_test.go
@@ -243,6 +243,14 @@ var metaTests = map[string]string{
 		x.y.z.w:  Hash
 		x:        Hash
 	`,
+	"table/without-super-with-values": `
+		x.y.z.w: Hash
+		x.y.z.w.a: Integer
+		x.y.z.w.b: Integer
+		x: Hash
+		x.c: Integer
+		x.d: Integer
+	`,
 }
 
 // TOML 1.0