From 65096d9b23118d23807ec8171f48279609d7c76d Mon Sep 17 00:00:00 2001 From: Dave Rolsky Date: Tue, 4 May 2021 10:29:11 -0500 Subject: [PATCH 1/2] Fix Makefile lines with leading space instead of tab --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index c5273407..4199a5e4 100644 --- a/Makefile +++ b/Makefile @@ -29,10 +29,10 @@ generate: build ./tests/escaping.go bin/easyjson -all \ ./tests/data.go \ - ./tests/nothing.go \ - ./tests/errors.go \ - ./tests/html.go \ - ./tests/type_declaration_skip.go + ./tests/nothing.go \ + ./tests/errors.go \ + ./tests/html.go \ + ./tests/type_declaration_skip.go bin/easyjson \ ./tests/nested_easy.go \ ./tests/named_type.go \ From 6ea728deef81cdcfc7071c6e2ecf4ec1c6d51058 Mon Sep 17 00:00:00 2001 From: Dave Rolsky Date: Mon, 10 May 2021 13:34:31 -0500 Subject: [PATCH 2/2] 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. --- gen/decoder.go | 44 ++++++++++++++++++++++++++------------------ gen/encoder.go | 31 +++++++++++++++++-------------- tests/basic_test.go | 1 + tests/data.go | 24 ++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 32 deletions(-) diff --git a/gen/decoder.go b/gen/decoder.go index 0a0faa26..a73fe937 100644 --- a/gen/decoder.go +++ b/gen/decoder.go @@ -61,26 +61,34 @@ var customDecoders = map[string]string{ func (g *Generator) genTypeDecoder(t reflect.Type, out string, tags fieldTags, indent int) error { ws := strings.Repeat(" ", indent) - unmarshalerIface := reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem() - if reflect.PtrTo(t).Implements(unmarshalerIface) { - fmt.Fprintln(g.out, ws+"("+out+").UnmarshalEasyJSON(in)") - return nil - } + // If the type is unnamed, then this is an anonymous struct. If that anon + // struct embeds another, named struct that implements one of these + // interfaces, then Implements(...) will return true, even if the anon + // struct has other fields. That causes us to skip code gen for those + // extra fields. This addresses + // https://github.com/mailru/easyjson/issues/337. + if t.Name() != "" { + unmarshalerIface := reflect.TypeOf((*easyjson.Unmarshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(unmarshalerIface) { + fmt.Fprintln(g.out, ws+"("+out+").UnmarshalEasyJSON(in)") + return nil + } - unmarshalerIface = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() - if reflect.PtrTo(t).Implements(unmarshalerIface) { - fmt.Fprintln(g.out, ws+"if data := in.Raw(); in.Ok() {") - fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalJSON(data) )") - fmt.Fprintln(g.out, ws+"}") - return nil - } + unmarshalerIface = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(unmarshalerIface) { + fmt.Fprintln(g.out, ws+"if data := in.Raw(); in.Ok() {") + fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalJSON(data) )") + fmt.Fprintln(g.out, ws+"}") + return nil + } - unmarshalerIface = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() - if reflect.PtrTo(t).Implements(unmarshalerIface) { - fmt.Fprintln(g.out, ws+"if data := in.UnsafeBytes(); in.Ok() {") - fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalText(data) )") - fmt.Fprintln(g.out, ws+"}") - return nil + unmarshalerIface = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(unmarshalerIface) { + fmt.Fprintln(g.out, ws+"if data := in.UnsafeBytes(); in.Ok() {") + fmt.Fprintln(g.out, ws+" in.AddError( ("+out+").UnmarshalText(data) )") + fmt.Fprintln(g.out, ws+"}") + return nil + } } err := g.genTypeDecoderNoCheck(t, out, tags, indent) diff --git a/gen/encoder.go b/gen/encoder.go index 74c530fc..286043b9 100644 --- a/gen/encoder.go +++ b/gen/encoder.go @@ -94,22 +94,25 @@ func parseFieldTags(f reflect.StructField) fieldTags { func (g *Generator) genTypeEncoder(t reflect.Type, in string, tags fieldTags, indent int, assumeNonEmpty bool) error { ws := strings.Repeat(" ", indent) - marshalerIface := reflect.TypeOf((*easyjson.Marshaler)(nil)).Elem() - if reflect.PtrTo(t).Implements(marshalerIface) { - fmt.Fprintln(g.out, ws+"("+in+").MarshalEasyJSON(out)") - return nil - } + // See the comment in genTypeDecoder for why we check the name. + if t.Name() != "" { + marshalerIface := reflect.TypeOf((*easyjson.Marshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(marshalerIface) { + fmt.Fprintln(g.out, ws+"("+in+").MarshalEasyJSON(out)") + return nil + } - marshalerIface = reflect.TypeOf((*json.Marshaler)(nil)).Elem() - if reflect.PtrTo(t).Implements(marshalerIface) { - fmt.Fprintln(g.out, ws+"out.Raw( ("+in+").MarshalJSON() )") - return nil - } + marshalerIface = reflect.TypeOf((*json.Marshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(marshalerIface) { + fmt.Fprintln(g.out, ws+"out.Raw( ("+in+").MarshalJSON() )") + return nil + } - marshalerIface = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() - if reflect.PtrTo(t).Implements(marshalerIface) { - fmt.Fprintln(g.out, ws+"out.RawText( ("+in+").MarshalText() )") - return nil + marshalerIface = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() + if reflect.PtrTo(t).Implements(marshalerIface) { + fmt.Fprintln(g.out, ws+"out.RawText( ("+in+").MarshalText() )") + return nil + } } err := g.genTypeEncoderNoCheck(t, in, tags, indent, assumeNonEmpty) diff --git a/tests/basic_test.go b/tests/basic_test.go index e18e515c..6fbb9667 100644 --- a/tests/basic_test.go +++ b/tests/basic_test.go @@ -59,6 +59,7 @@ var testCases = []struct { {&myTypeDeclaredValue, myTypeDeclaredString}, {&myTypeNotSkippedValue, myTypeNotSkippedString}, {&intern, internString}, + {&embedAnonWithEmbedValue, embedAnonWithEmbedString}, } func TestMarshal(t *testing.T) { diff --git a/tests/data.go b/tests/data.go index f64a48e2..cac61e2e 100644 --- a/tests/data.go +++ b/tests/data.go @@ -820,3 +820,27 @@ type MyUInt8Array [2]MyUInt8 var myUInt8ArrayValue = MyUInt8Array{1, 2} var myUInt8ArrayString = `[1,2]` + +type GrandChild struct { + V string `json:"v"` +} + +//easyjson:json +type EmbedAnonWithEmbed struct { + Child struct { + GrandChild + Sibling string `json:"sibling"` + } `json:"child"` +} + +var embedAnonWithEmbedValue = EmbedAnonWithEmbed{ + Child: struct { + GrandChild + Sibling string `json:"sibling"` + }{ + GrandChild: GrandChild{V: "val"}, + Sibling: "joe", + }, +} + +var embedAnonWithEmbedString = `{"child":{"sibling":"joe","v":"val"}}`