From bb238c98c3ab6f1dd10c02038e000e22f25d94d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Thu, 17 Sep 2026 23:07:27 +0200 Subject: [PATCH] fix(decode): let the later field win an equal-depth name clash Assisted-by: GLM 5.3 --- CHANGELOG.md | 2 ++ decode.go | 2 +- decode_test.go | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ced0e6..5e620cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 is not valid UTF-8, the way scalar keys already did, instead of silently emitting corrupt TOML (a header that lost its key, an inline table with a missing key). +- Struct fields that resolve to one key at equal depth decode through the + field declared later, matching the documented rule; the first one won before. - 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 diff --git a/decode.go b/decode.go index 13d49df..754f432 100644 --- a/decode.go +++ b/decode.go @@ -289,7 +289,7 @@ func newStructSchema(t reflect.Type) structSchema { name = f.Name } key := strings.ToLower(name) - if existing, ok := s.byName[key]; !ok || depth < existing.depth { + if existing, ok := s.byName[key]; !ok || depth <= existing.depth { s.byName[key] = structFieldLoc{index: path, depth: depth} } } diff --git a/decode_test.go b/decode_test.go index 85ed86c..3f2925e 100644 --- a/decode_test.go +++ b/decode_test.go @@ -586,6 +586,22 @@ func TestUnmarshalEmbeddedNameClashShallowerWins(t *testing.T) { } } +func TestUnmarshalNameClashEqualDepthLaterWins(t *testing.T) { + // At equal depth the field declared later resolves the name, matching the + // documented rule. + type C struct { + First string `toml:"v"` + Second int `toml:"v"` + } + var c C + if err := Unmarshal([]byte("v = 1\n"), &c); err != nil { + t.Fatalf("unmarshal: %v", err) + } + if c.Second != 1 { + t.Fatalf("decoded: %+v, want the later field to take the value", c) + } +} + func TestUnmarshalUnknownKeyWithoutEmbeddedMap(t *testing.T) { var cfg RoundTripDerived if err := Unmarshal([]byte("rogue = 1\n"), &cfg); err != nil {