File: EditorConfigSettings\DataProvider\Analyzer\AnalyzerSettingsProvider.cs
Web Access
Project: src\src\EditorFeatures\Core\Microsoft.CodeAnalysis.EditorFeatures_vqux3thj_wpftmp.csproj (Microsoft.CodeAnalysis.EditorFeatures)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Data;
using Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Updater;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.EditorConfig;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using RoslynEnumerableExtensions = Microsoft.CodeAnalysis.Editor.EditorConfigSettings.Extensions.EnumerableExtensions;
 
namespace Microsoft.CodeAnalysis.Editor.EditorConfigSettings.DataProvider.Analyzer;
 
internal sealed class AnalyzerSettingsProvider
    : SettingsProviderBase<AnalyzerSetting, AnalyzerSettingsUpdater, AnalyzerSetting, ReportDiagnostic>,
    // So we can unify descriptors across VB/C# to create single settings that apply to both languages.
    IEqualityComparer<DiagnosticDescriptor>
{
    public AnalyzerSettingsProvider(
        IThreadingContext threadingContext,
        string fileName,
        AnalyzerSettingsUpdater settingsUpdater,
        Workspace workspace,
        IGlobalOptionService optionService)
        : base(threadingContext, fileName, settingsUpdater, workspace, optionService)
    {
        Update();
    }
 
    protected override async Task UpdateOptionsAsync(
        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(
                someReferencingProject, analyzerReference, options.EditorConfigOptions, cancellationToken).ConfigureAwait(false);
            AddRange(configSettings);
        }
    }
 
    private async Task<ImmutableArray<AnalyzerSetting>> GetSettingsAsync(
        Project someReferencingProject, AnalyzerReference analyzerReference, AnalyzerConfigOptions editorConfigOptions, CancellationToken cancellationToken)
    {
        var solution = someReferencingProject.Solution;
        var service = solution.Services.GetRequiredService<IDiagnosticAnalyzerService>();
 
        var csharpDescriptors = await service.GetDiagnosticDescriptorsAsync(solution, someReferencingProject.Id, analyzerReference, LanguageNames.CSharp, cancellationToken).ConfigureAwait(false);
        var vbDescriptors = await service.GetDiagnosticDescriptorsAsync(solution, someReferencingProject.Id, analyzerReference, LanguageNames.VisualBasic, cancellationToken).ConfigureAwait(false);
 
        var dotnetDescriptors = csharpDescriptors.Intersect(vbDescriptors, this).ToImmutableArray();
 
        return [
            .. ToAnalyzerSettings(csharpDescriptors.Except(dotnetDescriptors), Language.CSharp),
            .. ToAnalyzerSettings(vbDescriptors.Except(dotnetDescriptors), Language.VisualBasic),
            .. ToAnalyzerSettings(dotnetDescriptors, Language.CSharp | Language.VisualBasic)];
 
        IEnumerable<AnalyzerSetting> ToAnalyzerSettings(
            IEnumerable<DiagnosticDescriptor> descriptors, Language language)
        {
            return descriptors
                .GroupBy(d => d.Id)
                .OrderBy(g => g.Key, StringComparer.CurrentCulture)
                .Select(g =>
                {
                    var selectedDiagnostic = g.First();
                    var isEditorconfig = selectedDiagnostic.IsDefinedInEditorConfig(editorConfigOptions);
                    var settingLocation = new SettingLocation(isEditorconfig ? LocationKind.EditorConfig : LocationKind.VisualStudio, FileName);
                    var severity = selectedDiagnostic.GetEffectiveSeverity(editorConfigOptions);
                    return new AnalyzerSetting(selectedDiagnostic, severity, SettingsUpdater, language, settingLocation);
                });
        }
    }
 
    bool IEqualityComparer<DiagnosticDescriptor>.Equals(DiagnosticDescriptor x, DiagnosticDescriptor y)
        => x.Id == y.Id;
 
    int IEqualityComparer<DiagnosticDescriptor>.GetHashCode(DiagnosticDescriptor obj)
        => obj.Id.GetHashCode();
}