Skip to content

Commit 6ea728d

Browse files
committed
Fix bug in handling on anon structs which embed other structs
If the anon struct embeds another struct _and_ contains fields of its own, there were cases where the generated code would ignore the anon struct's own fields. This happened because when checking if the anon struct implements a marshaling interface, the reflect package returns true if the anon struct's _embedded struct_ implements that interface. But using that interface means that the other fields in the anon struct are ignored.
1 parent 65096d9 commit 6ea728d

File tree

4 files changed

+68
-32
lines changed

4 files changed

+68
-32
lines changed

gen/decoder.go

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -61,26 +61,34 @@ var customDecoders = map[string]string{
6161
func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, indent int) error {
6262
ws := strings.Repeat(" ", indent)
6363

64-
unmarshalerIface := reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem()
65-
if reflect.PtrTo(t).Implements(unmarshalerIface) {
66-
fmt.Fprintln(g.out, ws+"("+out+").UnmarshalEasyJSON(in)")
67-
return nil
68-
}
64+
// If the type is unnamed, then this is an anonymous struct. If that anon
65+
// struct embeds another, named struct that implements one of these
66+
// interfaces, then Implements(...) will return true, even if the anon
67+
// struct has other fields. That causes us to skip code gen for those
68+
// extra fields. This addresses
69+
// https://github.com/mailru/easyjson/issues/337.
70+
if t.Name() != "" {
71+
unmarshalerIface := reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem()
72+
if reflect.PtrTo(t).Implements(unmarshalerIface) {
73+
fmt.Fprintln(g.out, ws+"("+out+").UnmarshalEasyJSON(in)")
74+
return nil
75+
}
6976

70-
unmarshalerIface = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
71-
if reflect.PtrTo(t).Implements(unmarshalerIface) {
72-
fmt.Fprintln(g.out, ws+"if data := in.Raw(); in.Ok() {")
73-
fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalJSON(data) )")
74-
fmt.Fprintln(g.out, ws+"}")
75-
return nil
76-
}
77+
unmarshalerIface = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
78+
if reflect.PtrTo(t).Implements(unmarshalerIface) {
79+
fmt.Fprintln(g.out, ws+"if data := in.Raw(); in.Ok() {")
80+
fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalJSON(data) )")
81+
fmt.Fprintln(g.out, ws+"}")
82+
return nil
83+
}
7784

78-
unmarshalerIface = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
79-
if reflect.PtrTo(t).Implements(unmarshalerIface) {
80-
fmt.Fprintln(g.out, ws+"if data := in.UnsafeBytes(); in.Ok() {")
81-
fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalText(data) )")
82-
fmt.Fprintln(g.out, ws+"}")
83-
return nil
85+
unmarshalerIface = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
86+
if reflect.PtrTo(t).Implements(unmarshalerIface) {
87+
fmt.Fprintln(g.out, ws+"if data := in.UnsafeBytes(); in.Ok() {")
88+
fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalText(data) )")
89+
fmt.Fprintln(g.out, ws+"}")
90+
return nil
91+
}
8492
}
8593

8694
err := g.genTypeDecoderNoCheck(t, out, tags, indent)

gen/encoder.go

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -94,22 +94,25 @@ func parseFieldTags(f reflect.StructField) fieldTags {
9494
func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, indent int, assumeNonEmpty bool) error {
9595
ws := strings.Repeat(" ", indent)
9696

97-
marshalerIface := reflect.TypeOf((*easyjson.Marshaler)(nil)).Elem()
98-
if reflect.PtrTo(t).Implements(marshalerIface) {
99-
fmt.Fprintln(g.out, ws+"("+in+").MarshalEasyJSON(out)")
100-
return nil
101-
}
97+
// See the comment in genTypeDecoder for why we check the name.
98+
if t.Name() != "" {
99+
marshalerIface := reflect.TypeOf((*easyjson.Marshaler)(nil)).Elem()
100+
if reflect.PtrTo(t).Implements(marshalerIface) {
101+
fmt.Fprintln(g.out, ws+"("+in+").MarshalEasyJSON(out)")
102+
return nil
103+
}
102104

103-
marshalerIface = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
104-
if reflect.PtrTo(t).Implements(marshalerIface) {
105-
fmt.Fprintln(g.out, ws+"out.Raw( ("+in+").MarshalJSON() )")
106-
return nil
107-
}
105+
marshalerIface = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
106+
if reflect.PtrTo(t).Implements(marshalerIface) {
107+
fmt.Fprintln(g.out, ws+"out.Raw( ("+in+").MarshalJSON() )")
108+
return nil
109+
}
108110

109-
marshalerIface = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
110-
if reflect.PtrTo(t).Implements(marshalerIface) {
111-
fmt.Fprintln(g.out, ws+"out.RawText( ("+in+").MarshalText() )")
112-
return nil
111+
marshalerIface = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
112+
if reflect.PtrTo(t).Implements(marshalerIface) {
113+
fmt.Fprintln(g.out, ws+"out.RawText( ("+in+").MarshalText() )")
114+
return nil
115+
}
113116
}
114117

115118
err := g.genTypeEncoderNoCheck(t, in, tags, indent, assumeNonEmpty)

tests/basic_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ var testCases = []struct {
5959
{&myTypeDeclaredValue, myTypeDeclaredString},
6060
{&myTypeNotSkippedValue, myTypeNotSkippedString},
6161
{&intern, internString},
62+
{&embedAnonWithEmbedValue, embedAnonWithEmbedString},
6263
}
6364

6465
func TestMarshal(t *testing.T) {

tests/data.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,3 +820,27 @@ type MyUInt8Array [2]MyUInt8
820820
var myUInt8ArrayValue = MyUInt8Array{1, 2}
821821

822822
var myUInt8ArrayString = `[1,2]`
823+
824+
type GrandChild struct {
825+
V string `json:"v"`
826+
}
827+
828+
//easyjson:json
829+
type EmbedAnonWithEmbed struct {
830+
Child struct {
831+
GrandChild
832+
Sibling string `json:"sibling"`
833+
} `json:"child"`
834+
}
835+
836+
var embedAnonWithEmbedValue = EmbedAnonWithEmbed{
837+
Child: struct {
838+
GrandChild
839+
Sibling string `json:"sibling"`
840+
}{
841+
GrandChild: GrandChild{V: "val"},
842+
Sibling: "joe",
843+
},
844+
}
845+
846+
var embedAnonWithEmbedString = `{"child":{"sibling":"joe","v":"val"}}`

0 commit comments

Comments
 (0)