diff --git a/Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs b/Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs
index 6302a188..b7b55a8d 100644
--- a/Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs
+++ b/Source/Mockolate.SourceGenerators/Sources/Sources.MockClass.cs
@@ -1555,6 +1555,64 @@ private static bool IsFastBufferEligibleMethod(Method method)
return true;
}
+ ///
+ /// A T? return where T is one of the method's generic parameters and is
+ /// constrained to a reference type (or any other non-value-type constraint such as
+ /// class, class?, an interface, or notnull) cannot be expressed in
+ /// the explicit setup-interface implementation: CS0460 forbids restating the inherited
+ /// constraint, and where T : default (CS8822) conflicts with those constraints.
+ /// Without a constraint clause the compiler resolves the bare T? as
+ /// Nullable<T> and reports CS0453/CS9334/CS0738/CS0266.
+ ///
+ /// The fix is to drop the trailing ? from the setup-side return type
+ /// (IReturnMethodSetup<T> instead of IReturnMethodSetup<T?>) and from
+ /// the matching ReturnMethodSetup<T> construction. NRT annotations are erased at
+ /// runtime, so the underlying setup object is identical and the fluent API still composes.
+ /// The user-facing mock body keeps T? because the constraint is visible there.
+ ///
+ private static bool ShouldStripNullableGenericReturnAnnotation(Method method)
+ {
+ if (method.GenericParameters is null || method.GenericParameters.Value.Count == 0)
+ {
+ return false;
+ }
+
+ string fullname = method.ReturnType.Fullname;
+ if (fullname.Length < 2 || fullname[fullname.Length - 1] != '?')
+ {
+ return false;
+ }
+
+ string raw = fullname.Substring(0, fullname.Length - 1);
+ foreach (GenericParameter gp in method.GenericParameters.Value)
+ {
+ if (gp.Name == raw)
+ {
+ return !gp.IsStruct && !gp.IsUnmanaged;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Emits the method's return type as it should appear inside the setup-side surface
+ /// (the IReturnMethodSetup<...> wrapper on the setup interface, the explicit
+ /// impl, and the new ReturnMethodSetup<...> construction). Strips a trailing
+ /// ? when applies.
+ ///
+ private static void AppendSetupReturnType(StringBuilder sb, Method method)
+ {
+ if (ShouldStripNullableGenericReturnAnnotation(method))
+ {
+ string fullname = method.ReturnType.Fullname;
+ sb.Append(fullname, 0, fullname.Length - 1);
+ return;
+ }
+
+ sb.AppendTypeOrWrapper(method.ReturnType);
+ }
+
#pragma warning disable S107 // Methods should not have too many parameters
private static void ImplementMockForInterface(StringBuilder sb, string mockRegistryName, string name,
bool hasEvents, bool hasProtectedMembers, bool hasProtectedEvents, bool hasStaticMembers, bool hasStaticEvents)
@@ -3913,7 +3971,8 @@ private static void AppendMethodSetupDefinition(StringBuilder sb, Class @class,
: "\t\tglobal::Mockolate.Setup.IReturnMethodSetup");
}
- sb.Append('<').AppendTypeOrWrapper(method.ReturnType);
+ sb.Append('<');
+ AppendSetupReturnType(sb, method);
foreach (MethodParameter parameter in method.Parameters)
{
sb.Append(", ").AppendTypeOrWrapper(parameter.Type);
@@ -4208,7 +4267,8 @@ private static void AppendMethodSetupImplementation(StringBuilder sb, Method met
: "\t\tglobal::Mockolate.Setup.IReturnMethodSetup");
}
- sb.Append('<').AppendTypeOrWrapper(method.ReturnType);
+ sb.Append('<');
+ AppendSetupReturnType(sb, method);
foreach (MethodParameter parameter in method.Parameters)
{
sb.Append(", ").AppendTypeOrWrapper(parameter.Type);
@@ -4299,8 +4359,8 @@ private static void AppendMethodSetupImplementation(StringBuilder sb, Method met
string methodSetupVar = Helpers.GetUniqueLocalVariableName("methodSetup", method.Parameters);
if (method.ReturnType != Type.Void)
{
- sb.Append("\t\t\tvar ").Append(methodSetupVar).Append(" = new global::Mockolate.Setup.ReturnMethodSetup<")
- .AppendTypeOrWrapper(method.ReturnType);
+ sb.Append("\t\t\tvar ").Append(methodSetupVar).Append(" = new global::Mockolate.Setup.ReturnMethodSetup<");
+ AppendSetupReturnType(sb, method);
foreach (MethodParameter parameter in method.Parameters)
{