From 2ffa961a907c82cd6673258d85eed9d3f4447da6 Mon Sep 17 00:00:00 2001 From: Jannis Mattheis Date: Thu, 22 Feb 2024 19:23:18 +0100 Subject: [PATCH] fix: panic with type params --- config/method.go | 2 ++ docs/changelog.md | 2 ++ generator/generator.go | 4 ++-- method/definition.go | 1 + method/parse.go | 12 +++++++++--- scenario/extend_generic.yml | 24 ++++++++++++++++++++++++ scenario/map_custom_generic.yml | 31 +++++++++++++++++++++++++++++++ xtype/type.go | 2 ++ 8 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 scenario/extend_generic.yml create mode 100644 scenario/map_custom_generic.yml diff --git a/config/method.go b/config/method.go index 9db813c..f175758 100644 --- a/config/method.go +++ b/config/method.go @@ -87,6 +87,7 @@ func parseMethodLine(ctx *context, c *Converter, m *Method, value string) (err e OutputPackagePath: c.OutputPackagePath, Converter: c.Type, Params: method.ParamsOptional, + AllowTypeParams: true, } f.Function, err = ctx.Loader.GetOne(c.Package, custom, opts) } @@ -129,6 +130,7 @@ func parseMethodLine(ctx *context, c *Converter, m *Method, value string) (err e OutputPackagePath: c.OutputPackagePath, Converter: c.Type, Params: method.ParamsOptional, + AllowTypeParams: true, } m.Constructor, err = ctx.Loader.GetOne(c.Package, rest, opts) default: diff --git a/docs/changelog.md b/docs/changelog.md index b695b3e..668d205 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -11,6 +11,8 @@ import GH from './GH.vue'; - Add [`wrapErrorsUsing`](./reference/wrapErrorsUsing.md) - Add [`enum`](./reference/enum.md), See [Guide: Enums](guide/enum.md) - Fix error messages when there is an return error mismatch +- Fix panic when using type params in [`extend`](./reference/extend), + [`map`](./reference/map) or [`default`](./reference/default). _internals_: diff --git a/generator/generator.go b/generator/generator.go index e42d4ef..5c8e17c 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -192,13 +192,13 @@ func (g *generator) CallMethod( if definition.Source != nil { params = append(params, sourceID.Code.Clone()) - if !source.AssignableTo(definition.Source) { + if !source.AssignableTo(definition.Source) && !definition.TypeParams { cause := fmt.Sprintf("Method source type mismatches with conversion source: %s != %s", definition.Source.String, source.String) return nil, nil, formatErr(cause) } } - if !definition.Target.AssignableTo(target) { + if !definition.Target.AssignableTo(target) && !definition.TypeParams { cause := fmt.Sprintf("Method return type mismatches with target: %s != %s", definition.Target.String, target.String) return nil, nil, formatErr(cause) } diff --git a/method/definition.go b/method/definition.go index 211c06d..a94e04d 100644 --- a/method/definition.go +++ b/method/definition.go @@ -20,6 +20,7 @@ func (m *Definition) Signature() xtype.Signature { type Parameters struct { ReturnError bool SelfAsFirstParameter bool + TypeParams bool Source *xtype.Type Target *xtype.Type } diff --git a/method/parse.go b/method/parse.go index bdf5142..142d095 100644 --- a/method/parse.go +++ b/method/parse.go @@ -20,9 +20,10 @@ type ParseOpts struct { Converter types.Type OutputPackagePath string - ErrorPrefix string - ConvFunction bool - Params ParamType + ErrorPrefix string + Params ParamType + ConvFunction bool + AllowTypeParams bool } // Parse parses an function into a Definition. @@ -62,10 +63,15 @@ func Parse(obj types.Object, opts *ParseOpts) (*Definition, error) { Parameters: Parameters{ ReturnError: returnError, Target: xtype.TypeOf(sig.Results().At(0).Type()), + TypeParams: sig.TypeParams().Len() > 0, }, Name: fn.Name(), } + if methodDef.TypeParams && !opts.AllowTypeParams { + return nil, formatErr("must not be generic") + } + if opts.ConvFunction { methodDef.Call = jen.Id(xtype.ThisVar).Dot(fn.Name()) } else { diff --git a/scenario/extend_generic.yml b/scenario/extend_generic.yml new file mode 100644 index 0000000..5161d25 --- /dev/null +++ b/scenario/extend_generic.yml @@ -0,0 +1,24 @@ +input: + input.go: | + package structs + + // goverter:converter + // goverter:extend Extend + type Converter interface { + Convert(source Input) (Output, error) + } + + func Extend[T any](T) int { + return 0 + } + type Input struct { Age string } + type Output struct { Age int } +error: |- + error parsing 'goverter:extend' at + @workdir/input.go:5:1 + github.com/jmattheis/goverter/execution.Converter + + error parsing type: + func github.com/jmattheis/goverter/execution.Extend[T any](T) int + + must not be generic diff --git a/scenario/map_custom_generic.yml b/scenario/map_custom_generic.yml new file mode 100644 index 0000000..06acdf5 --- /dev/null +++ b/scenario/map_custom_generic.yml @@ -0,0 +1,31 @@ +input: + input.go: | + package structs + + // goverter:converter + type Converter interface { + // goverter:map ID | ZeroIfNil + Convert(source Input) Output + } + + type Input struct { ID *int } + type Output struct { ID int } + func ZeroIfNil[T any](*T) T { + var t T + return t + } +success: + - generated/generated.go: | + // Code generated by github.com/jmattheis/goverter, DO NOT EDIT. + + package generated + + import execution "github.com/jmattheis/goverter/execution" + + type ConverterImpl struct{} + + func (c *ConverterImpl) Convert(source execution.Input) execution.Output { + var structsOutput execution.Output + structsOutput.ID = execution.ZeroIfNil(source.ID) + return structsOutput + } diff --git a/xtype/type.go b/xtype/type.go index 05a0276..f552feb 100644 --- a/xtype/type.go +++ b/xtype/type.go @@ -245,6 +245,8 @@ func TypeOf(t types.Type) *Type { case *types.Signature: rt.Signature = true rt.SignatureType = value + case *types.TypeParam: + // ignore default: panic("unknown types.Type " + t.String()) }