diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilderContext.cs b/src/Riok.Mapperly/Descriptors/MappingBuilderContext.cs index a0d0fc1ae3..9b5911227f 100644 --- a/src/Riok.Mapperly/Descriptors/MappingBuilderContext.cs +++ b/src/Riok.Mapperly/Descriptors/MappingBuilderContext.cs @@ -154,7 +154,9 @@ bool ignoreDerivedTypes Location? diagnosticLocation = null ) { - return FindMapping(mappingKey) ?? BuildMapping(mappingKey, options, diagnosticLocation); + return FindMapping(mappingKey) + ?? FindMapping(mappingKey.TargetNonNullable()) + ?? BuildMapping(mappingKey, options, diagnosticLocation); } /// diff --git a/src/Riok.Mapperly/Descriptors/TypeMappingKey.cs b/src/Riok.Mapperly/Descriptors/TypeMappingKey.cs index b0062da3c8..c499bf2e4a 100644 --- a/src/Riok.Mapperly/Descriptors/TypeMappingKey.cs +++ b/src/Riok.Mapperly/Descriptors/TypeMappingKey.cs @@ -26,6 +26,8 @@ public TypeMappingKey(ITypeMapping mapping, TypeMappingConfiguration? config = n public TypeMappingKey NonNullable() => new(Source.NonNullable(), Target.NonNullable(), Configuration); + public TypeMappingKey TargetNonNullable() => new(Source, Target.NonNullable(), Configuration); + public override bool Equals(object? obj) => obj is TypeMappingKey other && _comparer.Equals(Source, other.Source) diff --git a/test/Riok.Mapperly.Tests/Mapping/ObjectPropertyNullableTest.cs b/test/Riok.Mapperly.Tests/Mapping/ObjectPropertyNullableTest.cs index 742260b7a1..7fd5853a59 100644 --- a/test/Riok.Mapperly.Tests/Mapping/ObjectPropertyNullableTest.cs +++ b/test/Riok.Mapperly.Tests/Mapping/ObjectPropertyNullableTest.cs @@ -899,4 +899,33 @@ private A(C value) """ ); } + + [Fact] + public Task MixedNullableContextsWithDerivedTypesShouldWork() + { + var source = TestSourceBuilder.MapperWithBodyAndTypes( + """ + [MapDerivedType] + public partial BBase Map(ABase src); + """, + """ + #nullable disable + public abstract record BBase + { + public List Objects { get; init; } = []; + } + + public record B : BBase; + + #nullable enable + public abstract record ABase + { + public List Objects { get; init; } = []; + } + + public record A: ABase; + """ + ); + return TestHelper.VerifyGenerator(source); + } } diff --git a/test/Riok.Mapperly.Tests/_snapshots/ObjectPropertyNullableTest.MixedNullableContextsWithDerivedTypesShouldWork#Mapper.g.verified.cs b/test/Riok.Mapperly.Tests/_snapshots/ObjectPropertyNullableTest.MixedNullableContextsWithDerivedTypesShouldWork#Mapper.g.verified.cs new file mode 100644 index 0000000000..0cdab5cb2c --- /dev/null +++ b/test/Riok.Mapperly.Tests/_snapshots/ObjectPropertyNullableTest.MixedNullableContextsWithDerivedTypesShouldWork#Mapper.g.verified.cs @@ -0,0 +1,36 @@ +//HintName: Mapper.g.cs +// +#nullable enable +public partial class Mapper +{ + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + public partial global::BBase Map(global::ABase src) + { + return src switch + { + global::A x => MapToB(x), + _ => throw new System.ArgumentException($"Cannot map {src.GetType()} to BBase as there is no known derived type mapping", nameof(src)), + }; + } + + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + private global::B MapToB(global::A source) + { + var target = new global::B() + { + Objects = MapToListOfBBase(source.Objects), + }; + return target; + } + + [global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")] + private global::System.Collections.Generic.List MapToListOfBBase(global::System.Collections.Generic.IReadOnlyCollection source) + { + var target = new global::System.Collections.Generic.List(source.Count); + foreach (var item in source) + { + target.Add(Map(item)); + } + return target; + } +} \ No newline at end of file