diff --git a/README.md b/README.md
index b1354adee..f38a84eac 100755
--- a/README.md
+++ b/README.md
@@ -152,8 +152,8 @@ If you are already using other analyzers, you can check [which rules are duplica
|[MA0134](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0134.md)|Usage|Observe result of async calls|⚠️|✔️|❌|
|[MA0135](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0135.md)|Design|The log parameter has no configured type|⚠️|❌|❌|
|[MA0136](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0136.md)|Usage|Raw String contains an implicit end of line character|👻|✔️|❌|
-|[MA0137](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0137.md)|Design|Use 'Async' suffix when a method returns an awaitable type|⚠️|❌|❌|
-|[MA0138](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0138.md)|Design|Do not use 'Async' suffix when a method does not return an awaitable type|⚠️|❌|❌|
+|[MA0137](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0137.md)|Design|Use 'Async' suffix when a method returns an awaitable type|⚠️|❌|✔️|
+|[MA0138](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0138.md)|Design|Do not use 'Async' suffix when a method does not return an awaitable type|⚠️|❌|✔️|
|[MA0139](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0139.md)|Design|Log parameter type is not valid|⚠️|✔️|❌|
|[MA0140](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0140.md)|Design|Both if and else branch have identical code|⚠️|✔️|❌|
|[MA0141](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0141.md)|Usage|Use pattern matching instead of inequality operators for null check|ℹ️|❌|✔️|
diff --git a/docs/README.md b/docs/README.md
index 15873ba1c..c014ab89c 100755
--- a/docs/README.md
+++ b/docs/README.md
@@ -136,8 +136,8 @@
|[MA0134](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0134.md)|Usage|Observe result of async calls|⚠️|✔️|❌|
|[MA0135](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0135.md)|Design|The log parameter has no configured type|⚠️|❌|❌|
|[MA0136](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0136.md)|Usage|Raw String contains an implicit end of line character|👻|✔️|❌|
-|[MA0137](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0137.md)|Design|Use 'Async' suffix when a method returns an awaitable type|⚠️|❌|❌|
-|[MA0138](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0138.md)|Design|Do not use 'Async' suffix when a method does not return an awaitable type|⚠️|❌|❌|
+|[MA0137](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0137.md)|Design|Use 'Async' suffix when a method returns an awaitable type|⚠️|❌|✔️|
+|[MA0138](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0138.md)|Design|Do not use 'Async' suffix when a method does not return an awaitable type|⚠️|❌|✔️|
|[MA0139](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0139.md)|Design|Log parameter type is not valid|⚠️|✔️|❌|
|[MA0140](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0140.md)|Design|Both if and else branch have identical code|⚠️|✔️|❌|
|[MA0141](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0141.md)|Usage|Use pattern matching instead of inequality operators for null check|ℹ️|❌|✔️|
diff --git a/docs/Rules/MA0137.md b/docs/Rules/MA0137.md
index 90df90a4d..2412c6acd 100644
--- a/docs/Rules/MA0137.md
+++ b/docs/Rules/MA0137.md
@@ -1,6 +1,6 @@
# MA0137 - Use 'Async' suffix when a method returns an awaitable type
-Source: [MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs)
+Sources: [MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs), [MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixFixer.cs)
Methods that return awaitable types such as `Task` or `ValueTask` should have an Async suffix.
diff --git a/docs/Rules/MA0138.md b/docs/Rules/MA0138.md
index 337a2d579..556cf82e0 100644
--- a/docs/Rules/MA0138.md
+++ b/docs/Rules/MA0138.md
@@ -1,6 +1,6 @@
# MA0138 - Do not use 'Async' suffix when a method does not return an awaitable type
-Source: [MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs)
+Sources: [MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzer.cs), [MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixFixer.cs)
Methods that does not return an awaitable type such as `Task` or `ValueTask` should not have an 'Async' suffix.
diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixFixer.cs
new file mode 100644
index 000000000..e5d6e5c49
--- /dev/null
+++ b/src/Meziantou.Analyzer.CodeFixers/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixFixer.cs
@@ -0,0 +1,83 @@
+using System.Collections.Immutable;
+using System.Composition;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Rename;
+
+namespace Meziantou.Analyzer.Rules;
+
+[ExportCodeFixProvider(LanguageNames.CSharp), Shared]
+public sealed class MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixFixer : CodeFixProvider
+{
+ public override ImmutableArray FixableDiagnosticIds =>
+ ImmutableArray.Create(
+ RuleIdentifiers.MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffix,
+ RuleIdentifiers.MethodsNotReturningAnAwaitableTypeMustNotHaveTheAsyncSuffix);
+
+ public override FixAllProvider? GetFixAllProvider() => null;
+
+ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
+ {
+ var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
+ if (root is null)
+ return;
+
+ var nodeToFix = root.FindNode(context.Span, getInnermostNodeForTie: true);
+ if (nodeToFix is null)
+ return;
+
+ var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
+ if (semanticModel is null)
+ return;
+
+ IMethodSymbol? methodSymbol = null;
+
+ // Try to get the method symbol: either from a method declaration or a local function
+ var declarationNode = nodeToFix.AncestorsAndSelf().FirstOrDefault(n => n is MethodDeclarationSyntax or LocalFunctionStatementSyntax);
+ if (declarationNode is MethodDeclarationSyntax methodDeclaration)
+ {
+ methodSymbol = semanticModel.GetDeclaredSymbol(methodDeclaration, context.CancellationToken) as IMethodSymbol;
+ }
+ else if (declarationNode is LocalFunctionStatementSyntax localFunctionStatement)
+ {
+ methodSymbol = semanticModel.GetDeclaredSymbol(localFunctionStatement, context.CancellationToken) as IMethodSymbol;
+ }
+
+ if (methodSymbol is null)
+ return;
+
+ foreach (var diagnostic in context.Diagnostics)
+ {
+ string newName;
+ string title;
+ if (diagnostic.Id == RuleIdentifiers.MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffix)
+ {
+ newName = methodSymbol.Name + "Async";
+ title = $"Rename to '{newName}'";
+ }
+ else
+ {
+ if (!methodSymbol.Name.EndsWith("Async", StringComparison.Ordinal))
+ continue;
+
+ newName = methodSymbol.Name[..^"Async".Length];
+ title = $"Rename to '{newName}'";
+ }
+
+ context.RegisterCodeFix(
+ CodeAction.Create(
+ title,
+ ct => RenameMethodAsync(context.Document, methodSymbol, newName, ct),
+ equivalenceKey: title),
+ diagnostic);
+ }
+ }
+
+ private static async Task RenameMethodAsync(Document document, IMethodSymbol methodSymbol, string newName, CancellationToken cancellationToken)
+ {
+ var solution = document.Project.Solution;
+ return await Renamer.RenameSymbolAsync(solution, methodSymbol, new SymbolRenameOptions(), newName, cancellationToken).ConfigureAwait(false);
+ }
+}
diff --git a/tests/Meziantou.Analyzer.Test/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzerTests.cs
index f2e2cb3f7..ce2ede6cc 100644
--- a/tests/Meziantou.Analyzer.Test/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzerTests.cs
+++ b/tests/Meziantou.Analyzer.Test/Rules/MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyzerTests.cs
@@ -8,6 +8,7 @@ public sealed class MethodsReturningAnAwaitableTypeMustHaveTheAsyncSuffixAnalyze
private static ProjectBuilder CreateProjectBuilder()
=> new ProjectBuilder()
.WithAnalyzer()
+ .WithCodeFixProvider()
.WithTargetFramework(TargetFramework.Net8_0)
.WithLanguageVersion(Microsoft.CodeAnalysis.CSharp.LanguageVersion.Preview);
@@ -171,6 +172,69 @@ class TypeName
.AddXUnitApi()
.ValidateAsync();
+ [Fact]
+ public Task AsyncMethodWithoutSuffix_CodeFix_AddsAsyncSuffix()
+ => CreateProjectBuilder()
+ .WithSourceCode("""
+ class TypeName
+ {
+ System.Threading.Tasks.Task {|MA0137:Test|}() => throw null;
+ void Caller() { _ = Test(); }
+ }
+ """)
+ .ShouldFixCodeWith("""
+ class TypeName
+ {
+ System.Threading.Tasks.Task TestAsync() => throw null;
+ void Caller() { _ = TestAsync(); }
+ }
+ """)
+ .ValidateAsync();
+
+ [Fact]
+ public Task MethodNotReturningAwaitableTypeWithSuffix_CodeFix_RemovesAsyncSuffix()
+ => CreateProjectBuilder()
+ .WithSourceCode("""
+ class TypeName
+ {
+ void {|MA0138:TestAsync|}() => throw null;
+ void Caller() { TestAsync(); }
+ }
+ """)
+ .ShouldFixCodeWith("""
+ class TypeName
+ {
+ void Test() => throw null;
+ void Caller() { Test(); }
+ }
+ """)
+ .ValidateAsync();
+
+ [Fact]
+ public Task VoidLocalFunctionWithSuffix_CodeFix_RemovesAsyncSuffix()
+ => CreateProjectBuilder()
+ .WithSourceCode("""
+ class TypeName
+ {
+ void Test()
+ {
+ void {|MA0138:FooAsync|}() => throw null;
+ FooAsync();
+ }
+ }
+ """)
+ .ShouldFixCodeWith("""
+ class TypeName
+ {
+ void Test()
+ {
+ void Foo() => throw null;
+ Foo();
+ }
+ }
+ """)
+ .ValidateAsync();
+
[Fact]
public Task IgnoreTestMethods_ExcludeTestMethodsTrue()
=> CreateProjectBuilder()