Skip to content
Merged
Prev Previous commit
Next Next commit
Add targeted fix in TypedConstantFormatter for System.Nullable<>
Co-authored-by: thomhurst <30480171+thomhurst@users.noreply.github.com>
  • Loading branch information
Copilot and thomhurst committed Aug 21, 2025
commit a3cd06c1c2a8a552834c7b1963c63b9f2b5e8b3c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#pragma warning disable
#nullable enable
namespace TUnit.Generated;
internal sealed class Tests_SimpleTest_TestSource_21ec244a5088444d90b98db3ae4e625e : global::TUnit.Core.Interfaces.SourceGenerator.ITestSource
internal sealed class Tests_SimpleTest_TestSource_49ae2d23fddf49e799c37799159949b0 : global::TUnit.Core.Interfaces.SourceGenerator.ITestSource
{
public async global::System.Collections.Generic.IAsyncEnumerable<global::TUnit.Core.TestMetadata> GetTestsAsync(string testSessionId, [global::System.Runtime.CompilerServices.EnumeratorCancellation] global::System.Threading.CancellationToken cancellationToken = default)
{
Expand Down Expand Up @@ -84,11 +84,11 @@ internal sealed class Tests_SimpleTest_TestSource_21ec244a5088444d90b98db3ae4e62
yield break;
}
}
internal static class Tests_SimpleTest_ModuleInitializer_21ec244a5088444d90b98db3ae4e625e
internal static class Tests_SimpleTest_ModuleInitializer_49ae2d23fddf49e799c37799159949b0
{
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
global::TUnit.Core.SourceRegistrar.Register(typeof(global::TUnit.TestProject.Bugs._2971.Tests), new Tests_SimpleTest_TestSource_21ec244a5088444d90b98db3ae4e625e());
global::TUnit.Core.SourceRegistrar.Register(typeof(global::TUnit.TestProject.Bugs._2971.Tests), new Tests_SimpleTest_TestSource_49ae2d23fddf49e799c37799159949b0());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ public string FormatForCode(TypedConstant constant, ITypeSymbol? targetType = nu

case TypedConstantKind.Type:
var type = (ITypeSymbol)constant.Value!;

// Special handling for System.Nullable<> when it would be displayed as "T?"
if (type is INamedTypeSymbol namedType &&
(namedType.SpecialType == SpecialType.System_Nullable_T ||
namedType.ConstructedFrom?.SpecialType == SpecialType.System_Nullable_T) &&
namedType.TypeArguments.Length == 1 &&
namedType.TypeArguments[0].TypeKind == TypeKind.TypeParameter)
{
return "typeof(global::System.Nullable<>)";
}

return $"typeof({type.GloballyQualified()})";

case TypedConstantKind.Array:
Expand Down
17 changes: 10 additions & 7 deletions TUnit.Core.SourceGenerator/Extensions/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,17 +208,20 @@ public static bool EnumerableGenericTypeIs(this ITypeSymbol enumerable, Generato

public static string GloballyQualified(this ISymbol typeSymbol)
{
// Special handling for System.Nullable<> generic type definition first
// Special handling for System.Nullable<> generic type definition
// When Roslyn encounters System.Nullable<>, it displays it as "T?" which is not valid C# syntax
// Only apply this to the open generic type definition, not constructed types like Nullable<int>
if (typeSymbol is INamedTypeSymbol namedTypeSymbol)
{
// Check if this is the System.Nullable<> generic type definition with a type parameter
if ((namedTypeSymbol.SpecialType == SpecialType.System_Nullable_T ||
namedTypeSymbol.ConstructedFrom?.SpecialType == SpecialType.System_Nullable_T) &&
namedTypeSymbol.TypeArguments.Any(t => t.TypeKind == TypeKind.TypeParameter))
// Check if this is System.Nullable<T> where T is unbound/type parameter
if (namedTypeSymbol.SpecialType == SpecialType.System_Nullable_T ||
namedTypeSymbol.ConstructedFrom?.SpecialType == SpecialType.System_Nullable_T)
{
return "global::System.Nullable<>";
// For the unbound generic case like System.Nullable<> or Nullable<T> where T is a type parameter
if (namedTypeSymbol.TypeArguments.Length == 1 &&
namedTypeSymbol.TypeArguments[0].TypeKind == TypeKind.TypeParameter)
{
return "global::System.Nullable<>";
}
}
}

Expand Down