From b1df377728b1d45f46bf805a6f1175ced3a847e4 Mon Sep 17 00:00:00 2001 From: Team Humaki Date: Wed, 16 Sep 2026 18:00:15 -0700 Subject: [PATCH 1/2] section: return a zero-value Key for empty names Key() documents a zero-value when the name is missing, but NewKey rejects empty names and the error was discarded, so Key("") was nil and paniced callers. Return newKey instead of inserting an empty name. Fixes #358 --- section.go | 9 ++++++--- section_test.go | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/section.go b/section.go index a3615d8..4ef620d 100644 --- a/section.go +++ b/section.go @@ -166,9 +166,12 @@ func (s *Section) HasValue(value string) bool { func (s *Section) Key(name string) *Key { key, err := s.GetKey(name) if err != nil { - // It's OK here because the only possible error is empty key name, - // but if it's empty, this piece of code won't be executed. - key, _ = s.NewKey(name, "") + key, err = s.NewKey(name, "") + if err != nil { + // NewKey rejects empty names. Still honor the zero-value + // contract so Key("") is not nil. + return newKey(s, name, "") + } return key } return key diff --git a/section_test.go b/section_test.go index 09ed366..cecf5db 100644 --- a/section_test.go +++ b/section_test.go @@ -191,6 +191,14 @@ func TestSection_Key(t *testing.T) { assert.Equal(t, "404", k.Name()) }) + t.Run("empty key name", func(t *testing.T) { + k := f.Section("").Key("") + require.NotNil(t, k) + assert.Equal(t, "", k.Name()) + assert.Equal(t, "", k.Value()) + assert.False(t, f.Section("").HasKey("")) + }) + t.Run("key exists in parent section", func(t *testing.T) { k, err := f.Section("parent").NewKey("AGE", "18") require.NoError(t, err) From df6554589270b0b5e1761fc1faa659f7330f75d8 Mon Sep 17 00:00:00 2001 From: Joe Chen Date: Wed, 11 Nov 2026 20:21:35 +0000 Subject: [PATCH 2/2] Keep SetValue on the detached Key("") out of KeysHash The Key returned for an empty name isn't in the section, so setting its value must not add an entry that KeysHash shows but nothing writes or deletes. --- key.go | 5 ++++- section.go | 2 ++ section_test.go | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/key.go b/key.go index 800d04a..9db8c55 100644 --- a/key.go +++ b/key.go @@ -840,5 +840,8 @@ func (k *Key) SetValue(v string) { } k.value = v - k.s.keysHash[k.name] = v + // The Key Section.Key returns for an empty name isn't in the section. + if k.name != "" { + k.s.keysHash[k.name] = v + } } diff --git a/section.go b/section.go index 4ef620d..7ed7f7a 100644 --- a/section.go +++ b/section.go @@ -163,6 +163,8 @@ func (s *Section) HasValue(value string) bool { } // Key assumes named Key exists in section and returns a zero-value when not. +// For an empty name, the returned Key is not part of the section, so setting +// its value changes nothing in the section. func (s *Section) Key(name string) *Key { key, err := s.GetKey(name) if err != nil { diff --git a/section_test.go b/section_test.go index cecf5db..384067f 100644 --- a/section_test.go +++ b/section_test.go @@ -197,6 +197,11 @@ func TestSection_Key(t *testing.T) { assert.Equal(t, "", k.Name()) assert.Equal(t, "", k.Value()) assert.False(t, f.Section("").HasKey("")) + + before := f.Section("").KeysHash() + k.SetValue("x") + assert.Equal(t, before, f.Section("").KeysHash()) + assert.False(t, f.Section("").HasKey("")) }) t.Run("key exists in parent section", func(t *testing.T) {