Skip to content

Commit

Permalink
fix: prevent crash when object to object mapping (#340)
Browse files Browse the repository at this point in the history
  • Loading branch information
TimothyMakkison authored Apr 17, 2023
1 parent a8b7a50 commit b5f4559
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/Riok.Mapperly/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,5 @@ Rule ID | Category | Severity | Notes
RMG029 | Mapper | Error | Queryable projection mappings do not support reference handling
RMG030 | Mapper | Error | Reference loop detected while mapping to an init only property
RMG031 | Mapper | Warning | Reference loop detected while mapping to a constructor property
RMG032 | Mapper | Warning | The enum mapping strategy ByName cannot be used in projection mappings
RMG032 | Mapper | Warning | The enum mapping strategy ByName cannot be used in projection mappings
RMG033 | Mapper | Info | Object mapped to another object without deep clone
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.CodeAnalysis;
using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Descriptors.Mappings;
using Riok.Mapperly.Diagnostics;

namespace Riok.Mapperly.Descriptors.MappingBuilders;

Expand All @@ -13,10 +14,18 @@ public static class SpecialTypeMappingBuilder

return ctx.Target.SpecialType switch
{
SpecialType.System_Object when ctx.MapperConfiguration.UseDeepCloning && ctx.Source.SpecialType == SpecialType.System_Object
=> BuildDeepCloneObjectToObjectMapping(ctx),
SpecialType.System_Object when ctx.MapperConfiguration.UseDeepCloning
=> new CastMapping(ctx.Source, ctx.Target, ctx.FindOrBuildMapping(ctx.Source, ctx.Source)),
SpecialType.System_Object => new CastMapping(ctx.Source, ctx.Target),
_ => null,
};
}

private static DirectAssignmentMapping BuildDeepCloneObjectToObjectMapping(MappingBuilderContext ctx)
{
ctx.ReportDiagnostic(DiagnosticDescriptors.MappedObjectToObjectWithoutDeepClone, ctx.Source.Name, ctx.Target.Name);
return new DirectAssignmentMapping(ctx.Source);
}
}
8 changes: 8 additions & 0 deletions src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,4 +261,12 @@ internal static class DiagnosticDescriptors
DiagnosticCategories.Mapper,
DiagnosticSeverity.Warning,
true);

public static readonly DiagnosticDescriptor MappedObjectToObjectWithoutDeepClone = new DiagnosticDescriptor(
"RMG033",
"Object mapped to another object without deep clone",
"Object mapped to another object without deep clone, consider implementing the mapping manually",
DiagnosticCategories.Mapper,
DiagnosticSeverity.Info,
true);
}
28 changes: 28 additions & 0 deletions test/Riok.Mapperly.Tests/Mapping/SpecialTypeTest.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Riok.Mapperly.Diagnostics;

namespace Riok.Mapperly.Tests.Mapping;

public class SpecialTypeTest
Expand Down Expand Up @@ -27,6 +29,32 @@ public void ClassToObjectDeepCloning()
.HaveMapMethodBody("return (object)MapToA(source);");
}

[Fact]
public void ObjectToObjectDeepCloning()
{
var source = TestSourceBuilder.Mapping(
"object",
"object",
TestSourceBuilderOptions.WithDeepCloning);
TestHelper.GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
.Should()
.HaveMapMethodBody("return source;")
.HaveDiagnostic(new(DiagnosticDescriptors.MappedObjectToObjectWithoutDeepClone));
}

[Fact]
public void NullableObjectToNullableObjectDeepCloning()
{
var source = TestSourceBuilder.Mapping(
"object?",
"object?",
TestSourceBuilderOptions.WithDeepCloning);
TestHelper.GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
.Should()
.HaveMapMethodBody("return source == null ? default : source;")
.HaveDiagnostic(new(DiagnosticDescriptors.MappedObjectToObjectWithoutDeepClone));
}

[Fact]
public void StringToObject()
{
Expand Down

0 comments on commit b5f4559

Please sign in to comment.