diff --git a/README.md b/README.md
old mode 100644
new mode 100755
index 41d0bcf3a..7ee8f77e8
--- a/README.md
+++ b/README.md
@@ -169,7 +169,8 @@ If you are already using other analyzers, you can check [which rules are duplica
|[MA0151](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0151.md)|Usage|DebuggerDisplay must contain valid members|⚠️|✔️|❌|
|[MA0152](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0152.md)|Performance|Use Unwrap instead of using await twice|ℹ️|✔️|❌|
|[MA0153](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0153.md)|Design|Do not log symbols decorated with DataClassificationAttribute directly|⚠️|✔️|❌|
-|[MA0154](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0154.md)|Design|Use langword in XML comment|ℹ️|✔️|❌|
+|[MA0154](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0154.md)|Design|Use langword in XML comment|ℹ️|✔️|✔️|
+|[MA0155](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0155.md)|Design|Do not use async void methods|⚠️|❌|❌|
diff --git a/docs/README.md b/docs/README.md
old mode 100644
new mode 100755
index e332969cd..fd8e07f31
--- a/docs/README.md
+++ b/docs/README.md
@@ -153,7 +153,8 @@
|[MA0151](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0151.md)|Usage|DebuggerDisplay must contain valid members|⚠️|✔️|❌|
|[MA0152](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0152.md)|Performance|Use Unwrap instead of using await twice|ℹ️|✔️|❌|
|[MA0153](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0153.md)|Design|Do not log symbols decorated with DataClassificationAttribute directly|⚠️|✔️|❌|
-|[MA0154](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0154.md)|Design|Use langword in XML comment|ℹ️|✔️|❌|
+|[MA0154](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0154.md)|Design|Use langword in XML comment|ℹ️|✔️|✔️|
+|[MA0155](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0155.md)|Design|Do not use async void methods|⚠️|❌|❌|
|Id|Suppressed rule|Justification|
|--|---------------|-------------|
@@ -622,6 +623,9 @@ dotnet_diagnostic.MA0153.severity = warning
# MA0154: Use langword in XML comment
dotnet_diagnostic.MA0154.severity = suggestion
+
+# MA0155: Do not use async void methods
+dotnet_diagnostic.MA0155.severity = none
```
# .editorconfig - all rules disabled
@@ -1085,4 +1089,7 @@ dotnet_diagnostic.MA0153.severity = none
# MA0154: Use langword in XML comment
dotnet_diagnostic.MA0154.severity = none
+
+# MA0155: Do not use async void methods
+dotnet_diagnostic.MA0155.severity = none
```
diff --git a/docs/Rules/MA0155.md b/docs/Rules/MA0155.md
new file mode 100755
index 000000000..1018354e0
--- /dev/null
+++ b/docs/Rules/MA0155.md
@@ -0,0 +1,15 @@
+# MA0155 - Do not use async void methods
+
+```c#
+// not-compliant
+async void SomeMethod()
+{
+ await Task.Delay(1000);
+}
+
+// ok
+async Task SomeMethod()
+{
+ await Task.Delay(1000);
+}
+```
diff --git a/src/Meziantou.Analyzer/Rules/DoNotUseAsyncVoidAnalyzer.cs b/src/Meziantou.Analyzer/Rules/DoNotUseAsyncVoidAnalyzer.cs
new file mode 100755
index 000000000..4f7feb585
--- /dev/null
+++ b/src/Meziantou.Analyzer/Rules/DoNotUseAsyncVoidAnalyzer.cs
@@ -0,0 +1,38 @@
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Meziantou.Analyzer.Rules;
+
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class DoNotUseAsyncVoidAnalyzer : DiagnosticAnalyzer
+{
+ private static readonly DiagnosticDescriptor Rule = new(
+ RuleIdentifiers.DoNotUseAsyncVoid,
+ title: "Do not use async void methods",
+ messageFormat: "Do not use async void methods",
+ RuleCategories.Design,
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: false,
+ description: "",
+ helpLinkUri: RuleIdentifiers.GetHelpUri(RuleIdentifiers.DoNotUseAsyncVoid));
+
+ public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(Rule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.EnableConcurrentExecution();
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+
+ context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.Method);
+ }
+
+ private static void AnalyzeSymbol(SymbolAnalysisContext context)
+ {
+ var symbol = (IMethodSymbol)context.Symbol;
+ if (symbol is { ReturnsVoid: true, IsAsync: true })
+ {
+ context.ReportDiagnostic(Rule, symbol);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncVoidAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncVoidAnalyzerTests.cs
new file mode 100755
index 000000000..1afeb8da9
--- /dev/null
+++ b/tests/Meziantou.Analyzer.Test/Rules/DoNotUseAsyncVoidAnalyzerTests.cs
@@ -0,0 +1,55 @@
+using System.Threading.Tasks;
+using Meziantou.Analyzer.Rules;
+using TestHelper;
+using Xunit;
+
+namespace Meziantou.Analyzer.Test.Rules;
+public sealed class DoNotUseAsyncVoidAnalyzerTests
+{
+ private static ProjectBuilder CreateProjectBuilder()
+ {
+ return new ProjectBuilder()
+ .WithAnalyzer()
+ .WithTargetFramework(TargetFramework.Net8_0);
+ }
+
+ [Fact]
+ public async Task Method_Void()
+ {
+ await CreateProjectBuilder()
+ .WithSourceCode("""
+ class Sample
+ {
+ void A() => throw null;
+ }
+ """)
+ .ValidateAsync();
+ }
+
+ [Fact]
+ public async Task Method_AsyncVoid()
+ {
+ await CreateProjectBuilder()
+ .WithSourceCode("""
+ class Sample
+ {
+ async void [|A|]() => throw null;
+ }
+ """)
+ .ValidateAsync();
+ }
+
+ [Fact]
+ public async Task Method_AsyncTask()
+ {
+ await CreateProjectBuilder()
+ .WithSourceCode("""
+ class Sample
+ {
+ async System.Threading.Tasks.Task A() => throw null;
+ }
+ """)
+ .ValidateAsync();
+ }
+
+}