diff --git a/src/Riok.Mapperly/Helpers/NullableSymbolExtensions.cs b/src/Riok.Mapperly/Helpers/NullableSymbolExtensions.cs
index 8a65d315c1..d0ecf9d452 100644
--- a/src/Riok.Mapperly/Helpers/NullableSymbolExtensions.cs
+++ b/src/Riok.Mapperly/Helpers/NullableSymbolExtensions.cs
@@ -1,3 +1,4 @@
+using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
@@ -38,7 +39,7 @@ internal static bool TryUpgradeNullable(this ITypeSymbol symbol, [NotNullWhen(tr
return false;
}
- upgradedSymbol = symbol.WithNullableAnnotation(NullableAnnotation.Annotated);
+ upgradedSymbol = symbol.WithDeepNullableAnnotation(NullableAnnotation.Annotated);
return true;
}
@@ -112,4 +113,24 @@ internal static bool IsNullable(this ITypeParameterSymbol typeParameter, Nullabl
internal static bool IsNullable(this NullableAnnotation nullable) =>
nullable is NullableAnnotation.Annotated or NullableAnnotation.None;
+
+ ///
+ /// Returns a new type symbol with the provided nullable annotation.
+ /// Also sets the nullable annotation on all type arguments of the .
+ ///
+ /// The symbol to work on.
+ /// The to set.
+ /// The new symbol with the given nullable annotation.
+ private static ITypeSymbol WithDeepNullableAnnotation(this ITypeSymbol symbol, NullableAnnotation annotation)
+ {
+ if (symbol is INamedTypeSymbol { TypeArguments.Length: > 0 } namedSymbol)
+ {
+ symbol = namedSymbol.ConstructedFrom.Construct(
+ namedSymbol.TypeArguments,
+ Enumerable.Repeat(annotation, namedSymbol.TypeArguments.Length).ToImmutableArray()
+ );
+ }
+
+ return symbol.WithNullableAnnotation(annotation);
+ }
}
diff --git a/test/Riok.Mapperly.Tests/Mapping/EnumerableTest.cs b/test/Riok.Mapperly.Tests/Mapping/EnumerableTest.cs
index d6f2641e1b..73abe94557 100644
--- a/test/Riok.Mapperly.Tests/Mapping/EnumerableTest.cs
+++ b/test/Riok.Mapperly.Tests/Mapping/EnumerableTest.cs
@@ -550,6 +550,14 @@ public Task ArrayToReadOnlyCollectionShouldUpgradeNullability()
return TestHelper.VerifyGenerator(source, TestHelperOptions.DisabledNullable);
}
+ [Fact]
+ public Task ShouldUpgradeNullabilityOfGenericInDisabledNullableContext()
+ {
+ var source = TestSourceBuilder.Mapping("IList", "IList", "record A(int V);", "record B(int V);");
+
+ return TestHelper.VerifyGenerator(source, TestHelperOptions.DisabledNullable);
+ }
+
[Fact]
public Task ArrayToCollectionShouldUpgradeNullability()
{
diff --git a/test/Riok.Mapperly.Tests/_snapshots/EnumerableTest.ShouldUpgradeNullabilityOfGenericInDisabledNullableContext#Mapper.g.verified.cs b/test/Riok.Mapperly.Tests/_snapshots/EnumerableTest.ShouldUpgradeNullabilityOfGenericInDisabledNullableContext#Mapper.g.verified.cs
new file mode 100644
index 0000000000..7bde9f2820
--- /dev/null
+++ b/test/Riok.Mapperly.Tests/_snapshots/EnumerableTest.ShouldUpgradeNullabilityOfGenericInDisabledNullableContext#Mapper.g.verified.cs
@@ -0,0 +1,25 @@
+//HintName: Mapper.g.cs
+//
+#nullable enable
+public partial class Mapper
+{
+ private partial global::System.Collections.Generic.IList? Map(global::System.Collections.Generic.IList? source)
+ {
+ if (source == null)
+ return default;
+ var target = new global::System.Collections.Generic.List(source.Count);
+ foreach (var item in source)
+ {
+ target.Add(MapToB(item));
+ }
+ return target;
+ }
+
+ private global::B? MapToB(global::A? source)
+ {
+ if (source == null)
+ return default;
+ var target = new global::B(source.V);
+ return target;
+ }
+}
\ No newline at end of file