Skip to content

Commit

Permalink
fix: add modifying nested struct diagnostic
Browse files Browse the repository at this point in the history
  • Loading branch information
TimothyMakkison committed Jul 28, 2023
1 parent 5d4596a commit cd933e7
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/Riok.Mapperly/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,4 @@ RMG043 | Mapper | Warning | Enum fallback values are only supported for the
RMG044 | Mapper | Warning | An ignored enum member can not be found on the source enum
RMG045 | Mapper | Warning | An ignored enum member can not be found on the target enum
RMG046 | Mapper | Error | The used C# language version is not supported by Mapperly, Mapperly requires at least C# 9.0
RMG047 | Mapper | Error | Cannot map to member path due to modifying a temporary value
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,28 @@ public static bool ValidateMappingSpecification(
return false;
}

// cannot assign to intermediate value type
// invalid mapping when second to last member is a property that returns a value type
if (targetMemberPath.Path.Count > 1)
{
var member = targetMemberPath.Path[targetMemberPath.Path.Count - 2];
if (member is PropertyMember { Type: { IsValueType: true, IsRefLikeType: false } })
{
ctx.BuilderContext.ReportDiagnostic(
DiagnosticDescriptors.CannotMapToTemporarySourceMember,
ctx.Mapping.SourceType,
sourceMemberPath.FullName,
sourceMemberPath.Member.Type,
ctx.Mapping.TargetType,
targetMemberPath.FullName,
targetMemberPath.Member.Type,
member.Name,
member.Type
);
return false;
}
}

// a target member path part is init only
var noInitOnlyPath = allowInitOnlyMember ? targetMemberPath.ObjectPath : targetMemberPath.Path;
if (noInitOnlyPath.Any(p => p.IsInitOnly))
Expand Down
9 changes: 9 additions & 0 deletions src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -410,4 +410,13 @@ internal static class DiagnosticDescriptors
DiagnosticSeverity.Error,
true
);

public static readonly DiagnosticDescriptor CannotMapToTemporarySourceMember = new DiagnosticDescriptor(
"RMG047",
"Cannot map to member path due to modifying a temporary value",
"Cannot map from member {0}.{1} of type {2} to member path {3}.{4} of type {5} because {6}.{7} is a value type, returning a temporary value",
DiagnosticCategories.Mapper,
DiagnosticSeverity.Error,
true
);
}
30 changes: 30 additions & 0 deletions test/Riok.Mapperly.Tests/Mapping/ObjectPropertyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -443,4 +443,34 @@ public void ShouldIgnoreStaticConstructorAndDiagnostic()
.HaveDiagnostic(DiagnosticDescriptors.CouldNotCreateMapping)
.HaveAssertedAllDiagnostics();
}

[Fact]
public void ModifyingTemporaryStructShouldDiagnostic()
{
var source = TestSourceBuilder.MapperWithBodyAndTypes(
"""
[MapProperty("StringValue", "NestedValue.StringValue")]
partial B Map(A src);
""",
"class A { public string StringValue { get; set; } }",
"class B { public C NestedValue { get; set; } }",
"struct C { public string StringValue { get; set; } }"
);

TestHelper
.GenerateMapper(source, TestHelperOptions.AllowAllDiagnostics)
.Should()
.HaveDiagnostic(DiagnosticDescriptors.SourceMemberNotMapped)
.HaveDiagnostic(
DiagnosticDescriptors.CannotMapToTemporarySourceMember,
"Cannot map from member A.StringValue of type string to member path B.NestedValue.StringValue of type string because NestedValue.C is a value type, returning a temporary value"
)
.HaveAssertedAllDiagnostics()
.HaveSingleMethodBody(
"""
var target = new global::B();
return target;"
"""
);
}
}

0 comments on commit cd933e7

Please sign in to comment.