diff --git a/CodeConverter/CSharp/DeclarationNodeVisitor.cs b/CodeConverter/CSharp/DeclarationNodeVisitor.cs index 74a905a13..ed2af7634 100644 --- a/CodeConverter/CSharp/DeclarationNodeVisitor.cs +++ b/CodeConverter/CSharp/DeclarationNodeVisitor.cs @@ -854,7 +854,17 @@ public override async Task VisitMethodStatement(VBSyntax.Metho ); bool canHaveMethodBody = declaredSymbol?.CanHaveMethodBody() != false; - return hasBody && canHaveMethodBody ? decl : decl.WithSemicolonToken(SemicolonToken); + return hasBody && canHaveMethodBody ? decl : WithoutAsyncModifier(decl).WithSemicolonToken(SemicolonToken); + } + + /// + /// VB allows MustOverride Async Function, but C# reports CS1994 for async on a method without a body. + /// async is an implementation detail rather than part of the signature, so overriding methods can still be async. + /// + private static MethodDeclarationSyntax WithoutAsyncModifier(MethodDeclarationSyntax decl) + { + var asyncModifier = decl.Modifiers.FirstOrDefault(m => m.IsKind(CS.SyntaxKind.AsyncKeyword)); + return asyncModifier == default ? decl : decl.WithModifiers(decl.Modifiers.Remove(asyncModifier)); } public override async Task VisitEventBlock(VBSyntax.EventBlockSyntax node) diff --git a/Tests/CSharp/MemberTests/MemberTests.cs b/Tests/CSharp/MemberTests/MemberTests.cs index 57dca8153..b1d30ac72 100644 --- a/Tests/CSharp/MemberTests/MemberTests.cs +++ b/Tests/CSharp/MemberTests/MemberTests.cs @@ -1662,6 +1662,88 @@ internal string Format(string prefix = null, string suffix = null) suffix = suffix ?? ']'.ToString(); return prefix + suffix; } +}"); + } + + [Fact] + public async Task MustOverrideAsyncFunctionDropsAsyncModifierAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Imports System.Threading.Tasks + +MustInherit Class TestClass + Public MustOverride Async Function TestMethod() As Task +End Class + +Class ChildClass + Inherits TestClass + + Public Overrides Async Function TestMethod() As Task + Await Task.Delay(1) + End Function +End Class", @"using System.Threading.Tasks; + +internal abstract partial class TestClass +{ + public abstract Task TestMethod(); +} + +internal partial class ChildClass : TestClass +{ + + public override async Task TestMethod() + { + await Task.Delay(1); + } +}"); + } + + [Fact] + public async Task MustOverrideAsyncSubAndGenericFunctionDropsAsyncModifierAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Imports System.Threading.Tasks + +MustInherit Class TestClass + Protected MustOverride Async Sub Fire() + Public MustOverride Async Function GetAsync(x As Integer) As Task(Of Integer) +End Class", @"using System.Threading.Tasks; + +internal abstract partial class TestClass +{ + protected abstract void Fire(); + public abstract Task GetAsync(int x); +}"); + } + + [Fact] + public async Task PartialAsyncMethodAsync() + { + await TestConversionVisualBasicToCSharpAsync( + @"Imports System.Threading.Tasks + +Partial Class TestClass + Private Partial Sub Defined() + End Sub +End Class + +Partial Class TestClass + Private Async Sub Defined() + Await Task.Delay(1) + End Sub +End Class", @"using System.Threading.Tasks; + +internal partial class TestClass +{ + partial void Defined(); +} + +internal partial class TestClass +{ + async partial void Defined() + { + await Task.Delay(1); + } }"); } } \ No newline at end of file