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); } }