From bb584cc9d8163aa54a9efe0b59500ae39a3ffa8a Mon Sep 17 00:00:00 2001 From: "Nicolas S. Dade" Date: Thu, 5 Jan 2017 16:20:26 -0800 Subject: [PATCH 1/3] add unit test simplifying the failing case easyjson is mistakenly naming the type of this field 'tests.MyString' in the generated output something about a named type inside an anonmymous type is triggering this bug. --- Makefile | 4 +++- tests/named_type.go | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/named_type.go diff --git a/Makefile b/Makefile index e2c9bd6..0ebe32f 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,8 @@ generate: root build .root/src/$(PKG)/tests/snake.go \ .root/src/$(PKG)/tests/data.go \ .root/src/$(PKG)/tests/omitempty.go \ - .root/src/$(PKG)/tests/nothing.go + .root/src/$(PKG)/tests/nothing.go \ + .root/src/$(PKG)/tests/named_type.go .root/bin/easyjson -all .root/src/$(PKG)/tests/data.go .root/bin/easyjson -all .root/src/$(PKG)/tests/nothing.go @@ -29,6 +30,7 @@ generate: root build .root/bin/easyjson -omit_empty .root/src/$(PKG)/tests/omitempty.go .root/bin/easyjson -build_tags=use_easyjson .root/src/$(PKG)/benchmark/data.go .root/bin/easyjson .root/src/$(PKG)/tests/nested_easy.go + .root/bin/easyjson .root/src/$(PKG)/tests/named_type.go test: generate root go test \ diff --git a/tests/named_type.go b/tests/named_type.go new file mode 100644 index 0000000..a157553 --- /dev/null +++ b/tests/named_type.go @@ -0,0 +1,15 @@ +package tests + +//easyjson:json +type NamedType struct { + Inner struct { + // easyjson is mistakenly naming the type of this field 'tests.MyString' in the generated output + // something about a named type inside an anonmymous type is triggering this bug + Field MyString + } +} + +type MyString string + +var namedTypeValue = NamedType{Inner: struct{ Field MyString }{Field: "test"}} +var namedTypeValueString = `{"Inner":{"Field":"test"}}` From 14bd82d332192df0ae0af5a3d21cec9aed176b4a Mon Sep 17 00:00:00 2001 From: "Nicolas S. Dade" Date: Thu, 5 Jan 2017 20:13:35 -0800 Subject: [PATCH 2/3] properly handle named types inside anonymous structs without this the tests/named_type.go test fails because reflect.Type.String() of the anonmymous struct returns package-qualified type names for the fields, including those types which are within the current package. (it could also fail when our alias for the some other package collides with Type.String() picked) --- gen/generator.go | 16 ++++++++++++++-- tests/named_type.go | 8 ++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/gen/generator.go b/gen/generator.go index 32e806e..323fc64 100644 --- a/gen/generator.go +++ b/gen/generator.go @@ -60,7 +60,7 @@ func NewGenerator(filename string) *Generator { imports: map[string]string{ pkgWriter: "jwriter", pkgLexer: "jlexer", - pkgEasyjson: "easyjson", + pkgEasyjson: "easyjson", "encoding/json": "json", }, fieldNamer: DefaultFieldNamer{}, @@ -255,11 +255,23 @@ func (g *Generator) getType(t reflect.Type) string { } if t.Name() == "" || t.PkgPath() == "" { + if t.Kind() == reflect.Struct { + // the fields of an anonymous struct can have named types, + // and t.String() will not be sufficient because it does not + // remove the package name when it matches g.pkgPath. + // so we convert by hand + nf := t.NumField() + lines := make([]string, 0, nf) + for i := 0; i < nf; i++ { + f := t.Field(i) + lines = append(lines, f.Name+" "+g.getType(f.Type)) + } + return strings.Join([]string{"struct { ", strings.Join(lines, "; "), " }"}, "") + } return t.String() } else if t.PkgPath() == g.pkgPath { return t.Name() } - // TODO: unnamed structs. return g.pkgAlias(t.PkgPath()) + "." + t.Name() } diff --git a/tests/named_type.go b/tests/named_type.go index a157553..9948d3b 100644 --- a/tests/named_type.go +++ b/tests/named_type.go @@ -5,11 +5,15 @@ type NamedType struct { Inner struct { // easyjson is mistakenly naming the type of this field 'tests.MyString' in the generated output // something about a named type inside an anonmymous type is triggering this bug - Field MyString + Field MyString + Field2 int } } type MyString string -var namedTypeValue = NamedType{Inner: struct{ Field MyString }{Field: "test"}} +var namedTypeValue = NamedType{Inner: struct { + Field MyString + Field2 int +}{Field: "test"}} var namedTypeValueString = `{"Inner":{"Field":"test"}}` From 012765f25f17d602366e8c29265d9efbfdbb6b13 Mon Sep 17 00:00:00 2001 From: "Nicolas S. Dade" Date: Sat, 7 Jan 2017 16:50:05 -0800 Subject: [PATCH 3/3] field tags are considered part of the anonymous struct definition so they must be included in the generated source code. --- gen/generator.go | 17 ++++++++++++++++- tests/basic_test.go | 1 + tests/named_type.go | 17 ++++++++++------- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/gen/generator.go b/gen/generator.go index 323fc64..1b9284d 100644 --- a/gen/generator.go +++ b/gen/generator.go @@ -264,7 +264,12 @@ func (g *Generator) getType(t reflect.Type) string { lines := make([]string, 0, nf) for i := 0; i < nf; i++ { f := t.Field(i) - lines = append(lines, f.Name+" "+g.getType(f.Type)) + line := f.Name + " " + g.getType(f.Type) + t := f.Tag + if t != "" { + line += " " + escapeTag(t) + } + lines = append(lines, line) } return strings.Join([]string{"struct { ", strings.Join(lines, "; "), " }"}, "") } @@ -275,6 +280,16 @@ func (g *Generator) getType(t reflect.Type) string { return g.pkgAlias(t.PkgPath()) + "." + t.Name() } +// escape a struct field tag string back to source code +func escapeTag(tag reflect.StructTag) string { + t := string(tag) + if strings.ContainsRune(t, '`') { + // there are ` in the string; we can't use ` to enclose the string + return strconv.Quote(t) + } + return "`" + t + "`" +} + // uniqueVarName returns a file-unique name that can be used for generated variables. func (g *Generator) uniqueVarName() string { g.varCounter++ diff --git a/tests/basic_test.go b/tests/basic_test.go index 25b1bfc..c072f28 100644 --- a/tests/basic_test.go +++ b/tests/basic_test.go @@ -36,6 +36,7 @@ var testCases = []struct { {&deepNestValue, deepNestString}, {&IntsValue, IntsString}, {&mapStringStringValue, mapStringStringString}, + {&namedTypeValue, namedTypeValueString}, } func TestMarshal(t *testing.T) { diff --git a/tests/named_type.go b/tests/named_type.go index 9948d3b..0ff8dfe 100644 --- a/tests/named_type.go +++ b/tests/named_type.go @@ -5,15 +5,18 @@ type NamedType struct { Inner struct { // easyjson is mistakenly naming the type of this field 'tests.MyString' in the generated output // something about a named type inside an anonmymous type is triggering this bug - Field MyString - Field2 int + Field MyString `tag:"value"` + Field2 int "tag:\"value with ` in it\"" } } type MyString string -var namedTypeValue = NamedType{Inner: struct { - Field MyString - Field2 int -}{Field: "test"}} -var namedTypeValueString = `{"Inner":{"Field":"test"}}` +var namedTypeValue NamedType + +func init() { + namedTypeValue.Inner.Field = "test" + namedTypeValue.Inner.Field2 = 123 +} + +var namedTypeValueString = `{"Inner":{"Field":"test","Field2":123}}`