Skip to content

Commit dfa7fc6

Browse files
authored
Include project diagnostic suppressors in host analyzer execution (#75684)
Fixes #75399
2 parents 913fb4e + aa3df16 commit dfa7fc6

File tree

7 files changed

+310
-121
lines changed

7 files changed

+310
-121
lines changed

src/EditorFeatures/CSharpTest/Diagnostics/DiagnosticAnalyzerDriver/DiagnosticAnalyzerDriverTests.cs

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -656,28 +656,21 @@ await TestNuGetAndVsixAnalyzerCoreAsync(
656656
// 1) No duplicate diagnostics
657657
// 2) Both NuGet and Vsix analyzers execute
658658
// 3) Appropriate diagnostic filtering is done - Nuget suppressor suppresses VSIX analyzer.
659-
//
660-
// 🐛 After splitting fallback options into separate CompilationWithAnalyzers for project and host analyzers,
661-
// NuGet-installed suppressors no longer act on VSIX-installed analyzer diagnostics. Fixing this requires us to
662-
// add NuGet-installed analyzer references to the host CompilationWithAnalyzers, with an additional flag
663-
// indicating that only suppressors should run for these references.
664-
// https://github.com/dotnet/roslyn/issues/75399
665-
const bool FalseButShouldBeTrue = false;
666659
await TestNuGetAndVsixAnalyzerCoreAsync(
667660
nugetAnalyzers: ImmutableArray.Create(firstNugetAnalyzer),
668661
expectedNugetAnalyzersExecuted: true,
669662
vsixAnalyzers: ImmutableArray.Create(vsixAnalyzer),
670663
expectedVsixAnalyzersExecuted: true,
671664
nugetSuppressors: ImmutableArray.Create(nugetSuppressor),
672-
expectedNugetSuppressorsExecuted: FalseButShouldBeTrue,
665+
expectedNugetSuppressorsExecuted: true,
673666
vsixSuppressors: ImmutableArray<VsixSuppressor>.Empty,
674667
expectedVsixSuppressorsExecuted: false,
675668
new[]
676669
{
677670
(Diagnostic("A", "Class").WithLocation(1, 7), nameof(NuGetAnalyzer)),
678-
(Diagnostic("X", "Class", isSuppressed: FalseButShouldBeTrue).WithLocation(1, 7), nameof(VsixAnalyzer)),
679-
(Diagnostic("Y", "Class", isSuppressed: FalseButShouldBeTrue).WithLocation(1, 7), nameof(VsixAnalyzer)),
680-
(Diagnostic("Z", "Class", isSuppressed: FalseButShouldBeTrue).WithLocation(1, 7), nameof(VsixAnalyzer))
671+
(Diagnostic("X", "Class", isSuppressed: true).WithLocation(1, 7), nameof(VsixAnalyzer)),
672+
(Diagnostic("Y", "Class", isSuppressed: true).WithLocation(1, 7), nameof(VsixAnalyzer)),
673+
(Diagnostic("Z", "Class", isSuppressed: true).WithLocation(1, 7), nameof(VsixAnalyzer))
681674
});
682675

683676
// Suppressors with duplicate support for VsixAnalyzer, but not 100% overlap. Verify the following:
@@ -691,15 +684,15 @@ await TestNuGetAndVsixAnalyzerCoreAsync(
691684
vsixAnalyzers: ImmutableArray.Create(vsixAnalyzer),
692685
expectedVsixAnalyzersExecuted: true,
693686
nugetSuppressors: ImmutableArray.Create(partialNugetSuppressor),
694-
expectedNugetSuppressorsExecuted: FalseButShouldBeTrue,
687+
expectedNugetSuppressorsExecuted: true,
695688
vsixSuppressors: ImmutableArray.Create(vsixSuppressor),
696689
expectedVsixSuppressorsExecuted: false,
697690
new[]
698691
{
699692
(Diagnostic("A", "Class").WithLocation(1, 7), nameof(NuGetAnalyzer)),
700693
(Diagnostic("X", "Class", isSuppressed: false).WithLocation(1, 7), nameof(VsixAnalyzer)),
701-
(Diagnostic("Y", "Class", isSuppressed: FalseButShouldBeTrue).WithLocation(1, 7), nameof(VsixAnalyzer)),
702-
(Diagnostic("Z", "Class", isSuppressed: FalseButShouldBeTrue).WithLocation(1, 7), nameof(VsixAnalyzer))
694+
(Diagnostic("Y", "Class", isSuppressed: true).WithLocation(1, 7), nameof(VsixAnalyzer)),
695+
(Diagnostic("Z", "Class", isSuppressed: true).WithLocation(1, 7), nameof(VsixAnalyzer))
703696
});
704697

705698
// Suppressors with duplicate support for VsixAnalyzer, with 100% overlap. Verify the following:
@@ -713,15 +706,15 @@ await TestNuGetAndVsixAnalyzerCoreAsync(
713706
vsixAnalyzers: ImmutableArray.Create(vsixAnalyzer),
714707
expectedVsixAnalyzersExecuted: true,
715708
nugetSuppressors: ImmutableArray.Create(nugetSuppressor),
716-
expectedNugetSuppressorsExecuted: FalseButShouldBeTrue,
709+
expectedNugetSuppressorsExecuted: true,
717710
vsixSuppressors: ImmutableArray.Create(vsixSuppressor),
718711
expectedVsixSuppressorsExecuted: false,
719712
new[]
720713
{
721714
(Diagnostic("A", "Class").WithLocation(1, 7), nameof(NuGetAnalyzer)),
722-
(Diagnostic("X", "Class", isSuppressed: FalseButShouldBeTrue).WithLocation(1, 7), nameof(VsixAnalyzer)),
723-
(Diagnostic("Y", "Class", isSuppressed: FalseButShouldBeTrue).WithLocation(1, 7), nameof(VsixAnalyzer)),
724-
(Diagnostic("Z", "Class", isSuppressed: FalseButShouldBeTrue).WithLocation(1, 7), nameof(VsixAnalyzer))
715+
(Diagnostic("X", "Class", isSuppressed: true).WithLocation(1, 7), nameof(VsixAnalyzer)),
716+
(Diagnostic("Y", "Class", isSuppressed: true).WithLocation(1, 7), nameof(VsixAnalyzer)),
717+
(Diagnostic("Z", "Class", isSuppressed: true).WithLocation(1, 7), nameof(VsixAnalyzer))
725718
});
726719
}
727720

src/LanguageServer/Protocol/Features/Diagnostics/DocumentAnalysisExecutor_Helpers.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ static string GetLanguageSpecificId(string? language, string noLanguageId, strin
146146
// Create driver that holds onto compilation and associated analyzers
147147
var filteredProjectAnalyzers = projectAnalyzers.WhereAsArray(static a => !a.IsWorkspaceDiagnosticAnalyzer());
148148
var filteredHostAnalyzers = hostAnalyzers.WhereAsArray(static a => !a.IsWorkspaceDiagnosticAnalyzer());
149+
var filteredProjectSuppressors = filteredProjectAnalyzers.WhereAsArray(static a => a is DiagnosticSuppressor);
150+
filteredHostAnalyzers = filteredHostAnalyzers.AddRange(filteredProjectSuppressors);
149151

150152
// PERF: there is no analyzers for this compilation.
151153
// compilationWithAnalyzer will throw if it is created with no analyzers which is perf optimization.

src/LanguageServer/Protocol/Features/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.InProcOrRemoteHostAnalyzerRunner.cs

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,14 @@ private async Task<DiagnosticAnalysisResultMap<DiagnosticAnalyzer, DiagnosticAna
122122
{
123123
var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false);
124124

125-
var (projectAnalysisResult, hostAnalysisResult, additionalPragmaSuppressionDiagnostics) = await compilationWithAnalyzers.GetAnalysisResultAsync(
125+
var (analysisResult, additionalPragmaSuppressionDiagnostics) = await compilationWithAnalyzers.GetAnalysisResultAsync(
126126
documentAnalysisScope, project, AnalyzerInfoCache, cancellationToken).ConfigureAwait(false);
127127

128128
if (logPerformanceInfo)
129129
{
130130
// if remote host is there, report performance data
131131
var asyncToken = _asyncOperationListener.BeginAsyncOperation(nameof(AnalyzeInProcAsync));
132-
var _ = FireAndForgetReportAnalyzerPerformanceAsync(documentAnalysisScope, project, client, projectAnalysisResult, hostAnalysisResult, cancellationToken).CompletesAsyncOperation(asyncToken);
132+
var _ = FireAndForgetReportAnalyzerPerformanceAsync(documentAnalysisScope, project, client, analysisResult, cancellationToken).CompletesAsyncOperation(asyncToken);
133133
}
134134

135135
var projectAnalyzers = documentAnalysisScope?.ProjectAnalyzers ?? compilationWithAnalyzers.ProjectAnalyzers;
@@ -138,37 +138,20 @@ private async Task<DiagnosticAnalysisResultMap<DiagnosticAnalyzer, DiagnosticAna
138138

139139
// get compiler result builder map
140140
var builderMap = ImmutableDictionary<DiagnosticAnalyzer, DiagnosticAnalysisResultBuilder>.Empty;
141-
if (projectAnalysisResult is not null)
141+
if (analysisResult is not null)
142142
{
143-
var map = await projectAnalysisResult.ToResultBuilderMapAsync(
143+
var map = await analysisResult.ToResultBuilderMapAsync(
144144
additionalPragmaSuppressionDiagnostics, documentAnalysisScope, project, version,
145-
compilationWithAnalyzers.ProjectCompilation!, projectAnalyzers, skippedAnalyzersInfo,
146-
compilationWithAnalyzers.ReportSuppressedDiagnostics, cancellationToken).ConfigureAwait(false);
147-
builderMap = builderMap.AddRange(map);
148-
}
149-
150-
if (hostAnalysisResult is not null)
151-
{
152-
var map = await hostAnalysisResult.ToResultBuilderMapAsync(
153-
additionalPragmaSuppressionDiagnostics, documentAnalysisScope, project, version,
154-
compilationWithAnalyzers.HostCompilation!, hostAnalyzers, skippedAnalyzersInfo,
145+
projectAnalyzers, hostAnalyzers, skippedAnalyzersInfo,
155146
compilationWithAnalyzers.ReportSuppressedDiagnostics, cancellationToken).ConfigureAwait(false);
156147
builderMap = builderMap.AddRange(map);
157148
}
158149

159150
var result = builderMap.ToImmutableDictionary(kv => kv.Key, kv => DiagnosticAnalysisResult.CreateFromBuilder(kv.Value));
160151
var telemetry = ImmutableDictionary<DiagnosticAnalyzer, AnalyzerTelemetryInfo>.Empty;
161-
if (getTelemetryInfo)
152+
if (getTelemetryInfo && analysisResult is not null)
162153
{
163-
if (projectAnalysisResult is not null)
164-
{
165-
telemetry = telemetry.AddRange(projectAnalysisResult.AnalyzerTelemetryInfo);
166-
}
167-
168-
if (hostAnalysisResult is not null)
169-
{
170-
telemetry = telemetry.AddRange(hostAnalysisResult.AnalyzerTelemetryInfo);
171-
}
154+
telemetry = analysisResult.MergedAnalyzerTelemetryInfo;
172155
}
173156

174157
return DiagnosticAnalysisResultMap.Create(result, telemetry);
@@ -178,8 +161,7 @@ private async Task FireAndForgetReportAnalyzerPerformanceAsync(
178161
DocumentAnalysisScope? documentAnalysisScope,
179162
Project project,
180163
RemoteHostClient? client,
181-
AnalysisResult? projectAnalysisResult,
182-
AnalysisResult? hostAnalysisResult,
164+
AnalysisResultPair? analysisResult,
183165
CancellationToken cancellationToken)
184166
{
185167
if (client == null)
@@ -194,14 +176,9 @@ private async Task FireAndForgetReportAnalyzerPerformanceAsync(
194176
var forSpanAnalysis = documentAnalysisScope?.Span.HasValue ?? false;
195177

196178
ImmutableArray<AnalyzerPerformanceInfo> performanceInfo = [];
197-
if (projectAnalysisResult is not null)
198-
{
199-
performanceInfo = performanceInfo.AddRange(projectAnalysisResult.AnalyzerTelemetryInfo.ToAnalyzerPerformanceInfo(AnalyzerInfoCache));
200-
}
201-
202-
if (hostAnalysisResult is not null)
179+
if (analysisResult is not null)
203180
{
204-
performanceInfo = performanceInfo.AddRange(hostAnalysisResult.AnalyzerTelemetryInfo.ToAnalyzerPerformanceInfo(AnalyzerInfoCache));
181+
performanceInfo = performanceInfo.AddRange(analysisResult.MergedAnalyzerTelemetryInfo.ToAnalyzerPerformanceInfo(AnalyzerInfoCache));
205182
}
206183

207184
_ = await client.TryInvokeAsync<IRemoteDiagnosticAnalyzerService>(

0 commit comments

Comments
 (0)