Skip to content

[TUnit.Mocks] Generated mock for interface with 'unmanaged' type constraint does not compile (emits 'where T : struct, unmanaged') #6471

Description

@jdewera

Description

Mock.Of<T>() over a generic interface whose type parameter has an unmanaged constraint produces generated code that does not compile. The generator emits where T : struct, unmanaged on the mock classes, but C# forbids combining struct and unmanaged (CS0449). Because the constraint clause is invalid, T is then not recognized as satisfying the interface's unmanaged constraint, which cascades into ~30 CS8377 errors across the generated files.

Minimal reproduction

public interface ISnapshotSource<T> where T : unmanaged
{
    void Fill(ref T snapshot);
}

public sealed class ReproTests
{
    [Test]
    public async Task Mock_OfUnmanagedConstrainedInterface_Compiles()
    {
        var sourceMock = Mock.Of<ISnapshotSource<int>>();

        await Assert.That(sourceMock).IsNotNull();
    }
}
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <IsTestProject>true</IsTestProject>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="TUnit" Version="1.61.29" />
    <PackageReference Include="TUnit.Mocks" Version="1.61.29" />
  </ItemGroup>
</Project>

Actual behavior

dotnet build fails with 33 errors in the generated sources. The generated mock class is declared as:

public sealed class ISnapshotSource_T_Mock<T> : global::TUnit.Mocks.Mock<global::ISnapshotSource<T>>, global::ISnapshotSource<T> where T : struct, unmanaged

Representative errors:

ISnapshotSource_T__Mock.g.cs(5,148): error CS0449: The 'class', 'struct', 'unmanaged', 'notnull', and 'default' constraints cannot be combined or duplicated, and must be specified first in the constraints list.
ISnapshotSource_T__Mock.g.cs(5,21): error CS8377: The type 'T' must be a non-nullable value type, along with all fields at any level of nesting, in order to use it as parameter 'T' in the generic type or method 'ISnapshotSource<T>'

The same pattern (where T : struct, unmanaged, ...) is emitted in ISnapshotSource_T__MockImplFactory.g.cs and fails the same way.

Expected behavior

The generated mock compiles, emitting where T : unmanaged only (the unmanaged constraint already implies a non-nullable value type, so struct must be omitted).

Environment

  • TUnit / TUnit.Mocks 1.61.29
  • .NET SDK 10.0.302, net10.0
  • macOS (arm64), also reproduced in a larger solution with where T : unmanaged, ISomeInterface

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions