diff --git a/src/Riok.Mapperly.Abstractions/IgnoreObsoleteMembersStrategy.cs b/src/Riok.Mapperly.Abstractions/IgnoreObsoleteMembersStrategy.cs
new file mode 100644
index 0000000000..ce9d0fbe0d
--- /dev/null
+++ b/src/Riok.Mapperly.Abstractions/IgnoreObsoleteMembersStrategy.cs
@@ -0,0 +1,30 @@
+namespace Riok.Mapperly.Abstractions;
+
+///
+/// Defines the strategy to use when mapping members marked with .
+/// Note that will always map marked members,
+/// even if they are ignored.
+///
+[Flags]
+public enum IgnoreObsoleteMembersStrategy
+{
+ ///
+ /// Maps marked members.
+ ///
+ None = 0,
+
+ ///
+ /// Will not map marked source or target members.
+ ///
+ Both = ~None,
+
+ ///
+ /// Ignores source marked members.
+ ///
+ Source = 1 << 0,
+
+ ///
+ /// Ignores target marked members.
+ ///
+ Target = 1 << 1,
+}
diff --git a/src/Riok.Mapperly.Abstractions/MapperAttribute.cs b/src/Riok.Mapperly.Abstractions/MapperAttribute.cs
index 21cb2f386c..6aa1f5e792 100644
--- a/src/Riok.Mapperly.Abstractions/MapperAttribute.cs
+++ b/src/Riok.Mapperly.Abstractions/MapperAttribute.cs
@@ -70,4 +70,10 @@ public sealed class MapperAttribute : Attribute
/// to keep track of and reuse existing target object instances.
///
public bool UseReferenceHandling { get; set; }
+
+ ///
+ /// The ignore obsolete attribute strategy. Determines how marked members are mapped.
+ /// Defaults to .
+ ///
+ public IgnoreObsoleteMembersStrategy IgnoreObsoleteMembersStrategy { get; set; } = IgnoreObsoleteMembersStrategy.None;
}
diff --git a/src/Riok.Mapperly.Abstractions/MapperIgnoreObsoleteMembersAttribute.cs b/src/Riok.Mapperly.Abstractions/MapperIgnoreObsoleteMembersAttribute.cs
new file mode 100644
index 0000000000..e798fb3956
--- /dev/null
+++ b/src/Riok.Mapperly.Abstractions/MapperIgnoreObsoleteMembersAttribute.cs
@@ -0,0 +1,22 @@
+namespace Riok.Mapperly.Abstractions;
+
+///
+/// Specifies options for obsolete ignoring strategy.
+///
+[AttributeUsage(AttributeTargets.Method)]
+public sealed class MapperIgnoreObsoleteMembersAttribute : Attribute
+{
+ ///
+ /// Specifies options for obsolete ignoring strategy.
+ ///
+ /// The strategy to be used to map marked members. Defaults to .
+ public MapperIgnoreObsoleteMembersAttribute(IgnoreObsoleteMembersStrategy ignoreObsoleteStrategy = IgnoreObsoleteMembersStrategy.Both)
+ {
+ IgnoreObsoleteStrategy = ignoreObsoleteStrategy;
+ }
+
+ ///
+ /// The strategy used to map marked members.
+ ///
+ public IgnoreObsoleteMembersStrategy IgnoreObsoleteStrategy { get; }
+}
diff --git a/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt b/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt
index 193f4726f1..317c57b6a4 100644
--- a/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt
+++ b/src/Riok.Mapperly.Abstractions/PublicAPI.Shipped.txt
@@ -3,6 +3,11 @@
Riok.Mapperly.Abstractions.EnumMappingStrategy
Riok.Mapperly.Abstractions.EnumMappingStrategy.ByName = 1 -> Riok.Mapperly.Abstractions.EnumMappingStrategy
Riok.Mapperly.Abstractions.EnumMappingStrategy.ByValue = 0 -> Riok.Mapperly.Abstractions.EnumMappingStrategy
+Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy
+Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy.Both = -1 -> Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy
+Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy.None = 0 -> Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy
+Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy.Source = 1 -> Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy
+Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy.Target = 2 -> Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy
Riok.Mapperly.Abstractions.MapEnumAttribute
Riok.Mapperly.Abstractions.MapEnumAttribute.IgnoreCase.get -> bool
Riok.Mapperly.Abstractions.MapEnumAttribute.IgnoreCase.set -> void
@@ -19,6 +24,8 @@ Riok.Mapperly.Abstractions.MapperAttribute.EnumMappingIgnoreCase.get -> bool
Riok.Mapperly.Abstractions.MapperAttribute.EnumMappingIgnoreCase.set -> void
Riok.Mapperly.Abstractions.MapperAttribute.EnumMappingStrategy.get -> Riok.Mapperly.Abstractions.EnumMappingStrategy
Riok.Mapperly.Abstractions.MapperAttribute.EnumMappingStrategy.set -> void
+Riok.Mapperly.Abstractions.MapperAttribute.IgnoreObsoleteMembersStrategy.get -> Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy
+Riok.Mapperly.Abstractions.MapperAttribute.IgnoreObsoleteMembersStrategy.set -> void
Riok.Mapperly.Abstractions.MapperAttribute.MapperAttribute() -> void
Riok.Mapperly.Abstractions.MapperAttribute.PropertyNameMappingStrategy.get -> Riok.Mapperly.Abstractions.PropertyNameMappingStrategy
Riok.Mapperly.Abstractions.MapperAttribute.PropertyNameMappingStrategy.set -> void
@@ -33,6 +40,9 @@ Riok.Mapperly.Abstractions.MapperConstructorAttribute.MapperConstructorAttribute
Riok.Mapperly.Abstractions.MapperIgnoreAttribute
Riok.Mapperly.Abstractions.MapperIgnoreAttribute.MapperIgnoreAttribute(string! target) -> void
Riok.Mapperly.Abstractions.MapperIgnoreAttribute.Target.get -> string!
+Riok.Mapperly.Abstractions.MapperIgnoreObsoleteMembersAttribute
+Riok.Mapperly.Abstractions.MapperIgnoreObsoleteMembersAttribute.IgnoreObsoleteStrategy.get -> Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy
+Riok.Mapperly.Abstractions.MapperIgnoreObsoleteMembersAttribute.MapperIgnoreObsoleteMembersAttribute(Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy ignoreObsoleteStrategy = (Riok.Mapperly.Abstractions.IgnoreObsoleteMembersStrategy)-1) -> void
Riok.Mapperly.Abstractions.MapperIgnoreSourceAttribute
Riok.Mapperly.Abstractions.MapperIgnoreSourceAttribute.MapperIgnoreSourceAttribute(string! source) -> void
Riok.Mapperly.Abstractions.MapperIgnoreSourceAttribute.Source.get -> string!
diff --git a/src/Riok.Mapperly/Configuration/MapperConfiguration.cs b/src/Riok.Mapperly/Configuration/MapperConfiguration.cs
index 9f220ae1a7..ec20e0516e 100644
--- a/src/Riok.Mapperly/Configuration/MapperConfiguration.cs
+++ b/src/Riok.Mapperly/Configuration/MapperConfiguration.cs
@@ -20,7 +20,12 @@ public MapperConfiguration(SymbolAccessor symbolAccessor, ISymbol mapperSymbol)
null,
Array.Empty()
),
- new PropertiesMappingConfiguration(Array.Empty(), Array.Empty(), Array.Empty()),
+ new PropertiesMappingConfiguration(
+ Array.Empty(),
+ Array.Empty(),
+ Array.Empty(),
+ Mapper.IgnoreObsoleteMembersStrategy
+ ),
Array.Empty()
);
}
@@ -58,7 +63,11 @@ private PropertiesMappingConfiguration BuildPropertiesConfig(IMethodSymbol metho
#pragma warning restore CS0618
.ToList();
var explicitMappings = _dataAccessor.Access(method).ToList();
- return new PropertiesMappingConfiguration(ignoredSourceProperties, ignoredTargetProperties, explicitMappings);
+ var ignoreObsolete = _dataAccessor.Access(method).FirstOrDefault() is not { } methodIgnore
+ ? _defaultConfiguration.Properties.IgnoreObsoleteMembersStrategy
+ : methodIgnore.IgnoreObsoleteStrategy;
+
+ return new PropertiesMappingConfiguration(ignoredSourceProperties, ignoredTargetProperties, explicitMappings, ignoreObsolete);
}
private EnumMappingConfiguration BuildEnumConfig(IMethodSymbol method)
diff --git a/src/Riok.Mapperly/Configuration/PropertiesMappingConfiguration.cs b/src/Riok.Mapperly/Configuration/PropertiesMappingConfiguration.cs
index ccd3bca757..f991cc536a 100644
--- a/src/Riok.Mapperly/Configuration/PropertiesMappingConfiguration.cs
+++ b/src/Riok.Mapperly/Configuration/PropertiesMappingConfiguration.cs
@@ -1,7 +1,10 @@
+using Riok.Mapperly.Abstractions;
+
namespace Riok.Mapperly.Configuration;
public record PropertiesMappingConfiguration(
IReadOnlyCollection IgnoredSources,
IReadOnlyCollection IgnoredTargets,
- IReadOnlyCollection ExplicitMappings
+ IReadOnlyCollection ExplicitMappings,
+ IgnoreObsoleteMembersStrategy IgnoreObsoleteMembersStrategy
);
diff --git a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/MembersMappingBuilderContext.cs b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/MembersMappingBuilderContext.cs
index cb09e44455..c635e30708 100644
--- a/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/MembersMappingBuilderContext.cs
+++ b/src/Riok.Mapperly/Descriptors/MappingBodyBuilders/BuilderContext/MembersMappingBuilderContext.cs
@@ -1,3 +1,4 @@
+using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Configuration;
using Riok.Mapperly.Descriptors.Mappings;
using Riok.Mapperly.Diagnostics;
@@ -21,11 +22,17 @@ protected MembersMappingBuilderContext(MappingBuilderContext builderContext, T m
{
BuilderContext = builderContext;
Mapping = mapping;
+ MemberConfigsByRootTargetName = GetMemberConfigurations();
_unmappedSourceMemberNames = GetSourceMemberNames();
TargetMembers = GetTargetMembers();
- IgnoredSourceMemberNames = builderContext.Configuration.Properties.IgnoredSources;
+ IgnoredSourceMemberNames = builderContext.Configuration.Properties.IgnoredSources
+ .Concat(GetIgnoredObsoleteSourceMembers())
+ .ToHashSet();
+ var ignoredTargetMemberNames = builderContext.Configuration.Properties.IgnoredTargets
+ .Concat(GetIgnoredObsoleteTargetMembers())
+ .ToHashSet();
_ignoredUnmatchedSourceMemberNames = InitIgnoredUnmatchedProperties(IgnoredSourceMemberNames, _unmappedSourceMemberNames);
_ignoredUnmatchedTargetMemberNames = InitIgnoredUnmatchedProperties(
@@ -34,9 +41,13 @@ protected MembersMappingBuilderContext(MappingBuilderContext builderContext, T m
);
_unmappedSourceMemberNames.ExceptWith(IgnoredSourceMemberNames);
- TargetMembers.RemoveRange(builderContext.Configuration.Properties.IgnoredTargets);
MemberConfigsByRootTargetName = GetMemberConfigurations();
+
+ // remove explicitly mapped ignored targets from ignoredTargetMemberNames
+ // then remove all ignored targets from TargetMembers, leaving unignored and explicitly mapped ignored members
+ ignoredTargetMemberNames.ExceptWith(MemberConfigsByRootTargetName.Keys);
+ TargetMembers.RemoveRange(ignoredTargetMemberNames);
}
public MappingBuilderContext BuilderContext { get; }
@@ -66,6 +77,32 @@ private HashSet InitIgnoredUnmatchedProperties(IEnumerable allPr
return unmatched;
}
+ private IEnumerable GetIgnoredObsoleteTargetMembers()
+ {
+ var obsoleteStrategy = BuilderContext.Configuration.Properties.IgnoreObsoleteMembersStrategy;
+
+ if (!obsoleteStrategy.HasFlag(IgnoreObsoleteMembersStrategy.Target))
+ return Enumerable.Empty();
+
+ return BuilderContext.SymbolAccessor
+ .GetAllAccessibleMappableMembers(Mapping.TargetType)
+ .Where(x => BuilderContext.SymbolAccessor.HasAttribute(x.MemberSymbol))
+ .Select(x => x.Name);
+ }
+
+ private IEnumerable GetIgnoredObsoleteSourceMembers()
+ {
+ var obsoleteStrategy = BuilderContext.Configuration.Properties.IgnoreObsoleteMembersStrategy;
+
+ if (!obsoleteStrategy.HasFlag(IgnoreObsoleteMembersStrategy.Source))
+ return Enumerable.Empty();
+
+ return BuilderContext.SymbolAccessor
+ .GetAllAccessibleMappableMembers(Mapping.SourceType)
+ .Where(x => BuilderContext.SymbolAccessor.HasAttribute(x.MemberSymbol))
+ .Select(x => x.Name);
+ }
+
private HashSet GetSourceMemberNames()
{
return BuilderContext.SymbolAccessor.GetAllAccessibleMappableMembers(Mapping.SourceType).Select(x => x.Name).ToHashSet();
diff --git a/src/Riok.Mapperly/Symbols/FieldMember.cs b/src/Riok.Mapperly/Symbols/FieldMember.cs
index a2c5bc755c..67065063b2 100644
--- a/src/Riok.Mapperly/Symbols/FieldMember.cs
+++ b/src/Riok.Mapperly/Symbols/FieldMember.cs
@@ -14,6 +14,7 @@ public FieldMember(IFieldSymbol fieldSymbol)
public string Name => _fieldSymbol.Name;
public ITypeSymbol Type => _fieldSymbol.Type;
+ public ISymbol MemberSymbol => _fieldSymbol;
public bool IsNullable => _fieldSymbol.NullableAnnotation == NullableAnnotation.Annotated || Type.IsNullable();
public bool IsIndexer => false;
public bool CanGet => !_fieldSymbol.IsReadOnly && _fieldSymbol.IsAccessible();
diff --git a/src/Riok.Mapperly/Symbols/IMappableMember.cs b/src/Riok.Mapperly/Symbols/IMappableMember.cs
index bebdf5fee5..9eb6a873ee 100644
--- a/src/Riok.Mapperly/Symbols/IMappableMember.cs
+++ b/src/Riok.Mapperly/Symbols/IMappableMember.cs
@@ -12,6 +12,8 @@ public interface IMappableMember
ITypeSymbol Type { get; }
+ ISymbol MemberSymbol { get; }
+
bool IsNullable { get; }
bool IsIndexer { get; }
diff --git a/src/Riok.Mapperly/Symbols/PropertyMember.cs b/src/Riok.Mapperly/Symbols/PropertyMember.cs
index 1a825ef584..4180059569 100644
--- a/src/Riok.Mapperly/Symbols/PropertyMember.cs
+++ b/src/Riok.Mapperly/Symbols/PropertyMember.cs
@@ -14,6 +14,7 @@ internal PropertyMember(IPropertySymbol propertySymbol)
public string Name => _propertySymbol.Name;
public ITypeSymbol Type => _propertySymbol.Type;
+ public ISymbol MemberSymbol => _propertySymbol;
public bool IsNullable => _propertySymbol.NullableAnnotation == NullableAnnotation.Annotated || Type.IsNullable();
public bool IsIndexer => _propertySymbol.IsIndexer;
public bool CanGet => !_propertySymbol.IsWriteOnly && _propertySymbol.GetMethod?.IsAccessible() != false;
diff --git a/test/Riok.Mapperly.Tests/Mapping/IgnoreObsoleteTest.cs b/test/Riok.Mapperly.Tests/Mapping/IgnoreObsoleteTest.cs
new file mode 100644
index 0000000000..f8562e5607
--- /dev/null
+++ b/test/Riok.Mapperly.Tests/Mapping/IgnoreObsoleteTest.cs
@@ -0,0 +1,378 @@
+using Riok.Mapperly.Abstractions;
+using Riok.Mapperly.Diagnostics;
+
+namespace Riok.Mapperly.Tests.Mapping;
+
+public class IgnoreObsoleteTest
+{
+ private const string _classA = """
+ class A
+ {
+ public int Value { get; set; }
+
+ [Obsolete]
+ public int Ignored { get; set; }
+ }
+ """;
+
+ private const string _classB = """
+ class B
+ {
+ public int Value { get; set; }
+
+ [Obsolete]
+ public int Ignored { get; set; }
+ }
+ """;
+
+ [Fact]
+ public void ClassAttributeIgnoreObsoleteNone()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "A",
+ "B",
+ TestSourceBuilderOptions.WithIgnoreObsolete(IgnoreObsoleteMembersStrategy.None),
+ _classA,
+ _classB
+ );
+
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value = source.Value;
+ target.Ignored = source.Ignored;
+ return target;
+ """
+ );
+ }
+
+ [Fact]
+ public void ClassAttributeIgnoreObsoleteBoth()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "A",
+ "B",
+ TestSourceBuilderOptions.WithIgnoreObsolete(IgnoreObsoleteMembersStrategy.Both),
+ _classA,
+ _classB
+ );
+
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value = source.Value;
+ return target;
+ """
+ );
+ }
+
+ [Fact]
+ public void ClassAttributeIgnoreSourceShouldDiagnostic()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "A",
+ "B",
+ TestSourceBuilderOptions.WithIgnoreObsolete(IgnoreObsoleteMembersStrategy.Source),
+ _classA,
+ _classB
+ );
+
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value = source.Value;
+ return target;
+ """
+ )
+ .HaveDiagnostic(DiagnosticDescriptors.SourceMemberNotFound);
+ }
+
+ [Fact]
+ public void ClassAttributeIgnoreTargetShouldDiagnostic()
+ {
+ var source = TestSourceBuilder.Mapping(
+ "A",
+ "B",
+ TestSourceBuilderOptions.WithIgnoreObsolete(IgnoreObsoleteMembersStrategy.Target),
+ _classA,
+ _classB
+ );
+
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value = source.Value;
+ return target;
+ """
+ )
+ .HaveDiagnostic(DiagnosticDescriptors.SourceMemberNotMapped);
+ }
+
+ [Fact]
+ public void MethodAttributeIgnoreObsoleteNone()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ "[MapperIgnoreObsoleteMembers(IgnoreObsoleteMembersStrategy.None)] partial B Map(A source);",
+ _classA,
+ _classB
+ );
+
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value = source.Value;
+ target.Ignored = source.Ignored;
+ return target;
+ """
+ );
+ }
+
+ [Fact]
+ public void MethodAttributeIgnoreObsoleteBoth()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes("[MapperIgnoreObsoleteMembers] partial B Map(A source);", _classA, _classB);
+
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value = source.Value;
+ return target;
+ """
+ );
+ }
+
+ [Fact]
+ public void MethodAttributeIgnoreObsoleteSourceShouldDiagnostic()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ "[MapperIgnoreObsoleteMembers(IgnoreObsoleteMembersStrategy.Source)] partial B Map(A source);",
+ _classA,
+ _classB
+ );
+
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value = source.Value;
+ return target;
+ """
+ )
+ .HaveDiagnostic(DiagnosticDescriptors.SourceMemberNotFound);
+ }
+
+ [Fact]
+ public void MethodAttributeIgnoreObsoleteTargetShouldDiagnostic()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ "[MapperIgnoreObsoleteMembers(IgnoreObsoleteMembersStrategy.Target)] partial B Map(A source);",
+ _classA,
+ _classB
+ );
+
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value = source.Value;
+ return target;
+ """
+ )
+ .HaveDiagnostic(DiagnosticDescriptors.SourceMemberNotMapped);
+ }
+
+ [Fact]
+ public void MethodAttributeOverridesClassAttribute()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ "[MapperIgnoreObsoleteMembers(IgnoreObsoleteMembersStrategy.None)] partial B Map(A source);",
+ TestSourceBuilderOptions.WithIgnoreObsolete(IgnoreObsoleteMembersStrategy.Both),
+ _classA,
+ _classB
+ );
+
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value = source.Value;
+ target.Ignored = source.Ignored;
+ return target;
+ """
+ );
+ }
+
+ [Fact]
+ public void MapPropertyOverridesIgnoreObsoleteBoth()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ """
+ [MapProperty("Ignored", "Ignored")]
+ [MapperIgnoreObsoleteMembers]
+ partial B Map(A source);
+ """,
+ _classA,
+ _classB
+ );
+
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value = source.Value;
+ target.Ignored = source.Ignored;
+ return target;
+ """
+ );
+ }
+
+ [Fact]
+ public void MapPropertyOverridesIgnoreObsoleteSource()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ """
+ [MapProperty("Ignored", "Ignored")]
+ [MapperIgnoreObsoleteMembers(IgnoreObsoleteMembersStrategy.Source)]
+ partial B Map(A source);
+ """,
+ _classA,
+ _classB
+ );
+
+ TestHelper
+ .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value = source.Value;
+ target.Ignored = source.Ignored;
+ return target;
+ """
+ );
+ }
+
+ [Fact]
+ public void MapPropertyOverridesIgnoreObsoleteTarget()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ """
+ [MapProperty("Ignored", "Ignored")]
+ [MapperIgnoreObsoleteMembers(IgnoreObsoleteMembersStrategy.Target)]
+ partial B Map(A source);
+ """,
+ _classA,
+ _classB
+ );
+
+ TestHelper
+ .GenerateMapper(source)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B();
+ target.Value = source.Value;
+ target.Ignored = source.Ignored;
+ return target;
+ """
+ );
+ }
+
+ [Fact]
+ public void MapInitPropertyWhenIgnoreObsoleteTarget()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ """
+ [MapProperty("Ignored", "Ignored")]
+ [MapperIgnoreObsoleteMembers(IgnoreObsoleteMembersStrategy.Target)]
+ partial B Map(A source);
+ """,
+ _classA,
+ """
+ class B
+ {
+ public int Value { get; set; }
+
+ [Obsolete]
+ public int Ignored { get; init; }
+ }
+ """
+ );
+
+ TestHelper
+ .GenerateMapper(source)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B()
+ {
+ Ignored = source.Ignored
+ };
+ target.Value = source.Value;
+ return target;
+ """
+ );
+ }
+
+ [Fact]
+ public void MapRequiredPropertyWhenIgnoreObsoleteTarget()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ """
+ [MapProperty("Ignored", "Ignored")]
+ [MapperIgnoreObsoleteMembers(IgnoreObsoleteMembersStrategy.Target)]
+ partial B Map(A source);
+ """,
+ _classA,
+ """
+ class B
+ {
+ public int Value { get; set; }
+
+ [Obsolete]
+ public required int Ignored { get; set; }
+ }
+ """
+ );
+
+ TestHelper
+ .GenerateMapper(source)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B()
+ {
+ Ignored = source.Ignored
+ };
+ target.Value = source.Value;
+ return target;
+ """
+ );
+ }
+}
diff --git a/test/Riok.Mapperly.Tests/Mapping/ObjectPropertyInitPropertyTest.cs b/test/Riok.Mapperly.Tests/Mapping/ObjectPropertyInitPropertyTest.cs
index 7aa3fe1ff5..5f678290f8 100644
--- a/test/Riok.Mapperly.Tests/Mapping/ObjectPropertyInitPropertyTest.cs
+++ b/test/Riok.Mapperly.Tests/Mapping/ObjectPropertyInitPropertyTest.cs
@@ -312,6 +312,66 @@ public void RequiredProperty()
);
}
+ [Fact]
+ public void IgnoredTargetRequiredPropertyWithConfiguration()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ """
+ [MapProperty("StringValue", "StringValue")]
+ [MapperIgnoreTarget("StringValue")]
+ partial B Map(A source);
+ """,
+ "A",
+ "B",
+ "class A { public string StringValue { get; init; } public int IntValue { get; set; } }",
+ "class B { public required string StringValue { get; set; } public int IntValue { get; set; } }"
+ );
+
+ TestHelper
+ .GenerateMapper(source)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B()
+ {
+ StringValue = source.StringValue
+ };
+ target.IntValue = source.IntValue;
+ return target;
+ """
+ );
+ }
+
+ [Fact]
+ public void IgnoredTargetInitPropertyWithConfiguration()
+ {
+ var source = TestSourceBuilder.MapperWithBodyAndTypes(
+ """
+ [MapProperty("StringValue", "StringValue")]
+ [MapperIgnoreTarget("StringValue")]
+ partial B Map(A source);
+ """,
+ "A",
+ "B",
+ "class A { public string StringValue { get; init; } public int IntValue { get; set; } }",
+ "class B { public string StringValue { get; init; } public int IntValue { get; set; } }"
+ );
+
+ TestHelper
+ .GenerateMapper(source)
+ .Should()
+ .HaveSingleMethodBody(
+ """
+ var target = new global::B()
+ {
+ StringValue = source.StringValue
+ };
+ target.IntValue = source.IntValue;
+ return target;
+ """
+ );
+ }
+
[Fact]
public Task RequiredPropertySourceNotFoundShouldDiagnostic()
{
diff --git a/test/Riok.Mapperly.Tests/TestSourceBuilder.cs b/test/Riok.Mapperly.Tests/TestSourceBuilder.cs
index 724cc92778..bee56a2910 100644
--- a/test/Riok.Mapperly.Tests/TestSourceBuilder.cs
+++ b/test/Riok.Mapperly.Tests/TestSourceBuilder.cs
@@ -85,6 +85,7 @@ private static string BuildAttribute(TestSourceBuilderOptions options)
Attribute(options.PropertyNameMappingStrategy),
Attribute(options.EnumMappingStrategy),
Attribute(options.EnumMappingIgnoreCase),
+ Attribute(options.IgnoreObsoleteMembersStrategy),
};
return $"[Mapper({string.Join(", ", attrs)})]";
diff --git a/test/Riok.Mapperly.Tests/TestSourceBuilderOptions.cs b/test/Riok.Mapperly.Tests/TestSourceBuilderOptions.cs
index 6fbbf2256a..fcc0c8684c 100644
--- a/test/Riok.Mapperly.Tests/TestSourceBuilderOptions.cs
+++ b/test/Riok.Mapperly.Tests/TestSourceBuilderOptions.cs
@@ -11,13 +11,17 @@ public record TestSourceBuilderOptions(
PropertyNameMappingStrategy PropertyNameMappingStrategy = PropertyNameMappingStrategy.CaseSensitive,
MappingConversionType EnabledConversions = MappingConversionType.All,
EnumMappingStrategy EnumMappingStrategy = EnumMappingStrategy.ByValue,
- bool EnumMappingIgnoreCase = false
+ bool EnumMappingIgnoreCase = false,
+ IgnoreObsoleteMembersStrategy IgnoreObsoleteMembersStrategy = IgnoreObsoleteMembersStrategy.None
)
{
public static readonly TestSourceBuilderOptions Default = new();
public static readonly TestSourceBuilderOptions WithDeepCloning = new(UseDeepCloning: true);
public static readonly TestSourceBuilderOptions WithReferenceHandling = new(UseReferenceHandling: true);
+ public static TestSourceBuilderOptions WithIgnoreObsolete(IgnoreObsoleteMembersStrategy ignoreObsoleteStrategy) =>
+ new(IgnoreObsoleteMembersStrategy: ignoreObsoleteStrategy);
+
public static TestSourceBuilderOptions WithDisabledMappingConversion(params MappingConversionType[] conversionTypes)
{
var enabled = MappingConversionType.All;