Repository navigation
Fix Set on nil nested maps - #191
Conversation
Signed-off-by: Yusuf Efe <120668197+yuefdev@users.noreply.github.com>
hanzei
left a comment
There was a problem hiding this comment.
Non-blocking suggestions:
- The same panic is still reachable behind a slice index. The branch is guarded by
len(indexes) == 0, so a nil map reached through[i]still crashes:For comparison,objx.Map{"a": []any{map[string]any(nil)}}.Set("a[0].x", 1) // panic objx.Map{"a": []map[string]any{nil}}.Set("a[0].x", 1) // panic
[]any{nil}with the sameSetis a silent no-op. One fix: at the top of themap[string]anycase, returnnil, falsewhencurMSI == nil && action == actionSet. That matches the plain-nil behaviour rather than auto-creating maps inside slices. Your call whether it fits here or in a follow-up. - A nil
objx.Mapis replaced with amap[string]any{}, not anobjx.Map{}. That's consistent with how non-map values are overwritten, andObjxMap()handles both, so I'd keep it. Anassert.IsTypein the test would pin it down. - Only the
mapsubtest reproduces the bug. ThenilandobjxMapcases already passed before the fix, presumably becauseMap.Setunwraps a nilobjx.Map. They're fine as regression coverage.
|
@hanzei I've included the slice case here as well (9b3fd94). Setting through a nil map inside a slice is now a no-op, matching
|
hanzei
left a comment
There was a problem hiding this comment.
With the nil check at the top of access, Set on a nil top-level Map now does nothing instead of panicking. objx.New(badInput).Set("a", 1) would drop the write without any error. I'd keep the panic, as a nil Go map and MergeHere do, and check only for nil maps reached through a slice index:
curMSI := current.(map[string]any)
- if curMSI == nil && action == actionSet {
- return nil, false
- }
@@
}
+ // A nil map inside a slice cannot be replaced from here.
+ if action == actionSet {
+ if v := reflect.ValueOf(current); v.Kind() == reflect.Map && v.IsNil() {
+ return nil, false
+ }
+ }
}TestAccessorsAccessSetNilMap would then use assert.Panics(t, func() { m.Set("answer", 42) }). The tests, vet and lint pass with both changes.
Generated by Claude Code
|
Moved the nil-map guard after slice indexing, so a nil root |
Summary
Map{"nested": map[string]any(nil)}.Set("nested.answer", 42)panics instead of creating the nested map. The same happens with a nilobjx.Map.Treat both like a missing map when setting a nested value. Adds a regression test for both types and plain nil.
Checked with
go test -race ./...,go vet ./..., golangci-lint v2.14.0 andgo generate ./...(no changes). Lint and generation ran in an LF checkout.