Skip to content

Commit

Permalink
fix: Respect AllowNullPropertyAssignment correctly when mapping code …
Browse files Browse the repository at this point in the history
…is not a direct assignment (#939)
  • Loading branch information
latonz authored Nov 28, 2023
1 parent 825a853 commit cac2f2e
Show file tree
Hide file tree
Showing 15 changed files with 308 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Riok.Mapperly.Descriptors.Mappings.MemberMappings;
using Riok.Mapperly.Diagnostics;
using Riok.Mapperly.Helpers;
using Riok.Mapperly.Symbols;

namespace Riok.Mapperly.Descriptors.MappingBodyBuilders.BuilderContext;
Expand All @@ -22,6 +23,13 @@ public void AddNullDelegateMemberAssignmentMapping(IMemberAssignmentMapping memb
var nullConditionSourcePath = new MemberPath(memberMapping.SourcePath.PathWithoutTrailingNonNullable().ToList());
var container = GetOrCreateNullDelegateMappingForPath(nullConditionSourcePath);
AddMemberAssignmentMapping(container, memberMapping);

// set target member to null if null assignments are allowed
// and the source is null
if (BuilderContext.MapperConfiguration.AllowNullPropertyAssignment && memberMapping.TargetPath.Member.Type.IsNullable())
{
container.AddNullMemberAssignment(SetterMemberPath.Build(BuilderContext, memberMapping.TargetPath));
}
}

private void AddMemberAssignmentMapping(IMemberAssignmentMappingContainer container, IMemberAssignmentMapping mapping)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ bool needsNullSafeAccess
{
private readonly GetterMemberPath _nullConditionalSourcePath = nullConditionalSourcePath;
private readonly bool _throwInsteadOfConditionalNullMapping = throwInsteadOfConditionalNullMapping;
private readonly List<SetterMemberPath> _targetsToSetNull = new();

public override IEnumerable<StatementSyntax> Build(TypeMappingBuildContext ctx, ExpressionSyntax targetAccess)
{
Expand All @@ -26,17 +27,16 @@ public override IEnumerable<StatementSyntax> Build(TypeMappingBuildContext ctx,
// else
// throw ...
var sourceNullConditionalAccess = _nullConditionalSourcePath.BuildAccess(ctx.Source, false, needsNullSafeAccess, true);
var nameofSourceAccess = _nullConditionalSourcePath.BuildAccess(ctx.Source, false, false, true);
var condition = IsNotNull(sourceNullConditionalAccess);
var conditionCtx = ctx.AddIndentation();
var trueClause = base.Build(conditionCtx, targetAccess);
var elseClause = _throwInsteadOfConditionalNullMapping
? new[] { conditionCtx.SyntaxFactory.ExpressionStatement(ThrowArgumentNullException(nameofSourceAccess)) }
: null;
var elseClause = BuildElseClause(conditionCtx, targetAccess);
var ifExpression = ctx.SyntaxFactory.If(condition, trueClause, elseClause);
return new[] { ifExpression };
}

public void AddNullMemberAssignment(SetterMemberPath targetPath) => _targetsToSetNull.Add(targetPath);

public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj))
Expand Down Expand Up @@ -70,4 +70,19 @@ protected bool Equals(MemberNullDelegateAssignmentMapping other)
return _nullConditionalSourcePath.Equals(other._nullConditionalSourcePath)
&& _throwInsteadOfConditionalNullMapping == other._throwInsteadOfConditionalNullMapping;
}

private IEnumerable<StatementSyntax>? BuildElseClause(TypeMappingBuildContext ctx, ExpressionSyntax targetAccess)
{
if (_throwInsteadOfConditionalNullMapping)
{
// throw new ArgumentNullException
var nameofSourceAccess = _nullConditionalSourcePath.BuildAccess(ctx.Source, false, false, true);
return new[] { ctx.SyntaxFactory.ExpressionStatement(ThrowArgumentNullException(nameofSourceAccess)) };
}

// target.A = null;
return _targetsToSetNull.Count == 0
? null
: _targetsToSetNull.Select(x => ctx.SyntaxFactory.ExpressionStatement(x.BuildAssignment(targetAccess, NullLiteral())));
}
}
8 changes: 4 additions & 4 deletions src/Riok.Mapperly/Symbols/SetterMemberPath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ private static (IMappableMember, bool) BuildMemberSetter(MappingBuilderContext c
return (new MethodAccessorMember(member, unsafeGetAccessor.MethodName, methodRequiresParameter: true), true);
}

public ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, ExpressionSyntax sourceValue, bool coalesceAssignment = false)
public ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, ExpressionSyntax valueToAssign, bool coalesceAssignment = false)
{
IEnumerable<IMappableMember> path = Path;

Expand All @@ -73,16 +73,16 @@ public ExpressionSyntax BuildAssignment(ExpressionSyntax? baseAccess, Expression
Debug.Assert(!IsMethod);

// target.Value ??= mappedValue;
return CoalesceAssignment(memberPath, sourceValue);
return CoalesceAssignment(memberPath, valueToAssign);
}

if (IsMethod)
{
// target.SetValue(source.Value);
return Invocation(memberPath, sourceValue);
return Invocation(memberPath, valueToAssign);
}

// target.Value = source.Value;
return Assignment(memberPath, sourceValue);
return Assignment(memberPath, valueToAssign);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ public static partial class CircularReferenceMapper
{
target.Parent = MapToCircularReferenceDto(source.Parent, refHandler);
}
else
{
target.Parent = null;
}
target.Value = source.Value;
return target;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,66 @@ public static partial class DeepCloningMapper
{
target.NullableFlattening = Copy(src.NullableFlattening);
}
else
{
target.NullableFlattening = null;
}
if (src.NestedNullable != null)
{
target.NestedNullable = MapToTestObjectNested(src.NestedNullable);
}
else
{
target.NestedNullable = null;
}
if (src.NestedNullableTargetNotNullable != null)
{
target.NestedNullableTargetNotNullable = MapToTestObjectNested(src.NestedNullableTargetNotNullable);
}
else
{
target.NestedNullableTargetNotNullable = null;
}
if (src.TupleValue != null)
{
target.TupleValue = MapToValueTuple(src.TupleValue.Value);
}
else
{
target.TupleValue = null;
}
if (src.RecursiveObject != null)
{
target.RecursiveObject = Copy(src.RecursiveObject);
}
else
{
target.RecursiveObject = null;
}
if (src.SourceTargetSameObjectType != null)
{
target.SourceTargetSameObjectType = Copy(src.SourceTargetSameObjectType);
}
else
{
target.SourceTargetSameObjectType = null;
}
if (src.NullableReadOnlyObjectCollection != null)
{
target.NullableReadOnlyObjectCollection = MapToIReadOnlyCollection(src.NullableReadOnlyObjectCollection);
}
else
{
target.NullableReadOnlyObjectCollection = null;
}
if (src.SubObject != null)
{
target.SubObject = MapToInheritanceSubObject(src.SubObject);
}
else
{
target.SubObject = null;
}
target.IntValue = src.IntValue;
target.StringValue = src.StringValue;
target.RenamedStringValue = src.RenamedStringValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ public partial int ParseableInt(string value)
{
target.NullableFlatteningIdValue = CastIntNullable(testObject.NullableFlattening.IdValue);
}
else
{
target.NullableFlatteningIdValue = null;
}
if (testObject.NullableUnflatteningIdValue != null)
{
target.NullableUnflattening ??= new();
Expand All @@ -65,6 +69,10 @@ public partial int ParseableInt(string value)
target.NestedNullableIntValue = DirectInt(testObject.NestedNullable.IntValue);
target.NestedNullable = MapToTestObjectNestedDto(testObject.NestedNullable);
}
else
{
target.NestedNullable = null;
}
if (testObject.NestedNullableTargetNotNullable != null)
{
target.NestedNullableTargetNotNullable = MapToTestObjectNestedDto(testObject.NestedNullableTargetNotNullable);
Expand All @@ -77,18 +85,34 @@ public partial int ParseableInt(string value)
{
target.TupleValue = MapToValueTuple(testObject.TupleValue.Value);
}
else
{
target.TupleValue = null;
}
if (testObject.RecursiveObject != null)
{
target.RecursiveObject = MapToDto(testObject.RecursiveObject);
}
else
{
target.RecursiveObject = null;
}
if (testObject.NullableReadOnlyObjectCollection != null)
{
target.NullableReadOnlyObjectCollection = MapToTestObjectNestedDtoArray(testObject.NullableReadOnlyObjectCollection);
}
else
{
target.NullableReadOnlyObjectCollection = null;
}
if (testObject.SubObject != null)
{
target.SubObject = MapToInheritanceSubObjectDto(testObject.SubObject);
}
else
{
target.SubObject = null;
}
target.IntValue = DirectInt(testObject.IntValue);
target.StringValue = testObject.StringValue;
target.RenamedStringValue2 = testObject.RenamedStringValue;
Expand Down Expand Up @@ -154,26 +178,50 @@ public partial int ParseableInt(string value)
{
target.NullableUnflatteningIdValue = CastIntNullable(dto.NullableUnflattening.IdValue);
}
else
{
target.NullableUnflatteningIdValue = null;
}
if (dto.NestedNullable != null)
{
target.NestedNullable = MapToTestObjectNested(dto.NestedNullable);
}
else
{
target.NestedNullable = null;
}
if (dto.TupleValue != null)
{
target.TupleValue = MapToValueTuple1(dto.TupleValue.Value);
}
else
{
target.TupleValue = null;
}
if (dto.RecursiveObject != null)
{
target.RecursiveObject = MapFromDto(dto.RecursiveObject);
}
else
{
target.RecursiveObject = null;
}
if (dto.NullableReadOnlyObjectCollection != null)
{
target.NullableReadOnlyObjectCollection = MapToIReadOnlyCollection(dto.NullableReadOnlyObjectCollection);
}
else
{
target.NullableReadOnlyObjectCollection = null;
}
if (dto.SubObject != null)
{
target.SubObject = MapToInheritanceSubObject(dto.SubObject);
}
else
{
target.SubObject = null;
}
target.IntValue = DirectInt(dto.IntValue);
target.StringValue = dto.StringValue;
target.UnflatteningIdValue = DirectInt(dto.Unflattening.IdValue);
Expand Down Expand Up @@ -229,11 +277,19 @@ public partial void UpdateDto(global::Riok.Mapperly.IntegrationTests.Models.Test
{
target.NullableFlatteningIdValue = CastIntNullable(source.NullableFlattening.IdValue);
}
else
{
target.NullableFlatteningIdValue = null;
}
if (source.NestedNullable != null)
{
target.NestedNullableIntValue = DirectInt(source.NestedNullable.IntValue);
target.NestedNullable = MapToTestObjectNestedDto(source.NestedNullable);
}
else
{
target.NestedNullable = null;
}
if (source.NestedNullableTargetNotNullable != null)
{
target.NestedNullableTargetNotNullable = MapToTestObjectNestedDto(source.NestedNullableTargetNotNullable);
Expand All @@ -246,18 +302,34 @@ public partial void UpdateDto(global::Riok.Mapperly.IntegrationTests.Models.Test
{
target.TupleValue = MapToValueTuple(source.TupleValue.Value);
}
else
{
target.TupleValue = null;
}
if (source.RecursiveObject != null)
{
target.RecursiveObject = MapToDto(source.RecursiveObject);
}
else
{
target.RecursiveObject = null;
}
if (source.NullableReadOnlyObjectCollection != null)
{
target.NullableReadOnlyObjectCollection = MapToTestObjectNestedDtoArray(source.NullableReadOnlyObjectCollection);
}
else
{
target.NullableReadOnlyObjectCollection = null;
}
if (source.SubObject != null)
{
target.SubObject = MapToInheritanceSubObjectDto(source.SubObject);
}
else
{
target.SubObject = null;
}
target.CtorValue = DirectInt(source.CtorValue);
target.CtorValue2 = DirectInt(source.CtorValue2);
target.IntValue = DirectInt(source.IntValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ public static partial class ProjectionMapper
target.NestedNullableIntValue = testObject.NestedNullable.IntValue;
target.NestedNullable = MapToTestObjectNestedDto(testObject.NestedNullable);
}
else
{
target.NestedNullable = null;
}
if (testObject.NestedNullableTargetNotNullable != null)
{
target.NestedNullableTargetNotNullable = MapToTestObjectNestedDto(testObject.NestedNullableTargetNotNullable);
Expand All @@ -90,10 +94,18 @@ public static partial class ProjectionMapper
{
target.NullableReadOnlyObjectCollection = MapToIReadOnlyCollection(testObject.NullableReadOnlyObjectCollection);
}
else
{
target.NullableReadOnlyObjectCollection = null;
}
if (testObject.SubObject != null)
{
target.SubObject = MapToInheritanceSubObjectDto(testObject.SubObject);
}
else
{
target.SubObject = null;
}
target.IntValue = testObject.IntValue;
target.StringValue = testObject.StringValue;
target.RenamedStringValue2 = testObject.RenamedStringValue;
Expand Down
Loading

0 comments on commit cac2f2e

Please sign in to comment.