Skip to content

Commit 7ead97f

Browse files
Compute diagnostic descriptor info in OOP process (#79812)
2 parents eb3d589 + 8f37819 commit 7ead97f

File tree

13 files changed

+244
-35
lines changed

13 files changed

+244
-35
lines changed

src/EditorFeatures/Core/EditorConfigSettings/DataProvider/Analyzer/AnalyzerSettingsProvider.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,34 @@ public AnalyzerSettingsProvider(
3434
}
3535

3636
protected override async Task UpdateOptionsAsync(
37-
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
37+
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
3838
{
3939
var analyzerReferences = RoslynEnumerableExtensions.DistinctBy(projectsInScope.SelectMany(p => p.AnalyzerReferences), a => a.Id).ToImmutableArray();
40+
using var _ = PooledDictionary<AnalyzerReference, Project>.GetInstance(out var analyzerReferenceToSomeReferencingProject);
41+
42+
foreach (var project in projectsInScope)
43+
{
44+
foreach (var analyzerReference in project.AnalyzerReferences)
45+
analyzerReferenceToSomeReferencingProject[analyzerReference] = project;
46+
47+
}
48+
4049
foreach (var analyzerReference in analyzerReferences)
4150
{
51+
var someReferencingProject = analyzerReferenceToSomeReferencingProject[analyzerReference];
4252
var configSettings = await GetSettingsAsync(
43-
solution, analyzerReference, options.EditorConfigOptions, cancellationToken).ConfigureAwait(false);
53+
someReferencingProject, analyzerReference, options.EditorConfigOptions, cancellationToken).ConfigureAwait(false);
4454
AddRange(configSettings);
4555
}
4656
}
4757

4858
private async Task<ImmutableArray<AnalyzerSetting>> GetSettingsAsync(
49-
Solution solution, AnalyzerReference analyzerReference, AnalyzerConfigOptions editorConfigOptions, CancellationToken cancellationToken)
59+
Project someReferencingProject, AnalyzerReference analyzerReference, AnalyzerConfigOptions editorConfigOptions, CancellationToken cancellationToken)
5060
{
61+
var solution = someReferencingProject.Solution;
5162
var service = solution.Services.GetRequiredService<IDiagnosticAnalyzerService>();
52-
var map = await service.GetDiagnosticDescriptorsAsync(
53-
solution, analyzerReference, cancellationToken).ConfigureAwait(false);
63+
var map = await service.GetLanguageKeyedDiagnosticDescriptorsAsync(
64+
solution, someReferencingProject.Id, analyzerReference, cancellationToken).ConfigureAwait(false);
5465

5566
using var _ = ArrayBuilder<AnalyzerSetting>.GetInstance(out var allSettings);
5667

src/EditorFeatures/Core/EditorConfigSettings/DataProvider/NamingStyles/NamingStyleSettingsProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public NamingStyleSettingsProvider(
3232
}
3333

3434
protected override Task UpdateOptionsAsync(
35-
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
35+
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
3636
{
3737
options.GetInitialLocationAndValue<NamingStylePreferences>(NamingStyleOptions.NamingPreferences, out var location, out var namingPreferences);
3838

src/EditorFeatures/Core/EditorConfigSettings/DataProvider/SettingsProviderBase.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
using Microsoft.CodeAnalysis.ErrorReporting;
2121
using Microsoft.CodeAnalysis.Options;
2222
using Microsoft.CodeAnalysis.Text;
23-
using Microsoft.VisualStudio.Shell;
2423
using Roslyn.Utilities;
2524

2625
namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider;
@@ -44,7 +43,7 @@ internal abstract class SettingsProviderBase<TData, TOptionsUpdater, TOption, TV
4443
public readonly IGlobalOptionService GlobalOptions = globalOptions;
4544

4645
protected abstract Task UpdateOptionsAsync(
47-
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken);
46+
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken);
4847

4948
protected void Update()
5049
{
@@ -74,7 +73,7 @@ protected void Update()
7473
language: LanguageNames.CSharp,
7574
editorConfigFileName: FileName);
7675

77-
_ = UpdateOptionsAsync(options, solution, projects, cancellationToken)
76+
_ = UpdateOptionsAsync(options, projects, cancellationToken)
7877
.ReportNonFatalErrorUnlessCancelledAsync(cancellationToken);
7978
}
8079

src/Features/Core/Portable/Diagnostics/DiagnosticAnalyzerExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,6 @@ public static Task<ImmutableArray<DiagnosticDescriptor>> GetDiagnosticDescriptor
5151
{
5252
var diagnosticAnalyzerService = project.Solution.Services.GetRequiredService<IDiagnosticAnalyzerService>();
5353
return diagnosticAnalyzerService.GetDiagnosticDescriptorsAsync(
54-
project.Solution, analyzerReference, project.Language, cancellationToken);
54+
project.Solution, project.Id, analyzerReference, project.Language, cancellationToken);
5555
}
5656
}

src/Features/Core/Portable/Diagnostics/IDiagnosticAnalyzerService.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,19 @@ Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForSpanAsync(
8181
DiagnosticKind diagnosticKind,
8282
CancellationToken cancellationToken);
8383

84+
/// <param name="projectId">A project within <paramref name="solution"/> where <paramref name="analyzerReference"/> can be found</param>
8485
Task<ImmutableArray<DiagnosticDescriptor>> GetDiagnosticDescriptorsAsync(
85-
Solution solution, AnalyzerReference analyzerReference, string language, CancellationToken cancellationToken);
86+
Solution solution, ProjectId projectId, AnalyzerReference analyzerReference, string language, CancellationToken cancellationToken);
8687

8788
/// <summary>
8889
/// Returns all the descriptors for all <see cref="DiagnosticAnalyzer"/>s defined within <paramref name="analyzerReference"/>.
8990
/// The results are returned in a dictionary where the key is an <see cref="ImmutableArray{T}"/> of languages that descriptor
9091
/// is defined for. This can be <c>[<see cref="LanguageNames.CSharp"/>]</c>, <c>[<see cref="LanguageNames.VisualBasic"/>]</c>,
9192
/// or an array containing both languages if the descriptor is defined for both languages.
9293
/// </summary>
93-
Task<ImmutableDictionary<ImmutableArray<string>, ImmutableArray<DiagnosticDescriptor>>> GetDiagnosticDescriptorsAsync(
94-
Solution solution, AnalyzerReference analyzerReference, CancellationToken cancellationToken);
94+
/// <param name="projectId">A project within <paramref name="solution"/> where <paramref name="analyzerReference"/> can be found</param>
95+
Task<ImmutableDictionary<ImmutableArray<string>, ImmutableArray<DiagnosticDescriptor>>> GetLanguageKeyedDiagnosticDescriptorsAsync(
96+
Solution solution, ProjectId projectId, AnalyzerReference analyzerReference, CancellationToken cancellationToken);
9597

9698
/// <summary>
9799
/// Given a list of errors ids (like CS1234), attempts to find an associated descriptor for each id.

src/Features/Core/Portable/Diagnostics/Service/DiagnosticAnalyzerService.cs

Lines changed: 97 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Microsoft.CodeAnalysis.Host;
1717
using Microsoft.CodeAnalysis.Host.Mef;
1818
using Microsoft.CodeAnalysis.Options;
19+
using Microsoft.CodeAnalysis.Remote;
1920
using Microsoft.CodeAnalysis.Shared.TestHooks;
2021
using Microsoft.CodeAnalysis.SolutionCrawler;
2122
using Microsoft.CodeAnalysis.Text;
@@ -138,20 +139,51 @@ public Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsForIdsAsync(
138139
return _incrementalAnalyzer.GetProjectDiagnosticsForIdsAsync(project, diagnosticIds, shouldIncludeAnalyzer, includeNonLocalDocumentDiagnostics, cancellationToken);
139140
}
140141

141-
public Task<ImmutableArray<DiagnosticDescriptor>> GetDiagnosticDescriptorsAsync(
142-
Solution solution, AnalyzerReference analyzerReference, string language, CancellationToken cancellationToken)
142+
public async Task<ImmutableArray<DiagnosticDescriptor>> GetDiagnosticDescriptorsAsync(
143+
Solution solution, ProjectId projectId, AnalyzerReference analyzerReference, string language, CancellationToken cancellationToken)
143144
{
144-
// TODO(cyrusn): Remote this to OOP.
145-
var descriptors = analyzerReference
145+
// Attempt to compute this OOP.
146+
var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false);
147+
if (client is not null &&
148+
analyzerReference is AnalyzerFileReference analyzerFileReference)
149+
{
150+
var descriptors = await client.TryInvokeAsync<IRemoteDiagnosticAnalyzerService, ImmutableArray<DiagnosticDescriptorData>>(
151+
solution,
152+
(service, solution, cancellationToken) => service.GetDiagnosticDescriptorsAsync(solution, projectId, analyzerFileReference.FullPath, language, cancellationToken),
153+
cancellationToken).ConfigureAwait(false);
154+
if (!descriptors.HasValue)
155+
return [];
156+
157+
return descriptors.Value.SelectAsArray(d => d.ToDiagnosticDescriptor());
158+
}
159+
160+
// Otherwise, fallback to computing in proc.
161+
return analyzerReference
146162
.GetAnalyzers(language)
147163
.SelectManyAsArray(this._analyzerInfoCache.GetDiagnosticDescriptors);
148-
149-
return Task.FromResult(descriptors);
150164
}
151165

152-
public Task<ImmutableDictionary<ImmutableArray<string>, ImmutableArray<DiagnosticDescriptor>>> GetDiagnosticDescriptorsAsync(
153-
Solution solution, AnalyzerReference analyzerReference, CancellationToken cancellationToken)
166+
public async Task<ImmutableDictionary<ImmutableArray<string>, ImmutableArray<DiagnosticDescriptor>>> GetLanguageKeyedDiagnosticDescriptorsAsync(
167+
Solution solution, ProjectId projectId, AnalyzerReference analyzerReference, CancellationToken cancellationToken)
154168
{
169+
var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false);
170+
if (client is not null &&
171+
analyzerReference is AnalyzerFileReference analyzerFileReference)
172+
{
173+
var map = await client.TryInvokeAsync<IRemoteDiagnosticAnalyzerService, ImmutableDictionary<ImmutableArray<string>, ImmutableArray<DiagnosticDescriptorData>>>(
174+
solution,
175+
(service, solution, cancellationToken) => service.GetLanguageKeyedDiagnosticDescriptorsAsync(solution, projectId, analyzerFileReference.FullPath, cancellationToken),
176+
cancellationToken).ConfigureAwait(false);
177+
178+
if (!map.HasValue)
179+
return ImmutableDictionary<ImmutableArray<string>, ImmutableArray<DiagnosticDescriptor>>.Empty;
180+
181+
return map.Value.ToImmutableDictionary(
182+
kvp => kvp.Key,
183+
kvp => kvp.Value.SelectAsArray(d => d.ToDiagnosticDescriptor()));
184+
}
185+
186+
// Otherwise, fallback to computing in proc.
155187
var mapBuilder = ImmutableDictionary.CreateBuilder<ImmutableArray<string>, ImmutableArray<DiagnosticDescriptor>>();
156188

157189
var csharpAnalyzers = analyzerReference.GetAnalyzers(LanguageNames.CSharp);
@@ -165,30 +197,80 @@ public Task<ImmutableDictionary<ImmutableArray<string>, ImmutableArray<Diagnosti
165197
mapBuilder.Add(s_visualBasicLanguageArray, GetDiagnosticDescriptors(visualBasicAnalyzers));
166198
mapBuilder.Add(s_csharpAndVisualBasicLanguageArray, GetDiagnosticDescriptors(dotnetAnalyzers));
167199

168-
return Task.FromResult(mapBuilder.ToImmutable());
200+
return mapBuilder.ToImmutable();
169201

170202
ImmutableArray<DiagnosticDescriptor> GetDiagnosticDescriptors(ImmutableArray<DiagnosticAnalyzer> analyzers)
171203
=> analyzers.SelectManyAsArray(this._analyzerInfoCache.GetDiagnosticDescriptors);
172204
}
173205

174-
public Task<ImmutableDictionary<string, DiagnosticDescriptor>> TryGetDiagnosticDescriptorsAsync(
206+
public async Task<ImmutableDictionary<string, DiagnosticDescriptor>> TryGetDiagnosticDescriptorsAsync(
175207
Solution solution, ImmutableArray<string> diagnosticIds, CancellationToken cancellationToken)
176208
{
209+
var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false);
210+
if (client is not null)
211+
{
212+
var map = await client.TryInvokeAsync<IRemoteDiagnosticAnalyzerService, ImmutableDictionary<string, DiagnosticDescriptorData>>(
213+
solution,
214+
(service, solution, cancellationToken) => service.TryGetDiagnosticDescriptorsAsync(solution, diagnosticIds, cancellationToken),
215+
cancellationToken).ConfigureAwait(false);
216+
217+
if (!map.HasValue)
218+
return ImmutableDictionary<string, DiagnosticDescriptor>.Empty;
219+
220+
return map.Value.ToImmutableDictionary(
221+
kvp => kvp.Key,
222+
kvp => kvp.Value.ToDiagnosticDescriptor());
223+
}
224+
177225
var builder = ImmutableDictionary.CreateBuilder<string, DiagnosticDescriptor>();
178226
foreach (var diagnosticId in diagnosticIds)
179227
{
180228
if (this._analyzerInfoCache.TryGetDescriptorForDiagnosticId(diagnosticId, out var descriptor))
181229
builder[diagnosticId] = descriptor;
182230
}
183231

184-
return Task.FromResult(builder.ToImmutable());
232+
return builder.ToImmutable();
233+
}
234+
235+
public async Task<ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>>> GetDiagnosticDescriptorsPerReferenceAsync(Solution solution, CancellationToken cancellationToken)
236+
{
237+
var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false);
238+
if (client is not null)
239+
{
240+
var map = await client.TryInvokeAsync<IRemoteDiagnosticAnalyzerService, ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptorData>>>(
241+
solution,
242+
(service, solution, cancellationToken) => service.GetDiagnosticDescriptorsPerReferenceAsync(solution, cancellationToken),
243+
cancellationToken).ConfigureAwait(false);
244+
if (!map.HasValue)
245+
return ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>>.Empty;
246+
247+
return map.Value.ToImmutableDictionary(
248+
kvp => kvp.Key,
249+
kvp => kvp.Value.SelectAsArray(d => d.ToDiagnosticDescriptor()));
250+
}
251+
252+
return solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(this._analyzerInfoCache);
185253
}
186254

187-
public Task<ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>>> GetDiagnosticDescriptorsPerReferenceAsync(Solution solution, CancellationToken cancellationToken)
188-
=> Task.FromResult(solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(this._analyzerInfoCache));
255+
public async Task<ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>>> GetDiagnosticDescriptorsPerReferenceAsync(Project project, CancellationToken cancellationToken)
256+
{
257+
var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false);
258+
if (client is not null)
259+
{
260+
var map = await client.TryInvokeAsync<IRemoteDiagnosticAnalyzerService, ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptorData>>>(
261+
project,
262+
(service, solution, cancellationToken) => service.GetDiagnosticDescriptorsPerReferenceAsync(solution, project.Id, cancellationToken),
263+
cancellationToken).ConfigureAwait(false);
264+
if (!map.HasValue)
265+
return ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>>.Empty;
266+
267+
return map.Value.ToImmutableDictionary(
268+
kvp => kvp.Key,
269+
kvp => kvp.Value.SelectAsArray(d => d.ToDiagnosticDescriptor()));
270+
}
189271

190-
public Task<ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>>> GetDiagnosticDescriptorsPerReferenceAsync(Project project, CancellationToken cancellationToken)
191-
=> Task.FromResult(project.Solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(this._analyzerInfoCache, project));
272+
return project.Solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(this._analyzerInfoCache, project);
273+
}
192274

193275
private sealed class DiagnosticAnalyzerComparer : IEqualityComparer<DiagnosticAnalyzer>
194276
{

src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/CodeStyle/CSharpCodeStyleSettingsProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public CSharpCodeStyleSettingsProvider(
3434
}
3535

3636
protected override Task UpdateOptionsAsync(
37-
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
37+
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
3838
{
3939
var varSettings = GetVarCodeStyleOptions(options, SettingsUpdater);
4040
AddRange(varSettings);

src/VisualStudio/CSharp/Impl/EditorConfigSettings/DataProvider/Whitespace/CSharpWhitespaceSettingsProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public CSharpWhitespaceSettingsProvider(IThreadingContext threadingContext, stri
2929
}
3030

3131
protected override Task UpdateOptionsAsync(
32-
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
32+
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
3333
{
3434
var spacingOptions = GetSpacingOptions(options, SettingsUpdater);
3535
AddRange(spacingOptions.ToImmutableArray());

src/VisualStudio/Core/Def/EditorConfigSettings/DataProvider/CodeStyle/CommonCodeStyleSettingsProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public CommonCodeStyleSettingsProvider(
3030
}
3131

3232
protected override Task UpdateOptionsAsync(
33-
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
33+
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
3434
{
3535
var qualifySettings = GetQualifyCodeStyleOptions(options, SettingsUpdater);
3636
AddRange(qualifySettings);

src/VisualStudio/Core/Def/EditorConfigSettings/DataProvider/Whitespace/CommonWhitespaceSettingsProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public CommonWhitespaceSettingsProvider(
3030
}
3131

3232
protected override Task UpdateOptionsAsync(
33-
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
33+
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
3434
{
3535
var defaultOptions = GetDefaultOptions(options, SettingsUpdater);
3636
AddRange(defaultOptions);

0 commit comments

Comments
 (0)