diff --git a/Source/Mockolate.SourceGenerators/Entities/Method.cs b/Source/Mockolate.SourceGenerators/Entities/Method.cs
index 0312aee7..b6c6d761 100644
--- a/Source/Mockolate.SourceGenerators/Entities/Method.cs
+++ b/Source/Mockolate.SourceGenerators/Entities/Method.cs
@@ -87,6 +87,81 @@ internal string GetUniqueNameString()
return $"\"{ContainingType}.{Name}\"";
}
+ ///
+ /// A method has an unsupported allows ref struct type parameter when one of its
+ /// own generic parameters declares the anti-constraint and is referenced in the
+ /// return type or any parameter type. The standard setup pipeline parameterizes
+ /// ReturnMethodSetup<T> / IReturnMethodSetup<T> on T, but
+ /// those runtime types do not carry allows ref struct, so the generated source
+ /// would fail with CS9244. Methods that match this predicate get a NotSupportedException
+ /// stub instead — see the carve-out for unsupported ref-struct shapes for the same shape.
+ ///
+ public bool HasUnsupportedAllowsRefStructTypeParameter
+ {
+ get
+ {
+ if (GenericParameters is null || GenericParameters.Value.Count == 0)
+ {
+ return false;
+ }
+
+ GenericParameter[] refStructParameters = GenericParameters.Value
+ .Where(g => g.AllowsRefStruct).ToArray();
+ if (refStructParameters.Length == 0)
+ {
+ return false;
+ }
+
+ if (ReturnType != Type.Void && ContainsAnyTypeParameter(ReturnType.Fullname, refStructParameters))
+ {
+ return true;
+ }
+
+ foreach (MethodParameter parameter in Parameters)
+ {
+ if (ContainsAnyTypeParameter(parameter.Type.Fullname, refStructParameters))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ private static bool ContainsAnyTypeParameter(string text, GenericParameter[] genericParameters)
+ {
+ foreach (GenericParameter gp in genericParameters)
+ {
+ if (ContainsAsToken(text, gp.Name))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static bool ContainsAsToken(string text, string name)
+ {
+ int idx = 0;
+ while ((idx = text.IndexOf(name, idx, StringComparison.Ordinal)) >= 0)
+ {
+ bool startBoundary = idx == 0 || !IsIdentifierChar(text[idx - 1]);
+ bool endBoundary = idx + name.Length == text.Length || !IsIdentifierChar(text[idx + name.Length]);
+ if (startBoundary && endBoundary)
+ {
+ return true;
+ }
+
+ idx++;
+ }
+
+ return false;
+ }
+
+ private static bool IsIdentifierChar(char c) => char.IsLetterOrDigit(c) || c == '_';
+
public bool IsToString()
=> Name == "ToString" && Parameters.Count == 0;
diff --git a/Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs b/Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs
index b7b55a8d..0b7f6585 100644
--- a/Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs
+++ b/Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs
@@ -3002,6 +3002,20 @@ private static void AppendMockSubject_ImplementClass_AddMethod(StringBuilder sb,
sb.AppendLine();
sb.AppendLine("\t\t{");
+ // Methods that use a generic type parameter declaring `allows ref struct` in their
+ // signature cannot route through the regular setup pipeline: ReturnMethodSetup /
+ // IReturnMethodSetup do not declare the same anti-constraint, so referencing them
+ // with such a T fails with CS9244. Mirroring the carve-out for unsupported ref-struct
+ // shapes, the body throws NotSupportedException so the rest of the type still compiles.
+ if (method.HasUnsupportedAllowsRefStructTypeParameter)
+ {
+ sb.Append(
+ "\t\t\tthrow new global::System.NotSupportedException(\"Mockolate: methods with a generic type parameter declaring 'allows ref struct' are not supported. Method '")
+ .Append(method.ContainingType).Append('.').Append(method.Name).Append("'.\");").AppendLine();
+ sb.AppendLine("\t\t}");
+ return;
+ }
+
// Methods with at least one ref-struct parameter (outside the Span/ReadOnlySpan wrapper
// carve-out) route through the ref-struct setup pipeline. The ref-struct value cannot
// be captured in a closure, so we emit a synchronous, stack-bound match/invoke loop.
@@ -3902,6 +3916,14 @@ private static void AppendMethodSetupDefinition(StringBuilder sb, Class @class,
bool useParameters, string? methodNameOverride = null, bool[]? valueFlags = null,
bool hasOverloadResolutionPriority = false)
{
+ // Methods using a generic type parameter that declares `allows ref struct` cannot expose
+ // a setup surface: IReturnMethodSetup / IVoidMethodSetup do not carry the same
+ // anti-constraint. The override body throws NotSupportedException, so no setup is needed.
+ if (method.HasUnsupportedAllowsRefStructTypeParameter)
+ {
+ return;
+ }
+
// Ref-struct pipeline: emit only the narrow IRefStruct*Setup declaration. We skip the
// value-flag overloads entirely because an explicit ref-struct value cannot be captured
// via `It.Is(value)` (the static value would need to live in a class field). We also
@@ -4236,6 +4258,14 @@ private static void AppendMethodSetupImplementation(StringBuilder sb, Method met
string? methodNameOverride = null, bool[]? valueFlags = null,
string? scopeExpression = null)
{
+ // Setup-side carve-out: methods using a generic type parameter that declares
+ // `allows ref struct` have no setup interface declaration (see
+ // AppendMethodSetupDefinition), so no explicit implementation is emitted either.
+ if (method.HasUnsupportedAllowsRefStructTypeParameter)
+ {
+ return;
+ }
+
if (method.Parameters.Any(p => p.NeedsRefStructPipeline()))
{
// Emit exactly once: skip the useParameters=true variant (IParameters collection
diff --git a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs
index 73e8e8ac..9d89b0e2 100644
--- a/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs
+++ b/Tests/Mockolate.SourceGenerators.Tests/MockTests.ClassTests.MethodTests.cs
@@ -179,6 +179,35 @@ public bool MyMethod1(int index)
""").IgnoringNewlineStyle();
}
+ [Fact]
+ public async Task GenericMethodWithAllowsRefStruct_ShouldCompile()
+ {
+ GeneratorResult result = Generator
+ .Run("""
+ using Mockolate;
+
+ namespace MyCode;
+
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ _ = IMyService.CreateMock();
+ }
+ }
+
+ public interface IMyService
+ {
+ T G8() where T : allows ref struct;
+ }
+ """);
+
+ await That(result.Diagnostics).IsEmpty();
+ await That(result.Sources).ContainsKey("Mock.IMyService.g.cs");
+ await That(result.Sources["Mock.IMyService.g.cs"])
+ .Contains("throw new global::System.NotSupportedException(\"Mockolate: methods with a generic type parameter declaring 'allows ref struct' are not supported. Method 'global::MyCode.IMyService.G8'.\");");
+ }
+
[Fact]
public async Task InterfaceMethodWithParameterNamedMethodExecution_ShouldGenerateUniqueLocalVariableName()
{