Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/ALCops.Mcp/Services/CodeFixRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,16 @@ public async Task<IReadOnlyList<CodeFixInfo>> 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))
Expand Down
12 changes: 10 additions & 2 deletions src/ALCops.Mcp/Services/DiagnosticsRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,23 @@ public async Task<IReadOnlyList<DiagnosticResult>> 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<DiagnosticResult>();
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)
Expand Down
50 changes: 50 additions & 0 deletions tests/ALCops.Mcp.Tests/DiagnosticsRunnerTests.cs
Original file line number Diff line number Diff line change
@@ -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))}.");
}
}
9 changes: 9 additions & 0 deletions tests/ALCops.Mcp.Tests/Fixtures/PragmaProject/WithPragma.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma warning disable
codeunit 50101 "With Pragma"
{
procedure PublicProcedureWithoutDocs()
begin
Message('Hello');
end;
}
#pragma warning restore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
codeunit 50100 "Without Pragma"
{
procedure PublicProcedureWithoutDocs()
begin
Message('Hello');
end;
}
9 changes: 9 additions & 0 deletions tests/ALCops.Mcp.Tests/Fixtures/PragmaProject/app.json
Original file line number Diff line number Diff line change
@@ -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"
}
Loading