diff --git a/.editorconfig b/.editorconfig
index 25b8401..ba38d52 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -397,6 +397,7 @@ dotnet_diagnostic.CA1707.severity = none # Identifiers should not contain unders
dotnet_diagnostic.CA1716.severity = none # Identifiers should not match keywords
dotnet_diagnostic.CA1806.severity = none # Ignore method results (not working properly on all cases)
dotnet_diagnostic.CA2007.severity = none # Consider calling ConfigureAwait on the awaited task
+dotnet_diagnostic.VSTHRD202.severity = warning # Remove unnecessary async state machine (flagged by SonarQube)
dotnet_diagnostic.CS1574.severity = none # XML comment has cref attribute that could not be resolved (boring with {} generics)
dotnet_diagnostic.CS1591.severity = none # Missing XML comment for publicly visible type or member
diff --git a/src/ProjGraph.Lib.ClassDiagram/Application/ClassAnalysisService.cs b/src/ProjGraph.Lib.ClassDiagram/Application/ClassAnalysisService.cs
index 4ac36f4..b4dcfee 100644
--- a/src/ProjGraph.Lib.ClassDiagram/Application/ClassAnalysisService.cs
+++ b/src/ProjGraph.Lib.ClassDiagram/Application/ClassAnalysisService.cs
@@ -15,18 +15,18 @@ public class ClassAnalysisService(
: IClassAnalysisService
{
///
- public async Task AnalyzeFileAsync(
+ public Task AnalyzeFileAsync(
string filePath,
AnalysisOptions? options = null)
{
- return await analyzeFileUseCase.ExecuteAsync(filePath, options);
+ return analyzeFileUseCase.ExecuteAsync(filePath, options);
}
///
- public async Task AnalyzeDirectoryAsync(
+ public Task AnalyzeDirectoryAsync(
string directoryPath,
AnalysisOptions? options = null)
{
- return await analyzeDirectoryUseCase.ExecuteAsync(directoryPath, options);
+ return analyzeDirectoryUseCase.ExecuteAsync(directoryPath, options);
}
}
diff --git a/src/ProjGraph.Lib.ClassDiagram/Infrastructure/TypeProcessor.cs b/src/ProjGraph.Lib.ClassDiagram/Infrastructure/TypeProcessor.cs
index 11ee568..df154b9 100644
--- a/src/ProjGraph.Lib.ClassDiagram/Infrastructure/TypeProcessor.cs
+++ b/src/ProjGraph.Lib.ClassDiagram/Infrastructure/TypeProcessor.cs
@@ -18,12 +18,12 @@ public sealed class TypeProcessor(ISymbolResolver symbolResolver) : ITypeProcess
/// The containing the current state of the analysis.
/// The controlling the analysis behavior.
/// A task that represents the asynchronous operation.
- public async Task ProcessTypeQueueAsync(
+ public Task ProcessTypeQueueAsync(
Queue<(INamedTypeSymbol Symbol, int Depth)> typesToAnalyze,
AnalysisContext context,
AnalysisOptions options)
{
- await ProcessTypeQueueInternalAsync(typesToAnalyze, context, options);
+ return ProcessTypeQueueInternalAsync(typesToAnalyze, context, options);
}
///
diff --git a/src/ProjGraph.Lib.ClassDiagram/Infrastructure/WorkspaceTypeDiscovery.cs b/src/ProjGraph.Lib.ClassDiagram/Infrastructure/WorkspaceTypeDiscovery.cs
index 4c6f87c..46069ad 100644
--- a/src/ProjGraph.Lib.ClassDiagram/Infrastructure/WorkspaceTypeDiscovery.cs
+++ b/src/ProjGraph.Lib.ClassDiagram/Infrastructure/WorkspaceTypeDiscovery.cs
@@ -25,14 +25,14 @@ internal sealed class WorkspaceTypeDiscovery(IFileSystem fileSystem) : IWorkspac
/// A task that represents the asynchronous operation. The task result contains the full path of the file
/// containing the type definition if found; otherwise, null.
///
- public async Task FindTypeDefinitionFileAsync(string typeName, string startDirectory)
+ public Task FindTypeDefinitionFileAsync(string typeName, string startDirectory)
{
var root = WorkspaceRootResolver.FindWorkspaceRoot(startDirectory) ?? startDirectory;
// A single scan from the workspace root keeps resolution deterministic: a partial
// "common directory" pre-pass would return its first hit and override the path-sorted
// tie-break applied below. Lookups are memoized per type name by the caller.
- return await SearchDirectoryForTypeAsync(root, typeName);
+ return SearchDirectoryForTypeAsync(root, typeName);
}
///
diff --git a/src/ProjGraph.Lib.Core/Infrastructure/SpectreOutputConsole.cs b/src/ProjGraph.Lib.Core/Infrastructure/SpectreOutputConsole.cs
index 2283c19..714cce7 100644
--- a/src/ProjGraph.Lib.Core/Infrastructure/SpectreOutputConsole.cs
+++ b/src/ProjGraph.Lib.Core/Infrastructure/SpectreOutputConsole.cs
@@ -125,10 +125,10 @@ public void WriteMarkup(string markup)
}
///
- public async Task PromptSelectionAsync(string title, IEnumerable choices,
+ public Task PromptSelectionAsync(string title, IEnumerable choices,
CancellationToken cancellationToken = default)
{
- return await AnsiConsole.PromptAsync(
+ return AnsiConsole.PromptAsync(
new SelectionPrompt()
.Title(title)
.AddChoices(choices),
@@ -136,12 +136,12 @@ public async Task PromptSelectionAsync(string title, IEnumerable
}
///
- public async Task RunWithStatusAsync(string statusMessage, Func action,
+ public Task RunWithStatusAsync(string statusMessage, Func action,
CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
- await AnsiConsole.Status()
+ return AnsiConsole.Status()
.Spinner(Spinner.Known.Dots)
.StartAsync(statusMessage, async ctx =>
{
diff --git a/src/ProjGraph.Lib.EntityFramework/Application/EfAnalysisService.cs b/src/ProjGraph.Lib.EntityFramework/Application/EfAnalysisService.cs
index 8e0ff52..9913f2a 100644
--- a/src/ProjGraph.Lib.EntityFramework/Application/EfAnalysisService.cs
+++ b/src/ProjGraph.Lib.EntityFramework/Application/EfAnalysisService.cs
@@ -18,26 +18,26 @@ public class EfAnalysisService(
DiscoverSnapshotsUseCase discoverSnapshotsUseCase) : IEfAnalysisService
{
///
- public async Task> DiscoverContextsAsync(string path)
+ public Task> DiscoverContextsAsync(string path)
{
- return await discoverContextsUseCase.ExecuteAsync(path);
+ return discoverContextsUseCase.ExecuteAsync(path);
}
///
- public async Task AnalyzeContextAsync(string path, string? contextName = null)
+ public Task AnalyzeContextAsync(string path, string? contextName = null)
{
- return await analyzeContextUseCase.ExecuteAsync(path, contextName);
+ return analyzeContextUseCase.ExecuteAsync(path, contextName);
}
///
- public async Task> DiscoverSnapshotsAsync(string path)
+ public Task> DiscoverSnapshotsAsync(string path)
{
- return await discoverSnapshotsUseCase.ExecuteAsync(path);
+ return discoverSnapshotsUseCase.ExecuteAsync(path);
}
///
- public async Task AnalyzeSnapshotAsync(string path, string? snapshotName = null)
+ public Task AnalyzeSnapshotAsync(string path, string? snapshotName = null)
{
- return await analyzeSnapshotUseCase.ExecuteAsync(path, snapshotName);
+ return analyzeSnapshotUseCase.ExecuteAsync(path, snapshotName);
}
}
diff --git a/src/ProjGraph.Lib.EntityFramework/Application/UseCases/AnalyzeContextUseCase.cs b/src/ProjGraph.Lib.EntityFramework/Application/UseCases/AnalyzeContextUseCase.cs
index f9f05a2..c933f4a 100644
--- a/src/ProjGraph.Lib.EntityFramework/Application/UseCases/AnalyzeContextUseCase.cs
+++ b/src/ProjGraph.Lib.EntityFramework/Application/UseCases/AnalyzeContextUseCase.cs
@@ -16,9 +16,9 @@ public class AnalyzeContextUseCase(IEfModelAnalyzer modelAnalyzer)
/// The optional name of the DbContext class to analyze.
/// A task that represents the asynchronous operation. The task result contains the analyzed Entity Framework model.
/// Thrown when the provided file path is not a .cs file.
- public async Task ExecuteAsync(string path, string? contextName = null)
+ public Task ExecuteAsync(string path, string? contextName = null)
{
FilePathGuard.RequireCsFile(path);
- return await modelAnalyzer.AnalyzeContextAsync(path, contextName);
+ return modelAnalyzer.AnalyzeContextAsync(path, contextName);
}
}
diff --git a/src/ProjGraph.Lib.EntityFramework/Application/UseCases/AnalyzeSnapshotUseCase.cs b/src/ProjGraph.Lib.EntityFramework/Application/UseCases/AnalyzeSnapshotUseCase.cs
index 8c3f4f0..02793eb 100644
--- a/src/ProjGraph.Lib.EntityFramework/Application/UseCases/AnalyzeSnapshotUseCase.cs
+++ b/src/ProjGraph.Lib.EntityFramework/Application/UseCases/AnalyzeSnapshotUseCase.cs
@@ -16,10 +16,10 @@ public class AnalyzeSnapshotUseCase(IEfModelAnalyzer modelAnalyzer)
/// The optional name of the ModelSnapshot class to analyze.
/// A task that represents the asynchronous operation. The task result contains the analyzed Entity Framework model.
/// Thrown when the provided file path is not a .cs file.
- public async Task ExecuteAsync(string path, string? snapshotName = null)
+ public Task ExecuteAsync(string path, string? snapshotName = null)
{
FilePathGuard.RequireCsFile(path);
- return await modelAnalyzer.AnalyzeSnapshotAsync(path, snapshotName);
+ return modelAnalyzer.AnalyzeSnapshotAsync(path, snapshotName);
}
}
diff --git a/src/ProjGraph.Lib.EntityFramework/Infrastructure/EntityFileDiscovery.cs b/src/ProjGraph.Lib.EntityFramework/Infrastructure/EntityFileDiscovery.cs
index 75b5c26..30658d8 100644
--- a/src/ProjGraph.Lib.EntityFramework/Infrastructure/EntityFileDiscovery.cs
+++ b/src/ProjGraph.Lib.EntityFramework/Infrastructure/EntityFileDiscovery.cs
@@ -302,13 +302,13 @@ private static bool IsMigrationClass(TypeDeclarationSyntax typeDecl)
/// Any access errors encountered during directory traversal are ignored.
/// Directories like bin, obj, .git, and node_modules are skipped during traversal for performance.
///
- private async Task SearchDirectoryForEntitiesAsync(
+ private Task SearchDirectoryForEntitiesAsync(
string searchDir,
HashSet entityTypeNames,
string normalizedContextPath,
Dictionary entityFiles)
{
- await SearchDirectoryRecursiveAsync(searchDir, entityTypeNames, normalizedContextPath, entityFiles);
+ return SearchDirectoryRecursiveAsync(searchDir, entityTypeNames, normalizedContextPath, entityFiles);
}
///
diff --git a/src/ProjGraph.Mcp/Program.cs b/src/ProjGraph.Mcp/Program.cs
index fd3380d..c5c5b21 100644
--- a/src/ProjGraph.Mcp/Program.cs
+++ b/src/ProjGraph.Mcp/Program.cs
@@ -17,7 +17,7 @@ namespace ProjGraph.Mcp;
internal static class Program
{
- public static async Task Main(string[] args)
+ public static Task Main(string[] args)
{
var version = typeof(Program).Assembly
.GetCustomAttribute()
@@ -70,6 +70,6 @@ public static async Task Main(string[] args)
builder.Services.AddSingleton();
var host = builder.Build();
- await host.RunAsync();
+ return host.RunAsync();
}
}
diff --git a/src/ProjGraph.Mcp/ProjGraphTools.cs b/src/ProjGraph.Mcp/ProjGraphTools.cs
index 96c325f..8b5691f 100644
--- a/src/ProjGraph.Mcp/ProjGraphTools.cs
+++ b/src/ProjGraph.Mcp/ProjGraphTools.cs
@@ -459,7 +459,7 @@ private static async Task RunAnalysisAsync(Func> analysis)
/// A token to cancel the operation.
/// The resolved absolute path.
/// Thrown when the path is empty or cannot be resolved.
- private async Task PreparePathAsync(string path, McpServer server, CancellationToken cancellationToken)
+ private Task PreparePathAsync(string path, McpServer server, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (string.IsNullOrWhiteSpace(path))
@@ -469,7 +469,7 @@ private async Task PreparePathAsync(string path, McpServer server, Cance
throw new McpException("path must not be empty.");
}
- return await rootService.TryResolveAsync(path, server, cancellationToken);
+ return rootService.TryResolveAsync(path, server, cancellationToken);
}
private static void RequireCsFile(string path)
diff --git a/tests/ProjGraph.Tests.Integration.Mcp/Helpers/McpServerProcess.cs b/tests/ProjGraph.Tests.Integration.Mcp/Helpers/McpServerProcess.cs
index efb0cc9..13ca074 100644
--- a/tests/ProjGraph.Tests.Integration.Mcp/Helpers/McpServerProcess.cs
+++ b/tests/ProjGraph.Tests.Integration.Mcp/Helpers/McpServerProcess.cs
@@ -17,7 +17,7 @@ internal static class McpServerProcess
/// The client deliberately advertises no capabilities — in particular no workspace roots.
///
/// A connected client; disposing it stops the server process.
- public static async Task ConnectAsync()
+ public static Task ConnectAsync()
{
var transport = new StdioClientTransport(new StdioClientTransportOptions
{
@@ -25,7 +25,7 @@ public static async Task ConnectAsync()
Command = LocateServerExecutable()
});
- return await McpClient.CreateAsync(transport);
+ return McpClient.CreateAsync(transport);
}
private static string LocateServerExecutable()
diff --git a/tests/ProjGraph.Tests.Smoke.Aot/CliParityTests.cs b/tests/ProjGraph.Tests.Smoke.Aot/CliParityTests.cs
index d6c17a5..75b786e 100644
--- a/tests/ProjGraph.Tests.Smoke.Aot/CliParityTests.cs
+++ b/tests/ProjGraph.Tests.Smoke.Aot/CliParityTests.cs
@@ -20,74 +20,74 @@ public void Dispose()
}
[AotSmokeFact]
- public async Task Visualize_Mermaid_ShouldMatchReference()
+ public Task Visualize_Mermaid_ShouldMatchReference()
{
- await AssertDiagramParityAsync("visualize", ModularSolution, "--format", "mermaid");
+ return AssertDiagramParityAsync("visualize", ModularSolution, "--format", "mermaid");
}
[AotSmokeFact]
- public async Task Visualize_Tree_ShouldMatchReference()
+ public Task Visualize_Tree_ShouldMatchReference()
{
- await AssertDiagramParityAsync("visualize", ModularSolution, "--format", "tree");
+ return AssertDiagramParityAsync("visualize", ModularSolution, "--format", "tree");
}
[AotSmokeFact]
- public async Task Visualize_Flat_ShouldMatchReference()
+ public Task Visualize_Flat_ShouldMatchReference()
{
- await AssertDiagramParityAsync("visualize", ModularSolution, "--format", "flat");
+ return AssertDiagramParityAsync("visualize", ModularSolution, "--format", "flat");
}
[AotSmokeFact]
- public async Task Visualize_LegacySln_ShouldMatchReference()
+ public Task Visualize_LegacySln_ShouldMatchReference()
{
- await AssertDiagramParityAsync(
+ return AssertDiagramParityAsync(
"visualize", "tests/ProjGraph.Tests.Smoke.Aot/Fixtures/legacy.sln", "--format", "mermaid");
}
[AotSmokeFact]
- public async Task Erd_ComplexEcommerce_ShouldMatchReference()
+ public Task Erd_ComplexEcommerce_ShouldMatchReference()
{
- await AssertDiagramParityAsync("erd", "samples/erd/complex-ecommerce/Data/MyDbContext.cs");
+ return AssertDiagramParityAsync("erd", "samples/erd/complex-ecommerce/Data/MyDbContext.cs");
}
[AotSmokeFact]
- public async Task Erd_SimpleContext_ShouldMatchReference()
+ public Task Erd_SimpleContext_ShouldMatchReference()
{
- await AssertDiagramParityAsync("erd", "samples/erd/simple-context/EntityFramework/MyDbContext.cs");
+ return AssertDiagramParityAsync("erd", "samples/erd/simple-context/EntityFramework/MyDbContext.cs");
}
[AotSmokeFact]
- public async Task ClassDiagram_DesignPatterns_ShouldMatchReference()
+ public Task ClassDiagram_DesignPatterns_ShouldMatchReference()
{
- await AssertDiagramParityAsync(
+ return AssertDiagramParityAsync(
"classdiagram", "samples/classdiagram/design-patterns/Domain/Order.cs",
"--inheritance", "--dependencies", "--depth", "2", "--properties", "true", "--functions", "true");
}
[AotSmokeFact]
- public async Task ClassDiagram_ComplexHierarchy_ShouldMatchReference()
+ public Task ClassDiagram_ComplexHierarchy_ShouldMatchReference()
{
- await AssertDiagramParityAsync(
+ return AssertDiagramParityAsync(
"classdiagram", "samples/classdiagram/complex-hierarchy/Domain/Models/CEO.cs",
"--inheritance", "--dependencies", "--depth", "5", "--properties", "true", "--functions", "true");
}
[AotSmokeFact]
- public async Task ClassDiagram_SimpleHierarchy_ShouldMatchReference()
+ public Task ClassDiagram_SimpleHierarchy_ShouldMatchReference()
{
- await AssertDiagramParityAsync(
+ return AssertDiagramParityAsync(
"classdiagram", "samples/classdiagram/simple-hierarchy/Models/Admin.cs",
"--inheritance", "--dependencies", "--depth", "2", "--properties", "true", "--functions", "true");
}
[AotSmokeFact]
- public async Task ClassDiagram_SourceDirectory_ShouldMatchReference()
+ public Task ClassDiagram_SourceDirectory_ShouldMatchReference()
{
- await AssertDiagramParityAsync("classdiagram", "src", "--inheritance", "--dependencies");
+ return AssertDiagramParityAsync("classdiagram", "src", "--inheritance", "--dependencies");
}
[AotSmokeFact]
- public async Task ClassDiagram_ConcurrencyAndAnnotationTypes_ShouldMatchReference()
+ public Task ClassDiagram_ConcurrencyAndAnnotationTypes_ShouldMatchReference()
{
// BCL types that bind only through the embedded reference assemblies (spec §1.5).
_temp.CreateFile("Types/Types.csproj",
@@ -116,15 +116,15 @@ public class Inventory : Dictionary, IDisposable
public record StockItem(string Sku, decimal Price);
""");
- await AssertDiagramParityAsync("classdiagram", file, "--inheritance", "--dependencies");
+ return AssertDiagramParityAsync("classdiagram", file, "--inheritance", "--dependencies");
}
[AotSmokeFact]
- public async Task Visualize_SolutionWithMalformedProject_ShouldMatchReference()
+ public Task Visualize_SolutionWithMalformedProject_ShouldMatchReference()
{
var solution = CreateSolutionWithMalformedProject();
- await AssertDiagramParityAsync("visualize", solution, "--format", "mermaid");
+ return AssertDiagramParityAsync("visualize", solution, "--format", "mermaid");
}
[AotSmokeFact]
@@ -145,17 +145,17 @@ public async Task Visualize_SlnListingAProjectTwice_ShouldFailLikeReference()
}
[AotSmokeFact]
- public async Task Stats_Repository_ShouldMatchReferenceApartFromTiming()
+ public Task Stats_Repository_ShouldMatchReferenceApartFromTiming()
{
- await AssertStatsParityAsync("stats", "ProjGraph.slnx");
+ return AssertStatsParityAsync("stats", "ProjGraph.slnx");
}
[AotSmokeFact]
- public async Task Stats_SolutionWithMalformedProject_ShouldMatchReferenceApartFromTiming()
+ public Task Stats_SolutionWithMalformedProject_ShouldMatchReferenceApartFromTiming()
{
var solution = CreateSolutionWithMalformedProject();
- await AssertStatsParityAsync("stats", solution);
+ return AssertStatsParityAsync("stats", solution);
}
[AotSmokeFact]
diff --git a/tests/ProjGraph.Tests.Smoke.Aot/Helpers/McpServerPair.cs b/tests/ProjGraph.Tests.Smoke.Aot/Helpers/McpServerPair.cs
index 16268fd..852b733 100644
--- a/tests/ProjGraph.Tests.Smoke.Aot/Helpers/McpServerPair.cs
+++ b/tests/ProjGraph.Tests.Smoke.Aot/Helpers/McpServerPair.cs
@@ -57,7 +57,7 @@ public async Task DisposeAsync()
}
}
- private static async Task ConnectAsync(SmokeCommand command, ConcurrentQueue standardError)
+ private static Task ConnectAsync(SmokeCommand command, ConcurrentQueue standardError)
{
var transport = new StdioClientTransport(new StdioClientTransportOptions
{
@@ -68,6 +68,6 @@ private static async Task ConnectAsync(SmokeCommand command, Concurre
StandardErrorLines = standardError.Enqueue
});
- return await McpClient.CreateAsync(transport);
+ return McpClient.CreateAsync(transport);
}
}