Skip to content

fix: honor field shadowing in squashed embedded structs - #209

Open
AdamMagued wants to merge 1 commit into
go-viper:mainfrom
AdamMagued:fix/embedded-struct-field-shadowing
Open

AdamMagued wants to merge 1 commit into
go-viper:mainfrom
AdamMagued:fix/embedded-struct-field-shadowing

Conversation

@AdamMagued

Copy link
Copy Markdown

Fixes #81

Problem

When decoding a map into a struct with embedded, squashed structs, fields in an embedded struct that share a key name with an outer struct field are not shadowed. Instead, both fields are added to the decode queue and matched against the same map key.

For example:

type A struct {
    ObjType string `json:"obj_type"`
    ObjID   int64  `json:"obj_id"`
    Version int    `json:"version"`
}

type B struct {
    Version string `json:"version"`
    A
}

When decoding {"obj_type": "b", "obj_id": 2, "version": "defg"} with Squash: true:

  1. B.Version correctly decodes "defg".
  2. B.A.Version is also evaluated against "version": "defg", attempting to parse "defg" into an int, returning an unconvertible type/parsing error.

In Go struct embedding rules, outer struct fields at shallower embedding depth shadow fields with identical names or matching tags in embedded structs at deeper levels.

Root Cause

In decodeStructFromMap, embedded structs are flattened via breadth-first search into a flat fields slice. During collection, no record of embedding depth was retained, and all fields were queued indiscriminately. During iteration over fields, each field performed a map key lookup on dataVal, so both outer and embedded fields matched the same map key.

Fix

  1. Track embedding depth during the breadth-first traversal of squashed structs (depth: 0 for outer struct, incrementing for nested squashed structs).
  2. Record field names and their minimum embedding depth in a lookup table during field collection.
  3. If an embedded field (currentDepth > sf.depth) matches a field name already claimed at a shallower depth via MatchName, skip it as shadowed.

Verification

  • Added TestDecode_EmbeddedStructFieldShadowing reproducing the exact failure from Decode with embedded struct wrong set value into child struct #81 (fails prior to fix with parsing error on A.Version, passes with B.Version = "defg" and B.A.Version = 0).
  • Added TestDecode_EmbeddedStructFieldShadowing_MultiLevel verifying multi-level squashing where depth 0 shadows depths 1 and 2, and depth 1 shadows depth 2 while unconflicting fields remain populated.
  • Ran full test suite with race detection and shuffle enabled: go test -v -race -shuffle=on ./... passed.
  • Ran gofmt -s -w . and go vet ./... cleanly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decode with embedded struct wrong set value into child struct

1 participant