Skip to content

Commit

Permalink
fixed panic: reflect: indirection through nil pointer to embedded struct
Browse files Browse the repository at this point in the history
  • Loading branch information
morus12 committed Apr 3, 2024
1 parent 1ab5d82 commit 3d48d14
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 6 deletions.
13 changes: 8 additions & 5 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func (c *cache) get(t reflect.Type) *structInfo {
info := c.m[t]
c.l.RUnlock()
if info == nil {
info = c.create(t, "")
info = c.create(t, "", nil)
c.l.Lock()
c.m[t] = info
c.l.Unlock()
Expand All @@ -126,14 +126,14 @@ func (c *cache) get(t reflect.Type) *structInfo {
}

// create creates a structInfo with meta-data about a struct.
func (c *cache) create(t reflect.Type, parentAlias string) *structInfo {
func (c *cache) create(t reflect.Type, parentAlias string, fieldIndex []int) *structInfo {
info := &structInfo{}
var anonymousInfos []*structInfo
for i := 0; i < t.NumField(); i++ {
if f := c.createField(t.Field(i), parentAlias); f != nil {
if f := c.createField(t.Field(i), parentAlias, fieldIndex); f != nil {
info.fields = append(info.fields, f)
if ft := indirectType(f.typ); ft.Kind() == reflect.Struct && f.isAnonymous {
anonymousInfos = append(anonymousInfos, c.create(ft, f.canonicalAlias))
anonymousInfos = append(anonymousInfos, c.create(ft, f.canonicalAlias, append(fieldIndex, t.Field(i).Index...)) )
}
}
}
Expand All @@ -151,7 +151,7 @@ func (c *cache) create(t reflect.Type, parentAlias string) *structInfo {
}

// createField creates a fieldInfo for the given field.
func (c *cache) createField(field reflect.StructField, parentAlias string) *fieldInfo {
func (c *cache) createField(field reflect.StructField, parentAlias string, parentIndex []int) *fieldInfo {
alias, options := fieldAlias(field, c.tag)
if alias == "-" {
// Ignore this field.
Expand Down Expand Up @@ -198,6 +198,7 @@ func (c *cache) createField(field reflect.StructField, parentAlias string) *fiel
isAnonymous: field.Anonymous,
isRequired: options.Contains("required"),
defaultValue: options.getDefaultOptionValue(),
fieldIndex: append(parentIndex, field.Index...),
}
}

Expand Down Expand Up @@ -250,6 +251,8 @@ type fieldInfo struct {
isAnonymous bool
isRequired bool
defaultValue string

fieldIndex []int
}

func (f *fieldInfo) paths(prefix string) []string {
Expand Down
5 changes: 4 additions & 1 deletion decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ func (d *Decoder) setDefaults(t reflect.Type, v reflect.Value) MultiError {
errs := MultiError{}

for _, f := range struc.fields {
vCurrent := v.FieldByName(f.name)
vCurrent, err := v.FieldByIndexErr(f.fieldIndex)
if err != nil {
continue
}

if vCurrent.Type().Kind() == reflect.Struct && f.defaultValue == "" {
errs.merge(d.setDefaults(vCurrent.Type(), vCurrent))
Expand Down
68 changes: 68 additions & 0 deletions decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2056,6 +2056,74 @@ func TestUnmashalPointerToEmbedded(t *testing.T) {
}
}

type S24 struct {
F1 string `schema:"F1"`
}

type S24e struct {
*S24
F2 string `schema:"F2"`
}

func TestUnmarshallToEmbeddedNoData(t *testing.T) {
data := map[string][]string{
"F3": {"raw a"},
}

s := &S24e{}

decoder := NewDecoder()
err := decoder.Decode(s, data);

expectedErr := `schema: invalid path "F3"`
if err.Error() != expectedErr {
t.Fatalf("got %q, want %q", err, expectedErr)
}
}
type S25ee struct {
F3 string `schema:"F3"`
}

type S25e struct {
S25ee
F2 string `schema:"F2"`
}

type S25 struct {
S25e
F1 string `schema:"F1"`
}

func TestDoubleEmbedded(t *testing.T){
data := map[string][]string{
"F1": {"raw a"},
"F2": {"raw b"},
"F3": {"raw c"},
}


s := S25{}
decoder := NewDecoder()

if err := decoder.Decode(&s, data); err != nil {
t.Fatal("Error while decoding:", err)
}

expected := S25{
F1: "raw a",
S25e: S25e{
F2: "raw b",
S25ee: S25ee{
F3: "raw c",
},
},
}
if !reflect.DeepEqual(expected, s) {
t.Errorf("Expected %v errors, got %v", expected, s)
}

}

func TestDefaultValuesAreSet(t *testing.T) {
type N struct {
S1 string `schema:"s1,default:test1"`
Expand Down

0 comments on commit 3d48d14

Please sign in to comment.