-
-
Notifications
You must be signed in to change notification settings - Fork 111
Description
Description
When running the TUnit NUnit migration code fixer, methods that implement interface members with void return types are incorrectly converted to async Task methods. This breaks the interface implementation because the interface still expects void.
Steps to Reproduce
- Create a project with classes that implement interfaces (like
IInterceptor,IProxyGenerationHook) and use NUnit assertions - Add TUnit packages
- Run
dotnet format analyzers --severity info --diagnostics TUNU0001 - Build the project
Expected Behavior
Methods that implement interface members should NOT have their return type changed. The assertions should either:
- Use synchronous assertion patterns
- Be left unchanged for manual migration
Actual Behavior
Interface implementation methods are converted to async, causing build errors:
error CS0738: 'ChangeProxyTargetInterceptor' does not implement interface member
'IInterceptor.Intercept(IInvocation)'. 'ChangeProxyTargetInterceptor.Intercept(IInvocation)'
cannot implement 'IInterceptor.Intercept(IInvocation)' because it does not have the
matching return type of 'void'.
Example
Interface definition (not modified by migration):
public interface IInterceptor
{
void Intercept(IInvocation invocation);
}Before migration:
public class ChangeProxyTargetInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var targetAccessor = invocation.Proxy as IProxyTargetAccessor;
Assert.IsNotNull(targetAccessor);
targetAccessor.DynProxySetTarget(target);
invocation.Proceed();
}
}After migration (current - broken):
public class ChangeProxyTargetInterceptor : IInterceptor
{
public async Task Intercept(IInvocation invocation) // <-- Breaks interface implementation
{
var targetAccessor = invocation.Proxy as IProxyTargetAccessor;
await Assert.That(targetAccessor).IsNotNull();
targetAccessor.DynProxySetTarget(target);
invocation.Proceed();
}
}Expected after migration:
Option 1 - Use synchronous pattern:
public class ChangeProxyTargetInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var targetAccessor = invocation.Proxy as IProxyTargetAccessor;
Assert.That(targetAccessor).IsNotNull().Wait(); // or synchronous alternative
targetAccessor.DynProxySetTarget(target);
invocation.Proceed();
}
}Option 2 - Leave unchanged for manual migration:
public class ChangeProxyTargetInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
var targetAccessor = invocation.Proxy as IProxyTargetAccessor;
Assert.IsNotNull(targetAccessor); // Left for manual migration
targetAccessor.DynProxySetTarget(target);
invocation.Proceed();
}
}Detection Suggestion
The code fixer could detect interface implementations by checking:
- If the containing class implements any interfaces
- If the method signature matches an interface member
- If so, don't convert to async
Impact
This is a critical issue because it generates code that cannot compile.
Environment
- TUnit version: 1.9.91
- .NET version: .NET 8.0
- OS: Windows