Skip to content

Commit

Permalink
allow assignments of MapValue constant value types to nullable value …
Browse files Browse the repository at this point in the history
…types (#1536)
  • Loading branch information
latonz authored Oct 14, 2024
1 parent d573ed4 commit e26778b
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ private static bool TryBuildConstantSourceValue(
return true;
}

if (!SymbolEqualityComparer.Default.Equals(value.ConstantValue.Type, memberMappingInfo.TargetMember.MemberType))
// use non-nullable target type to allow non-null value type assignments
// to nullable value types
if (!SymbolEqualityComparer.Default.Equals(value.ConstantValue.Type, memberMappingInfo.TargetMember.MemberType.NonNullable()))
{
ctx.BuilderContext.ReportDiagnostic(
DiagnosticDescriptors.MapValueTypeMismatch,
Expand All @@ -135,7 +137,7 @@ private static bool TryBuildConstantSourceValue(
// expand enum member access to fully qualified identifier
// use simple member name approach instead of slower visitor pattern on the expression
var enumMemberName = ((MemberAccessExpressionSyntax)value.Expression).Name.Identifier.Text;
var enumTypeFullName = FullyQualifiedIdentifier(memberMappingInfo.TargetMember.MemberType);
var enumTypeFullName = FullyQualifiedIdentifier(memberMappingInfo.TargetMember.MemberType.NonNullable());
sourceValue = new ConstantSourceValue(MemberAccess(enumTypeFullName, enumMemberName));
return true;
case TypedConstantKind.Type:
Expand Down Expand Up @@ -179,8 +181,10 @@ private static bool ValidateValueProviderMethod(IMembersBuilderContext<IMapping>
return false;
}

// use non-nullable target type to allow non-null value type assignments
// to nullable value types
var methodCandidates = namedMethodCandidates.Where(x =>
SymbolEqualityComparer.Default.Equals(x.ReturnType, memberMappingInfo.TargetMember.MemberType)
SymbolEqualityComparer.Default.Equals(x.ReturnType, memberMappingInfo.TargetMember.MemberType.NonNullable())
);

if (!memberMappingInfo.TargetMember.Member.IsNullable)
Expand Down
50 changes: 50 additions & 0 deletions test/Riok.Mapperly.Tests/Mapping/ObjectPropertyValueMethodTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,56 @@ public void MethodReturnTypeNullMismatchShouldDiagnostic()
);
}

[Fact]
public void MethodReturnTypeNonNullableToNullable()
{
var source = TestSourceBuilder.MapperWithBodyAndTypes(
"""
[MapValue("Value", Use = nameof(BuildC))] partial B Map(A source);
C BuildC() => new C();
""",
"class A;",
"class B { public C? Value { get; set; } }",
"class C;"
);

TestHelper
.GenerateMapper(source)
.Should()
.HaveSingleMethodBody(
"""
var target = new global::B();
target.Value = BuildC();
return target;
"""
);
}

[Fact]
public void MethodReturnValueTypeNonNullableToNullable()
{
var source = TestSourceBuilder.MapperWithBodyAndTypes(
"""
[MapValue("Value", Use = nameof(BuildC))] partial B Map(A source);
C BuildC() => C.C1;
""",
"class A;",
"class B { public C? Value { get; set; } }",
"enum C { C1 };"
);

TestHelper
.GenerateMapper(source)
.Should()
.HaveSingleMethodBody(
"""
var target = new global::B();
target.Value = BuildC();
return target;
"""
);
}

[Fact]
public void MethodReturnTypeInDisabledNullableContext()
{
Expand Down
45 changes: 45 additions & 0 deletions test/Riok.Mapperly.Tests/Mapping/ObjectPropertyValueTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,27 @@ public void ExplicitNullToValuePropertyShouldDiagnostic()
);
}

[Fact]
public void IntToNullableValueProperty()
{
var source = TestSourceBuilder.MapperWithBodyAndTypes(
"""[MapValue("IntValue", 1)] partial B Map(A source);""",
"class A;",
"class B { public int? IntValue { get; set; } }"
);

TestHelper
.GenerateMapper(source)
.Should()
.HaveSingleMethodBody(
"""
var target = new global::B();
target.IntValue = 1;
return target;
"""
);
}

[Fact]
public void ExplicitNullToNullableValueProperty()
{
Expand Down Expand Up @@ -651,6 +672,30 @@ public void MapValueDuplicateForSameTargetMemberShouldDiagnostic()
);
}

[Fact]
public void EnumToNestedNullableProperty()
{
var source = TestSourceBuilder.MapperWithBodyAndTypes(
"""[MapValue("Nested.Value", E.E1)] partial B Map(A source);""",
"class A;",
"class B { public C? Nested { get; set; } }",
"class C { public E? Value { get; set; } }",
"enum E { E1 }"
);

TestHelper
.GenerateMapper(source)
.Should()
.HaveSingleMethodBody(
"""
var target = new global::B();
target.Nested ??= new global::C();
target.Nested.Value = global::E.E1;
return target;
"""
);
}

[Fact]
public void MapValueAndPropertyAttributeForSameTargetShouldDiagnostic()
{
Expand Down

0 comments on commit e26778b

Please sign in to comment.