diff --git a/src/ALCops.Mcp/Services/CodeFixRunner.cs b/src/ALCops.Mcp/Services/CodeFixRunner.cs index 9a642c6..fb76742 100644 --- a/src/ALCops.Mcp/Services/CodeFixRunner.cs +++ b/src/ALCops.Mcp/Services/CodeFixRunner.cs @@ -173,11 +173,16 @@ public async Task> GetFixesAsync( var compilationWithAnalyzers = new CompilationWithAnalyzers( compilation, analyzers, null!, ct); - var allDiagnostics = await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync(); + var rawDiagnostics = await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync(); + + // Apply pragma suppression filtering (same as DiagnosticsRunner) + var effectiveDiagnostics = CompilationWithAnalyzers + .GetEffectiveDiagnostics(rawDiagnostics, compilation); // Filter to the target file and diagnostic ID var documentPath = document.FilePath ?? ""; - var diagnostics = allDiagnostics + var diagnostics = effectiveDiagnostics + .Where(d => !d.IsSuppressed) .Where(d => d.Id == diagnosticId && d.Location.SourceTree?.FilePath is string fp && Path.GetFullPath(fp).Equals(Path.GetFullPath(documentPath), StringComparison.OrdinalIgnoreCase)) diff --git a/src/ALCops.Mcp/Services/DiagnosticsRunner.cs b/src/ALCops.Mcp/Services/DiagnosticsRunner.cs index f6713f4..49ae871 100644 --- a/src/ALCops.Mcp/Services/DiagnosticsRunner.cs +++ b/src/ALCops.Mcp/Services/DiagnosticsRunner.cs @@ -45,15 +45,23 @@ public async Task> RunAsync( var compilationWithAnalyzers = new CompilationWithAnalyzers( compilation, analyzers, null!, ct); - var allDiagnostics = await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync(); + var rawDiagnostics = await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync(); + + // GetAnalyzerDiagnosticsAsync returns raw diagnostics without pragma suppression. + // Apply CompilationOptions filtering to honor #pragma warning disable/restore. + var effectiveDiagnostics = CompilationWithAnalyzers + .GetEffectiveDiagnostics(rawDiagnostics, compilation); // Apply filters var results = new List(); - foreach (var diagnostic in allDiagnostics) + foreach (var diagnostic in effectiveDiagnostics) { if (ct.IsCancellationRequested) break; + if (diagnostic.IsSuppressed) + continue; + // Ruleset filter: suppress diagnostics set to None, override severity for others var effectiveSeverity = diagnostic.Severity; if (provider is AnalyzerSet analyzerSet && analyzerSet.RuleActions is { } ruleActions) diff --git a/tests/ALCops.Mcp.Tests/DiagnosticsRunnerTests.cs b/tests/ALCops.Mcp.Tests/DiagnosticsRunnerTests.cs new file mode 100644 index 0000000..432c720 --- /dev/null +++ b/tests/ALCops.Mcp.Tests/DiagnosticsRunnerTests.cs @@ -0,0 +1,50 @@ +using ALCops.Mcp.Services; +using Xunit; + +namespace ALCops.Mcp.Tests; + +public class DiagnosticsRunnerTests +{ + private static string GetFixturePath(string name) + { + var path = Path.Combine(AppContext.BaseDirectory, "Fixtures", name); + if (!Directory.Exists(path)) + throw new DirectoryNotFoundException( + $"Test fixture '{name}' not found at {path}. Ensure fixtures are copied to output."); + return path; + } + + private static ProjectLoader CreateLoader() + { + var locator = new DevToolsLocator(); + return new ProjectLoader(locator); + } + + [Fact] + public async Task RunAsync_PragmaSuppressedDiagnostics_AreNotReported() + { + var loader = CreateLoader(); + var projectPath = GetFixturePath("PragmaProject"); + var session = await loader.LoadProjectAsync(projectPath); + var registry = new AnalyzerRegistry(); + var runner = new DiagnosticsRunner(registry); + + var withoutPragmaPath = Path.Combine(projectPath, "WithoutPragma.al"); + var withPragmaPath = Path.Combine(projectPath, "WithPragma.al"); + + var withoutPragmaResults = await runner.RunAsync(session, filePath: withoutPragmaPath); + var withPragmaResults = await runner.RunAsync(session, filePath: withPragmaPath); + + // The unsuppressed file must produce diagnostics for the test to be meaningful. + Assert.True(withoutPragmaResults.Count > 0, + "No diagnostics produced for the unsuppressed file — " + + "built-in analyzers may not be loaded in this environment."); + + Assert.True( + withPragmaResults.Count < withoutPragmaResults.Count, + $"Expected pragma-suppressed file to produce fewer diagnostics " + + $"({withPragmaResults.Count}) than unsuppressed file ({withoutPragmaResults.Count}). " + + $"Unsuppressed diagnostics: {string.Join(", ", withoutPragmaResults.Select(d => d.Id))}. " + + $"Pragma-suppressed diagnostics: {string.Join(", ", withPragmaResults.Select(d => d.Id))}."); + } +} diff --git a/tests/ALCops.Mcp.Tests/Fixtures/PragmaProject/WithPragma.al b/tests/ALCops.Mcp.Tests/Fixtures/PragmaProject/WithPragma.al new file mode 100644 index 0000000..1bd5490 --- /dev/null +++ b/tests/ALCops.Mcp.Tests/Fixtures/PragmaProject/WithPragma.al @@ -0,0 +1,9 @@ +#pragma warning disable +codeunit 50101 "With Pragma" +{ + procedure PublicProcedureWithoutDocs() + begin + Message('Hello'); + end; +} +#pragma warning restore diff --git a/tests/ALCops.Mcp.Tests/Fixtures/PragmaProject/WithoutPragma.al b/tests/ALCops.Mcp.Tests/Fixtures/PragmaProject/WithoutPragma.al new file mode 100644 index 0000000..ce61703 --- /dev/null +++ b/tests/ALCops.Mcp.Tests/Fixtures/PragmaProject/WithoutPragma.al @@ -0,0 +1,7 @@ +codeunit 50100 "Without Pragma" +{ + procedure PublicProcedureWithoutDocs() + begin + Message('Hello'); + end; +} diff --git a/tests/ALCops.Mcp.Tests/Fixtures/PragmaProject/app.json b/tests/ALCops.Mcp.Tests/Fixtures/PragmaProject/app.json new file mode 100644 index 0000000..bf8cde2 --- /dev/null +++ b/tests/ALCops.Mcp.Tests/Fixtures/PragmaProject/app.json @@ -0,0 +1,9 @@ +{ + "id": "00000000-0000-0000-0000-000000000002", + "name": "PragmaTestFixture", + "publisher": "ALCops", + "version": "1.0.0.0", + "platform": "25.0.0.0", + "runtime": "15.0", + "target": "OnPrem" +}