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
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,34 @@ public AnalyzerSettingsProvider(
}

protected override async Task UpdateOptionsAsync(
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
{
var analyzerReferences = RoslynEnumerableExtensions.DistinctBy(projectsInScope.SelectMany(p => p.AnalyzerReferences), a => a.Id).ToImmutableArray();
using var _ = PooledDictionary<AnalyzerReference, Project>.GetInstance(out var analyzerReferenceToSomeReferencingProject);

foreach (var project in projectsInScope)
{
foreach (var analyzerReference in project.AnalyzerReferences)
analyzerReferenceToSomeReferencingProject[analyzerReference] = project;

}

foreach (var analyzerReference in analyzerReferences)
{
var someReferencingProject = analyzerReferenceToSomeReferencingProject[analyzerReference];
var configSettings = await GetSettingsAsync(
solution, analyzerReference, options.EditorConfigOptions, cancellationToken).ConfigureAwait(false);
someReferencingProject, analyzerReference, options.EditorConfigOptions, cancellationToken).ConfigureAwait(false);
AddRange(configSettings);
}
}

private async Task<ImmutableArray<AnalyzerSetting>> GetSettingsAsync(
Solution solution, AnalyzerReference analyzerReference, AnalyzerConfigOptions editorConfigOptions, CancellationToken cancellationToken)
Project someReferencingProject, AnalyzerReference analyzerReference, AnalyzerConfigOptions editorConfigOptions, CancellationToken cancellationToken)
{
var solution = someReferencingProject.Solution;
var service = solution.Services.GetRequiredService<IDiagnosticAnalyzerService>();
var map = await service.GetDiagnosticDescriptorsAsync(
solution, analyzerReference, cancellationToken).ConfigureAwait(false);
var map = await service.GetLanguageKeyedDiagnosticDescriptorsAsync(
solution, someReferencingProject.Id, analyzerReference, cancellationToken).ConfigureAwait(false);

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public NamingStyleSettingsProvider(
}

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Shell;
using Roslyn.Utilities;

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

protected abstract Task UpdateOptionsAsync(
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken);
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken);

protected void Update()
{
Expand Down Expand Up @@ -74,7 +73,7 @@ protected void Update()
language: LanguageNames.CSharp,
editorConfigFileName: FileName);

_ = UpdateOptionsAsync(options, solution, projects, cancellationToken)
_ = UpdateOptionsAsync(options, projects, cancellationToken)
.ReportNonFatalErrorUnlessCancelledAsync(cancellationToken);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ public static Task<ImmutableArray<DiagnosticDescriptor>> GetDiagnosticDescriptor
{
var diagnosticAnalyzerService = project.Solution.Services.GetRequiredService<IDiagnosticAnalyzerService>();
return diagnosticAnalyzerService.GetDiagnosticDescriptorsAsync(
project.Solution, analyzerReference, project.Language, cancellationToken);
project.Solution, project.Id, analyzerReference, project.Language, cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,19 @@ Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForSpanAsync(
DiagnosticKind diagnosticKind,
CancellationToken cancellationToken);

/// <param name="projectId">A project within <paramref name="solution"/> where <paramref name="analyzerReference"/> can be found</param>
Task<ImmutableArray<DiagnosticDescriptor>> GetDiagnosticDescriptorsAsync(
Solution solution, AnalyzerReference analyzerReference, string language, CancellationToken cancellationToken);
Solution solution, ProjectId projectId, AnalyzerReference analyzerReference, string language, CancellationToken cancellationToken);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just pass it the project then? That also covers the language parameter too.


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

/// <summary>
/// Given a list of errors ids (like CS1234), attempts to find an associated descriptor for each id.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.Text;
Expand Down Expand Up @@ -138,20 +139,51 @@ public Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsForIdsAsync(
return _incrementalAnalyzer.GetProjectDiagnosticsForIdsAsync(project, diagnosticIds, shouldIncludeAnalyzer, includeNonLocalDocumentDiagnostics, cancellationToken);
}

public Task<ImmutableArray<DiagnosticDescriptor>> GetDiagnosticDescriptorsAsync(
Solution solution, AnalyzerReference analyzerReference, string language, CancellationToken cancellationToken)
public async Task<ImmutableArray<DiagnosticDescriptor>> GetDiagnosticDescriptorsAsync(
Solution solution, ProjectId projectId, AnalyzerReference analyzerReference, string language, CancellationToken cancellationToken)
{
// TODO(cyrusn): Remote this to OOP.
var descriptors = analyzerReference
// Attempt to compute this OOP.
var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false);
if (client is not null &&
analyzerReference is AnalyzerFileReference analyzerFileReference)
{
var descriptors = await client.TryInvokeAsync<IRemoteDiagnosticAnalyzerService, ImmutableArray<DiagnosticDescriptorData>>(
solution,
(service, solution, cancellationToken) => service.GetDiagnosticDescriptorsAsync(solution, projectId, analyzerFileReference.FullPath, language, cancellationToken),
cancellationToken).ConfigureAwait(false);
if (!descriptors.HasValue)
return [];

return descriptors.Value.SelectAsArray(d => d.ToDiagnosticDescriptor());
}

// Otherwise, fallback to computing in proc.
return analyzerReference
.GetAnalyzers(language)
.SelectManyAsArray(this._analyzerInfoCache.GetDiagnosticDescriptors);

return Task.FromResult(descriptors);
}

public Task<ImmutableDictionary<ImmutableArray<string>, ImmutableArray<DiagnosticDescriptor>>> GetDiagnosticDescriptorsAsync(
Solution solution, AnalyzerReference analyzerReference, CancellationToken cancellationToken)
public async Task<ImmutableDictionary<ImmutableArray<string>, ImmutableArray<DiagnosticDescriptor>>> GetLanguageKeyedDiagnosticDescriptorsAsync(
Solution solution, ProjectId projectId, AnalyzerReference analyzerReference, CancellationToken cancellationToken)
{
var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false);
if (client is not null &&
analyzerReference is AnalyzerFileReference analyzerFileReference)
{
var map = await client.TryInvokeAsync<IRemoteDiagnosticAnalyzerService, ImmutableDictionary<ImmutableArray<string>, ImmutableArray<DiagnosticDescriptorData>>>(
solution,
(service, solution, cancellationToken) => service.GetLanguageKeyedDiagnosticDescriptorsAsync(solution, projectId, analyzerFileReference.FullPath, cancellationToken),
cancellationToken).ConfigureAwait(false);

if (!map.HasValue)
return ImmutableDictionary<ImmutableArray<string>, ImmutableArray<DiagnosticDescriptor>>.Empty;

return map.Value.ToImmutableDictionary(
kvp => kvp.Key,
kvp => kvp.Value.SelectAsArray(d => d.ToDiagnosticDescriptor()));
}

// Otherwise, fallback to computing in proc.
var mapBuilder = ImmutableDictionary.CreateBuilder<ImmutableArray<string>, ImmutableArray<DiagnosticDescriptor>>();

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

return Task.FromResult(mapBuilder.ToImmutable());
return mapBuilder.ToImmutable();

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

public Task<ImmutableDictionary<string, DiagnosticDescriptor>> TryGetDiagnosticDescriptorsAsync(
public async Task<ImmutableDictionary<string, DiagnosticDescriptor>> TryGetDiagnosticDescriptorsAsync(
Solution solution, ImmutableArray<string> diagnosticIds, CancellationToken cancellationToken)
{
var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false);
if (client is not null)
{
var map = await client.TryInvokeAsync<IRemoteDiagnosticAnalyzerService, ImmutableDictionary<string, DiagnosticDescriptorData>>(
solution,
(service, solution, cancellationToken) => service.TryGetDiagnosticDescriptorsAsync(solution, diagnosticIds, cancellationToken),
cancellationToken).ConfigureAwait(false);

if (!map.HasValue)
return ImmutableDictionary<string, DiagnosticDescriptor>.Empty;

return map.Value.ToImmutableDictionary(
kvp => kvp.Key,
kvp => kvp.Value.ToDiagnosticDescriptor());
}

var builder = ImmutableDictionary.CreateBuilder<string, DiagnosticDescriptor>();
foreach (var diagnosticId in diagnosticIds)
{
if (this._analyzerInfoCache.TryGetDescriptorForDiagnosticId(diagnosticId, out var descriptor))
builder[diagnosticId] = descriptor;
}

return Task.FromResult(builder.ToImmutable());
return builder.ToImmutable();
}

public async Task<ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>>> GetDiagnosticDescriptorsPerReferenceAsync(Solution solution, CancellationToken cancellationToken)
{
var client = await RemoteHostClient.TryGetClientAsync(solution.Services, cancellationToken).ConfigureAwait(false);
if (client is not null)
{
var map = await client.TryInvokeAsync<IRemoteDiagnosticAnalyzerService, ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptorData>>>(
solution,
(service, solution, cancellationToken) => service.GetDiagnosticDescriptorsPerReferenceAsync(solution, cancellationToken),
cancellationToken).ConfigureAwait(false);
if (!map.HasValue)
return ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>>.Empty;

return map.Value.ToImmutableDictionary(
kvp => kvp.Key,
kvp => kvp.Value.SelectAsArray(d => d.ToDiagnosticDescriptor()));
}

return solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(this._analyzerInfoCache);
}

public Task<ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>>> GetDiagnosticDescriptorsPerReferenceAsync(Solution solution, CancellationToken cancellationToken)
=> Task.FromResult(solution.SolutionState.Analyzers.GetDiagnosticDescriptorsPerReference(this._analyzerInfoCache));
public async Task<ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>>> GetDiagnosticDescriptorsPerReferenceAsync(Project project, CancellationToken cancellationToken)
{
var client = await RemoteHostClient.TryGetClientAsync(project, cancellationToken).ConfigureAwait(false);
if (client is not null)
{
var map = await client.TryInvokeAsync<IRemoteDiagnosticAnalyzerService, ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptorData>>>(
project,
(service, solution, cancellationToken) => service.GetDiagnosticDescriptorsPerReferenceAsync(solution, project.Id, cancellationToken),
cancellationToken).ConfigureAwait(false);
if (!map.HasValue)
return ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>>.Empty;

return map.Value.ToImmutableDictionary(
kvp => kvp.Key,
kvp => kvp.Value.SelectAsArray(d => d.ToDiagnosticDescriptor()));
}

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

private sealed class DiagnosticAnalyzerComparer : IEqualityComparer<DiagnosticAnalyzer>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public CSharpCodeStyleSettingsProvider(
}

protected override Task UpdateOptionsAsync(
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
{
var varSettings = GetVarCodeStyleOptions(options, SettingsUpdater);
AddRange(varSettings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public CSharpWhitespaceSettingsProvider(IThreadingContext threadingContext, stri
}

protected override Task UpdateOptionsAsync(
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
{
var spacingOptions = GetSpacingOptions(options, SettingsUpdater);
AddRange(spacingOptions.ToImmutableArray());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public CommonCodeStyleSettingsProvider(
}

protected override Task UpdateOptionsAsync(
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
{
var qualifySettings = GetQualifyCodeStyleOptions(options, SettingsUpdater);
AddRange(qualifySettings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public CommonWhitespaceSettingsProvider(
}

protected override Task UpdateOptionsAsync(
TieredAnalyzerConfigOptions options, Solution solution, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
TieredAnalyzerConfigOptions options, ImmutableArray<Project> projectsInScope, CancellationToken cancellationToken)
{
var defaultOptions = GetDefaultOptions(options, SettingsUpdater);
AddRange(defaultOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ async ValueTask<ImmutableArray<DiagnosticDescriptor>> GetDiagnosticDescriptorsAs
var result = await client.TryInvokeAsync<IRemoteDiagnosticAnalyzerService, ImmutableArray<DiagnosticDescriptorData>>(
project,
(service, solutionChecksum, cancellationToken) => service.GetDiagnosticDescriptorsAsync(
solutionChecksum, project.Id, analyzerFileReference.FullPath, cancellationToken),
solutionChecksum, project.Id, analyzerFileReference.FullPath, project.Language, cancellationToken),
cancellationToken).ConfigureAwait(false);

// If the call fails, the OOP substrate will have already reported an error
Expand Down
Loading
Loading