-
Notifications
You must be signed in to change notification settings - Fork 38.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix bunch of issues with conversion generator - part 3 #23373
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -207,7 +207,10 @@ func isConvertible(in, out *types.Type, preexisting conversions) bool { | |
if _, ok := preexisting[conversionType{in, out}]; ok { | ||
return true | ||
} | ||
return isDirectlyConvertible(in, out, preexisting) | ||
} | ||
|
||
func isDirectlyConvertible(in, out *types.Type, preexisting conversions) bool { | ||
// If one of the types is Alias, resolve it. | ||
if in.Kind == types.Alias { | ||
return isConvertible(in.Underlying, out, preexisting) | ||
|
@@ -234,8 +237,11 @@ func isConvertible(in, out *types.Type, preexisting conversions) bool { | |
|
||
switch in.Kind { | ||
case types.Builtin: | ||
// FIXME: Enough to be convertible - see AWSElastic | ||
return in.Name == out.Name | ||
if in == out { | ||
return true | ||
} | ||
// TODO: Support more conversion types. | ||
return types.IsInteger(in) && types.IsInteger(out) | ||
case types.Struct: | ||
convertible := true | ||
for _, inMember := range in.Members { | ||
|
@@ -404,10 +410,10 @@ func (g *genConversion) Init(c *generator.Context, w io.Writer) error { | |
func (g *genConversion) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { | ||
sw := generator.NewSnippetWriter(w, c, "$", "$") | ||
internalType, _ := getInternalTypeFor(c, t) | ||
if _, ok := g.preexists(t, internalType); !ok && isConvertible(t, internalType, g.preexisting) { | ||
if isDirectlyConvertible(t, internalType, g.preexisting) { | ||
g.generateConversion(t, internalType, sw) | ||
} | ||
if _, ok := g.preexists(internalType, t); !ok && isConvertible(internalType, t, g.preexisting) { | ||
if isDirectlyConvertible(internalType, t, g.preexisting) { | ||
g.generateConversion(internalType, t, sw) | ||
} | ||
return sw.Error() | ||
|
@@ -416,16 +422,27 @@ func (g *genConversion) GenerateType(c *generator.Context, t *types.Type, w io.W | |
func (g *genConversion) generateConversion(inType, outType *types.Type, sw *generator.SnippetWriter) { | ||
funcName := g.funcNameTmpl(inType, outType) | ||
if g.targetPackage == conversionPackagePath { | ||
sw.Do(fmt.Sprintf("func %s(in $.inType|raw$, out *$.outType|raw$, s *Scope) error {\n", funcName), argsFromType(inType, outType)) | ||
sw.Do(fmt.Sprintf("func auto%s(in *$.inType|raw$, out *$.outType|raw$, s Scope) error {\n", funcName), argsFromType(inType, outType)) | ||
} else { | ||
sw.Do(fmt.Sprintf("func %s(in $.inType|raw$, out *$.outType|raw$, s *conversion.Scope) error {\n", funcName), argsFromType(inType, outType)) | ||
sw.Do(fmt.Sprintf("func auto%s(in *$.inType|raw$, out *$.outType|raw$, s conversion.Scope) error {\n", funcName), argsFromType(inType, outType)) | ||
} | ||
sw.Do("if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {\n", nil) | ||
sw.Do("defaulting.(func($.|raw$))(in)\n", inType) | ||
sw.Do("defaulting.(func(*$.|raw$))(in)\n", inType) | ||
sw.Do("}\n", nil) | ||
g.generateFor(inType, outType, sw) | ||
sw.Do("return nil\n", nil) | ||
sw.Do("}\n\n", nil) | ||
|
||
// If there is no public preexisting Convert method, generate it. | ||
if _, ok := g.preexists(inType, outType); !ok { | ||
if g.targetPackage == conversionPackagePath { | ||
sw.Do(fmt.Sprintf("func %s(in *$.inType|raw$, out *$.outType|raw$, s Scope) error {\n", funcName), argsFromType(inType, outType)) | ||
} else { | ||
sw.Do(fmt.Sprintf("func %s(in *$.inType|raw$, out *$.outType|raw$, s conversion.Scope) error {\n", funcName), argsFromType(inType, outType)) | ||
} | ||
sw.Do(fmt.Sprintf("return auto%s(in, out, s)\n", funcName), argsFromType(inType, outType)) | ||
sw.Do("}\n\n", nil) | ||
} | ||
} | ||
|
||
// we use the system of shadowing 'in' and 'out' so that the same code is valid | ||
|
@@ -453,33 +470,46 @@ func (g *genConversion) generateFor(inType, outType *types.Type, sw *generator.S | |
} | ||
|
||
func (g *genConversion) doBuiltin(inType, outType *types.Type, sw *generator.SnippetWriter) { | ||
sw.Do("*out = in\n", nil) | ||
if inType == outType { | ||
sw.Do("*out = *in\n", nil) | ||
} else { | ||
sw.Do("*out = $.|raw$(*in)\n", outType) | ||
} | ||
} | ||
|
||
func (g *genConversion) doMap(inType, outType *types.Type, sw *generator.SnippetWriter) { | ||
sw.Do("*out = make($.|raw$)\n", outType) | ||
sw.Do("out = make($.|raw$, len(in))\n", outType) | ||
if outType.Key.IsAssignable() { | ||
sw.Do("for key, val := range in {\n", nil) | ||
if outType.Elem.IsAssignable() { | ||
if inType.Key == outType.Key { | ||
sw.Do("out[key] = ", nil) | ||
} else { | ||
sw.Do("out[$.|raw$(key)] = ", outType.Key) | ||
} | ||
if inType.Elem == outType.Elem { | ||
sw.Do("(*out)[key] = val\n", nil) | ||
sw.Do("val\n", nil) | ||
} else { | ||
sw.Do("(*out)[key] = $.|raw$(val)\n", outType.Elem) | ||
sw.Do("$.|raw$(val)\n", outType.Elem) | ||
} | ||
} else { | ||
sw.Do("newVal := new($.|raw$)\n", outType.Elem) | ||
if function, ok := g.preexists(inType.Elem, outType.Elem); ok { | ||
sw.Do("if err := $.|raw$(val, newVal, s); err != nil {\n", function) | ||
sw.Do("if err := $.|raw$(&val, newVal, s); err != nil {\n", function) | ||
} else if g.convertibleOnlyWithinPackage(inType.Elem, outType.Elem) { | ||
funcName := g.funcNameTmpl(inType.Elem, outType.Elem) | ||
sw.Do(fmt.Sprintf("if err := %s(val, newVal, s); err != nil {\n", funcName), argsFromType(inType.Elem, outType.Elem)) | ||
sw.Do(fmt.Sprintf("if err := %s(&val, newVal, s); err != nil {\n", funcName), argsFromType(inType.Elem, outType.Elem)) | ||
} else { | ||
sw.Do("// TODO: Inefficient conversion - can we improve it?\n", nil) | ||
sw.Do("if err := s.Convert(val, newVal, 0); err != nil {\n", nil) | ||
sw.Do("if err := s.Convert(&val, newVal, 0); err != nil {\n", nil) | ||
} | ||
sw.Do("return err\n", nil) | ||
sw.Do("}\n", nil) | ||
sw.Do("(*out)[key] = *newVal\n", nil) | ||
if inType.Key == outType.Key { | ||
sw.Do("out[key] = *newVal\n", nil) | ||
} else { | ||
sw.Do("out[$.|raw$(key)] = *newVal\n", outType.Key) | ||
} | ||
} | ||
} else { | ||
// TODO: Implement it when necessary. | ||
|
@@ -490,26 +520,26 @@ func (g *genConversion) doMap(inType, outType *types.Type, sw *generator.Snippet | |
} | ||
|
||
func (g *genConversion) doSlice(inType, outType *types.Type, sw *generator.SnippetWriter) { | ||
sw.Do("*out = make($.|raw$, len(in))\n", outType) | ||
sw.Do("out = make($.|raw$, len(in))\n", outType) | ||
if inType.Elem == outType.Elem && inType.Elem.Kind == types.Builtin { | ||
sw.Do("copy(*out, in)\n", nil) | ||
sw.Do("copy(out, in)\n", nil) | ||
} else { | ||
sw.Do("for i := range in {\n", nil) | ||
if outType.Elem.IsAssignable() { | ||
if inType.Elem == outType.Elem { | ||
sw.Do("(*out)[i] = in[i]\n", nil) | ||
sw.Do("out[i] = in[i]\n", nil) | ||
} else { | ||
sw.Do("(*out)[i] = $.|raw$(in[i])\n", outType.Elem) | ||
sw.Do("out[i] = $.|raw$(in[i])\n", outType.Elem) | ||
} | ||
} else { | ||
if function, ok := g.preexists(inType.Elem, outType.Elem); ok { | ||
sw.Do("if err := $.|raw$(&(*in)[i], &(*out)[i], s); err != nil {\n", function) | ||
sw.Do("if err := $.|raw$(&in[i], &out[i], s); err != nil {\n", function) | ||
} else if g.convertibleOnlyWithinPackage(inType.Elem, outType.Elem) { | ||
funcName := g.funcNameTmpl(inType.Elem, outType.Elem) | ||
sw.Do(fmt.Sprintf("if err := %s(in[i], &(*out)[i], c); err != nil {\n", funcName), argsFromType(inType.Elem, outType.Elem)) | ||
sw.Do(fmt.Sprintf("if err := %s(&in[i], &out[i], s); err != nil {\n", funcName), argsFromType(inType.Elem, outType.Elem)) | ||
} else { | ||
sw.Do("// TODO: Inefficient conversion - can we improve it?\n", nil) | ||
sw.Do("if err := s.Convert(in[i], &out[i], 0); err != nil {\n", nil) | ||
sw.Do("if err := s.Convert(&in[i], &out[i], 0); err != nil {\n", nil) | ||
} | ||
sw.Do("return err\n", nil) | ||
sw.Do("}\n", nil) | ||
|
@@ -526,56 +556,58 @@ func (g *genConversion) doStruct(inType, outType *types.Type, sw *generator.Snip | |
"outType": outMember.Type, | ||
"name": m.Name, | ||
} | ||
if function, ok := g.preexists(m.Type, outMember.Type); ok { | ||
args["function"] = function | ||
sw.Do("if err := $.function|raw$(&in.$.name$, &out.$.name$, s); err != nil {\n", args) | ||
sw.Do("return err\n", nil) | ||
sw.Do("}\n", nil) | ||
continue | ||
} | ||
switch m.Type.Kind { | ||
case types.Builtin: | ||
sw.Do("out.$.name$ = in.$.name$\n", args) | ||
if m.Type == outMember.Type { | ||
sw.Do("out.$.name$ = in.$.name$\n", args) | ||
} else { | ||
sw.Do("out.$.name$ = $.outType|raw$(in.$.name$)\n", args) | ||
} | ||
case types.Map, types.Slice, types.Pointer: | ||
sw.Do("if in.$.name$ != nil {\n", args) | ||
sw.Do("in, out := in.$.name$, &out.$.name$\n", args) | ||
sw.Do("in, out := in.$.name$, out.$.name$\n", args) | ||
g.generateFor(m.Type, outMember.Type, sw) | ||
sw.Do("} else {\n", nil) | ||
sw.Do("out.$.name$ = nil\n", args) | ||
sw.Do("}\n", nil) | ||
case types.Struct: | ||
if function, ok := g.preexists(m.Type, outMember.Type); ok { | ||
args["function"] = function | ||
sw.Do("if err := $.function|raw$(in.$.name$, &out.$.name$, s); err != nil {\n", args) | ||
} else if g.convertibleOnlyWithinPackage(m.Type, outMember.Type) { | ||
if g.convertibleOnlyWithinPackage(m.Type, outMember.Type) { | ||
funcName := g.funcNameTmpl(m.Type, outMember.Type) | ||
sw.Do(fmt.Sprintf("if err := %s(in.$.name$, &out.$.name$, c); err != nil {\n", funcName), args) | ||
sw.Do(fmt.Sprintf("if err := %s(&in.$.name$, &out.$.name$, s); err != nil {\n", funcName), args) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I take back what I said in the other PR now that I see this-- adding function name to args is better than nesting two templating systems :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we could make Do optionally take a list of pair of parameters, key + value, and have it construct a combined map? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah - that sounds good. Although, since I already did that, I will just add a TODO to fix that in the future :) |
||
} else { | ||
sw.Do("// TODO: Inefficient conversion - can we improve it?\n", nil) | ||
sw.Do("if err := s.Convert(in.$.name$, &out.$.name$, 0); err != nil {\n", args) | ||
sw.Do("if err := s.Convert(&in.$.name$, &out.$.name$, 0); err != nil {\n", args) | ||
} | ||
sw.Do("return err\n", nil) | ||
sw.Do("}\n", nil) | ||
case types.Alias: | ||
if outMember.Type.IsAssignable() { | ||
sw.Do("out.$.name$ = $.outType|raw$(in.$.name$)\n", args) | ||
} else { | ||
if function, ok := g.preexists(m.Type, outMember.Type); ok { | ||
args["function"] = function | ||
sw.Do("if err := $.function|raw$(in.$.name$, &out.$.name$, s); err != nil {\n", args) | ||
} else if g.convertibleOnlyWithinPackage(m.Type, outMember.Type) { | ||
if g.convertibleOnlyWithinPackage(m.Type, outMember.Type) { | ||
funcName := g.funcNameTmpl(m.Type, outMember.Type) | ||
sw.Do(fmt.Sprintf("if err := %s(in.$.name$, &out.$.name$, c); err != nil {\n", funcName), args) | ||
sw.Do(fmt.Sprintf("if err := %s(&in.$.name$, &out.$.name$, s); err != nil {\n", funcName), args) | ||
} else { | ||
sw.Do("// TODO: Inefficient conversion - can we improve it?\n", nil) | ||
sw.Do("if err := s.Convert(in.$.name$, &out.$.name$, 0); err != nil {\n", args) | ||
sw.Do("if err := s.Convert(&in.$.name$, &out.$.name$, 0); err != nil {\n", args) | ||
} | ||
sw.Do("return err\n", nil) | ||
sw.Do("}\n", nil) | ||
} | ||
default: | ||
if function, ok := g.preexists(m.Type, outMember.Type); ok { | ||
args["function"] = function | ||
sw.Do("if err := $.function|raw$(in.$.name$, &out.$.name$, s); err != nil {\n", args) | ||
} else if g.convertibleOnlyWithinPackage(m.Type, outMember.Type) { | ||
if g.convertibleOnlyWithinPackage(m.Type, outMember.Type) { | ||
funcName := g.funcNameTmpl(m.Type, outMember.Type) | ||
sw.Do(fmt.Sprintf("if err := %s(in.$.name$, &out.$.name$, c); err != nil {\n", funcName), args) | ||
sw.Do(fmt.Sprintf("if err := %s(&in.$.name$, &out.$.name$, s); err != nil {\n", funcName), args) | ||
} else { | ||
sw.Do("// TODO: Inefficient conversion - can we improve it?\n", nil) | ||
sw.Do("if err := s.Convert(in.$.name$, &out.$.name$, 0); err != nil {\n", args) | ||
sw.Do("if err := s.Convert(&in.$.name$, &out.$.name$, 0); err != nil {\n", args) | ||
} | ||
sw.Do("return err\n", nil) | ||
sw.Do("}\n", nil) | ||
|
@@ -584,22 +616,22 @@ func (g *genConversion) doStruct(inType, outType *types.Type, sw *generator.Snip | |
} | ||
|
||
func (g *genConversion) doPointer(inType, outType *types.Type, sw *generator.SnippetWriter) { | ||
sw.Do("*out = new($.Elem|raw$)\n", outType) | ||
sw.Do("out = new($.Elem|raw$)\n", outType) | ||
if outType.Elem.IsAssignable() { | ||
if inType.Elem == outType.Elem { | ||
sw.Do("**out = *in\n", nil) | ||
sw.Do("*out = *in\n", nil) | ||
} else { | ||
sw.Do("**out = $.|raw$(*in)\n", outType.Elem) | ||
sw.Do("*out = $.|raw$(*in)\n", outType.Elem) | ||
} | ||
} else { | ||
if function, ok := g.preexists(inType.Elem, outType.Elem); ok { | ||
sw.Do("if err := $.|raw$(*in, out, s); err != nil {\n", function) | ||
sw.Do("if err := $.|raw$(in, out, s); err != nil {\n", function) | ||
} else if g.convertibleOnlyWithinPackage(inType.Elem, outType.Elem) { | ||
funcName := g.funcNameTmpl(inType.Elem, outType.Elem) | ||
sw.Do(fmt.Sprintf("if err := %s(*in, out, c); err != nil {\n", funcName), argsFromType(inType.Elem, outType.Elem)) | ||
sw.Do(fmt.Sprintf("if err := %s(in, out, s); err != nil {\n", funcName), argsFromType(inType.Elem, outType.Elem)) | ||
} else { | ||
sw.Do("// TODO: Inefficient conversion - can we improve it?\n", nil) | ||
sw.Do("if err := s.Convert(*in, out, 0); err != nil {\n", nil) | ||
sw.Do("if err := s.Convert(in, out, 0); err != nil {\n", nil) | ||
} | ||
sw.Do("return err\n", nil) | ||
sw.Do("}\n", nil) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nifty!