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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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