From 840b6e17555a944912ba332711dbce2d5b66395f Mon Sep 17 00:00:00 2001 From: Lars Date: Mon, 15 Apr 2024 06:05:40 -0600 Subject: [PATCH] fix: expand target type new when inlining in queryable projection mappings (#1238) --- .../Descriptors/InlineExpressionRewriter.cs | 22 +++++++++- ...erTest.SnapshotGeneratedSource.verified.cs | 2 +- ...SnapshotGeneratedSource_NET6_0.verified.cs | 2 +- .../QueryableProjectionUserImplementedTest.cs | 40 +++++++++++++++++-- ...ntedWithTargetTypeNew#Mapper.g.verified.cs | 16 ++++++++ ...getTypeNewInitializer#Mapper.g.verified.cs | 16 ++++++++ ...ntedWithUsingMappings#Mapper.g.verified.cs | 2 +- 7 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 test/Riok.Mapperly.Tests/_snapshots/QueryableProjectionUserImplementedTest.ClassToClassUserImplementedWithTargetTypeNew#Mapper.g.verified.cs create mode 100644 test/Riok.Mapperly.Tests/_snapshots/QueryableProjectionUserImplementedTest.ClassToClassUserImplementedWithTargetTypeNewInitializer#Mapper.g.verified.cs diff --git a/src/Riok.Mapperly/Descriptors/InlineExpressionRewriter.cs b/src/Riok.Mapperly/Descriptors/InlineExpressionRewriter.cs index 60579bdc0d..09a8603121 100644 --- a/src/Riok.Mapperly/Descriptors/InlineExpressionRewriter.cs +++ b/src/Riok.Mapperly/Descriptors/InlineExpressionRewriter.cs @@ -60,13 +60,33 @@ public override SyntaxNode VisitTypeArgumentList(TypeArgumentListSyntax node) if (semanticModel.GetSymbolInfo(node.Expression).Symbol is ITypeSymbol namedTypeSymbol) { var expression = FullyQualifiedIdentifier(namedTypeSymbol).WithTriviaFrom(node.Expression); - return node.WithExpression(expression); } return base.VisitMemberAccessExpression(node); } + public override SyntaxNode VisitImplicitObjectCreationExpression(ImplicitObjectCreationExpressionSyntax node) + { + if ( + semanticModel.GetSymbolInfo(node).Symbol is IMethodSymbol + { + MethodKind: MethodKind.Constructor, + ReceiverType: not null + } ctorSymbol + ) + { + return ObjectCreationExpression( + TrailingSpacedToken(SyntaxKind.NewKeyword), + FullyQualifiedIdentifier(ctorSymbol.ReceiverType), + (ArgumentListSyntax?)base.VisitArgumentList(node.ArgumentList), + node.Initializer == null ? null : (InitializerExpressionSyntax?)base.VisitInitializerExpression(node.Initializer) + ); + } + + return node; + } + public override SyntaxNode VisitArrayType(ArrayTypeSyntax node) { var fullyQualifiedElementType = FullyQualifiedType(node.ElementType); diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/ProjectionMapperTest.SnapshotGeneratedSource.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/ProjectionMapperTest.SnapshotGeneratedSource.verified.cs index 551d6dccd1..3505a5e65c 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/ProjectionMapperTest.SnapshotGeneratedSource.verified.cs +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/ProjectionMapperTest.SnapshotGeneratedSource.verified.cs @@ -44,7 +44,7 @@ public static partial class ProjectionMapper } : default, DateTimeValueTargetDateOnly = global::System.DateOnly.FromDateTime(x.DateTimeValueTargetDateOnly), DateTimeValueTargetTimeOnly = global::System.TimeOnly.FromDateTime(x.DateTimeValueTargetTimeOnly), - ManuallyMapped = new(100) { StringValue = x.ManuallyMapped }, + ManuallyMapped = new global::Riok.Mapperly.IntegrationTests.Dto.TestObjectDtoManuallyMappedProjection(100) { StringValue = x.ManuallyMapped }, ManuallyMappedModified = x.ManuallyMappedModified + 10, ManuallyMappedList = global::System.Linq.Enumerable.ToList(global::System.Linq.Enumerable.Select(x.ManuallyMappedList, x1 => x1.Value)), IntegerValues = global::System.Linq.Enumerable.ToList(global::System.Linq.Enumerable.OrderBy(x.IntegerValues, x => x.Value)), diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/ProjectionMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/ProjectionMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs index 475c5dfe8f..641afe26da 100644 --- a/test/Riok.Mapperly.IntegrationTests/_snapshots/ProjectionMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs +++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/ProjectionMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs @@ -44,7 +44,7 @@ public static partial class ProjectionMapper } : default, DateTimeValueTargetDateOnly = global::System.DateOnly.FromDateTime(x.DateTimeValueTargetDateOnly), DateTimeValueTargetTimeOnly = global::System.TimeOnly.FromDateTime(x.DateTimeValueTargetTimeOnly), - ManuallyMapped = new(100) { StringValue = x.ManuallyMapped }, + ManuallyMapped = new global::Riok.Mapperly.IntegrationTests.Dto.TestObjectDtoManuallyMappedProjection(100) { StringValue = x.ManuallyMapped }, ManuallyMappedModified = x.ManuallyMappedModified + 10, ManuallyMappedList = global::System.Linq.Enumerable.ToList(global::System.Linq.Enumerable.Select(x.ManuallyMappedList, x1 => x1.Value)), IntegerValues = global::System.Linq.Enumerable.ToList(global::System.Linq.Enumerable.OrderBy(x.IntegerValues, x => x.Value)), diff --git a/test/Riok.Mapperly.Tests/Mapping/QueryableProjectionUserImplementedTest.cs b/test/Riok.Mapperly.Tests/Mapping/QueryableProjectionUserImplementedTest.cs index 8846159973..0d4d2c9295 100644 --- a/test/Riok.Mapperly.Tests/Mapping/QueryableProjectionUserImplementedTest.cs +++ b/test/Riok.Mapperly.Tests/Mapping/QueryableProjectionUserImplementedTest.cs @@ -127,9 +127,8 @@ public Task ClassToClassUserImplementedWithUsingMappings() """ private partial System.Linq.IQueryable Map(System.Linq.IQueryable source); - private static DateTimeOffset MapToDateTimeOffset( - DateTime dateTime - ) => new(dateTime, TimeSpan.Zero); + private static DateTimeOffset MapToDateTimeOffset(DateTime dateTime) + => new DateTimeOffset(dateTime, TimeSpan.Zero); """, "class A { public DateTime Value { get; set; } }", "class B { public DateTimeOffset Value { get; set; } }" @@ -137,4 +136,39 @@ DateTime dateTime return TestHelper.VerifyGenerator(source); } + + [Fact] + public Task ClassToClassUserImplementedWithTargetTypeNew() + { + var source = TestSourceBuilder.MapperWithBodyAndTypes( + """ + private partial System.Linq.IQueryable Map(System.Linq.IQueryable source); + + private static DateTimeOffset MapToDateTimeOffset(DateTime dateTime) + => new(dateTime, TimeSpan.Zero); + """, + "class A { public DateTime Value { get; set; } }", + "class B { public DateTimeOffset Value { get; set; } }" + ); + + return TestHelper.VerifyGenerator(source); + } + + [Fact] + public Task ClassToClassUserImplementedWithTargetTypeNewInitializer() + { + var source = TestSourceBuilder.MapperWithBodyAndTypes( + """ + private partial System.Linq.IQueryable Map(System.Linq.IQueryable source); + + private static C MapIt(DateTime dateTime) + => new(1) { Value2 = 10 }; + """, + "class A { public DateTime Value { get; set; } }", + "class B { public C Value { get; set; } }", + "class C(int value1) { public int Value2 { set; } }" + ); + + return TestHelper.VerifyGenerator(source); + } } diff --git a/test/Riok.Mapperly.Tests/_snapshots/QueryableProjectionUserImplementedTest.ClassToClassUserImplementedWithTargetTypeNew#Mapper.g.verified.cs b/test/Riok.Mapperly.Tests/_snapshots/QueryableProjectionUserImplementedTest.ClassToClassUserImplementedWithTargetTypeNew#Mapper.g.verified.cs new file mode 100644 index 0000000000..e05ef2aabe --- /dev/null +++ b/test/Riok.Mapperly.Tests/_snapshots/QueryableProjectionUserImplementedTest.ClassToClassUserImplementedWithTargetTypeNew#Mapper.g.verified.cs @@ -0,0 +1,16 @@ +//HintName: Mapper.g.cs +// +#nullable enable +public partial class Mapper +{ + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + private partial global::System.Linq.IQueryable Map(global::System.Linq.IQueryable source) + { +#nullable disable + return System.Linq.Queryable.Select(source, x => new global::B() + { + Value = new global::System.DateTimeOffset(x.Value, global::System.TimeSpan.Zero), + }); +#nullable enable + } +} \ No newline at end of file diff --git a/test/Riok.Mapperly.Tests/_snapshots/QueryableProjectionUserImplementedTest.ClassToClassUserImplementedWithTargetTypeNewInitializer#Mapper.g.verified.cs b/test/Riok.Mapperly.Tests/_snapshots/QueryableProjectionUserImplementedTest.ClassToClassUserImplementedWithTargetTypeNewInitializer#Mapper.g.verified.cs new file mode 100644 index 0000000000..965d2902ec --- /dev/null +++ b/test/Riok.Mapperly.Tests/_snapshots/QueryableProjectionUserImplementedTest.ClassToClassUserImplementedWithTargetTypeNewInitializer#Mapper.g.verified.cs @@ -0,0 +1,16 @@ +//HintName: Mapper.g.cs +// +#nullable enable +public partial class Mapper +{ + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + private partial global::System.Linq.IQueryable Map(global::System.Linq.IQueryable source) + { +#nullable disable + return System.Linq.Queryable.Select(source, x => new global::B() + { + Value = new global::C(1) { Value2 = 10 }, + }); +#nullable enable + } +} \ No newline at end of file diff --git a/test/Riok.Mapperly.Tests/_snapshots/QueryableProjectionUserImplementedTest.ClassToClassUserImplementedWithUsingMappings#Mapper.g.verified.cs b/test/Riok.Mapperly.Tests/_snapshots/QueryableProjectionUserImplementedTest.ClassToClassUserImplementedWithUsingMappings#Mapper.g.verified.cs index 82339094cf..e05ef2aabe 100644 --- a/test/Riok.Mapperly.Tests/_snapshots/QueryableProjectionUserImplementedTest.ClassToClassUserImplementedWithUsingMappings#Mapper.g.verified.cs +++ b/test/Riok.Mapperly.Tests/_snapshots/QueryableProjectionUserImplementedTest.ClassToClassUserImplementedWithUsingMappings#Mapper.g.verified.cs @@ -9,7 +9,7 @@ public partial class Mapper #nullable disable return System.Linq.Queryable.Select(source, x => new global::B() { - Value = new(x.Value, global::System.TimeSpan.Zero), + Value = new global::System.DateTimeOffset(x.Value, global::System.TimeSpan.Zero), }); #nullable enable }