File: Diagnostics\Service\DiagnosticAnalyzerService_ProduceProjectDiagnostics.cs
Web Access
Project: src\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// 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.PooledObjects;
using Microsoft.CodeAnalysis.Workspaces.Diagnostics;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Diagnostics;
 
internal sealed partial class DiagnosticAnalyzerService
{
    /// <summary>
    /// Should only be called from other "InProcess" methods as this loads and realizes the DiagnosticAnalyzers.
    /// </summary>
    private ImmutableArray<DiagnosticAnalyzer> GetDiagnosticAnalyzersInProcess(
       Project project,
       ImmutableHashSet<string>? diagnosticIds,
       AnalyzerFilter analyzerFilter)
    {
        var analyzersForProject = GetProjectAnalyzers_OnlyCallInProcess(project);
        return analyzersForProject.WhereAsArray(ShouldIncludeAnalyzer);
 
        bool ShouldIncludeAnalyzer(DiagnosticAnalyzer analyzer)
        {
            if (analyzer.IsCompilerAnalyzer())
            {
                if ((analyzerFilter & AnalyzerFilter.CompilerAnalyzer) == 0)
                    return false;
            }
            else
            {
                if ((analyzerFilter & AnalyzerFilter.NonCompilerAnalyzer) == 0)
                    return false;
            }
 
            if (!DocumentAnalysisExecutor.IsAnalyzerEnabledForProject(analyzer, project, this._globalOptions))
                return false;
 
            if (diagnosticIds != null && _analyzerInfoCache.GetDiagnosticDescriptors(analyzer).All(d => !diagnosticIds.Contains(d.Id)))
                return false;
 
            return true;
        }
    }
 
    private Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForIdsInProcessAsync(
        Project project,
        ImmutableArray<DocumentId> documentIds,
        ImmutableHashSet<string>? diagnosticIds,
        AnalyzerFilter analyzerFilter,
        bool includeLocalDocumentDiagnostics,
        CancellationToken cancellationToken)
    {
        return GetDiagnosticsForIdsInProcessAsync(
            project, documentIds, diagnosticIds,
            GetDiagnosticAnalyzersInProcess(project, diagnosticIds, analyzerFilter),
            includeLocalDocumentDiagnostics, cancellationToken);
    }
 
    private Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForIdsInProcessAsync(
        Project project,
        ImmutableArray<DocumentId> documentIds,
        ImmutableHashSet<string>? diagnosticIds,
        ImmutableArray<DiagnosticAnalyzer> analyzers,
        bool includeLocalDocumentDiagnostics,
        CancellationToken cancellationToken)
    {
        return ProduceProjectDiagnosticsInProcessAsync(
            project, diagnosticIds,
            // Ensure we compute and return diagnostics for both the normal docs and the additional docs in this
            // project if no specific document id was requested.
            documentIds.IsDefault ? [.. project.DocumentIds, .. project.AdditionalDocumentIds] : documentIds,
            analyzers,
            includeLocalDocumentDiagnostics,
            includeNonLocalDocumentDiagnostics: true,
            // return diagnostics specific to one project or document
            includeProjectNonLocalResult: documentIds.IsDefault,
            cancellationToken);
    }
 
    private Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsForIdsInProcessAsync(
        Project project,
        ImmutableHashSet<string>? diagnosticIds,
        AnalyzerFilter analyzerFilter,
        CancellationToken cancellationToken)
    {
        return GetProjectDiagnosticsForIdsInProcessAsync(
            project, diagnosticIds,
            GetDiagnosticAnalyzersInProcess(project, diagnosticIds, analyzerFilter),
            cancellationToken);
    }
 
    private Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsForIdsInProcessAsync(
        Project project,
        ImmutableHashSet<string>? diagnosticIds,
        ImmutableArray<DiagnosticAnalyzer> analyzers,
        CancellationToken cancellationToken)
    {
        return ProduceProjectDiagnosticsInProcessAsync(
            project, diagnosticIds, documentIds: [],
            analyzers,
            includeLocalDocumentDiagnostics: false,
            includeNonLocalDocumentDiagnostics: false,
            includeProjectNonLocalResult: true,
            cancellationToken);
    }
 
    private async Task<ImmutableArray<DiagnosticData>> ProduceProjectDiagnosticsInProcessAsync(
        Project project,
        ImmutableHashSet<string>? diagnosticIds,
        ImmutableArray<DocumentId> documentIds,
        ImmutableArray<DiagnosticAnalyzer> analyzers,
        bool includeLocalDocumentDiagnostics,
        bool includeNonLocalDocumentDiagnostics,
        bool includeProjectNonLocalResult,
        CancellationToken cancellationToken)
    {
        using var _ = ArrayBuilder<DiagnosticData>.GetInstance(out var builder);
 
        var hostAnalyzerInfo = GetOrCreateHostAnalyzerInfo_OnlyCallInProcess(project);
        var result = await GetOrComputeDiagnosticAnalysisResultsAsync(analyzers).ConfigureAwait(false);
 
        foreach (var analyzer in analyzers)
        {
            if (!result.TryGetValue(analyzer, out var analysisResult))
                continue;
 
            foreach (var documentId in documentIds)
            {
                if (includeLocalDocumentDiagnostics)
                {
                    AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Syntax));
                    AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.Semantic));
                }
 
                if (includeNonLocalDocumentDiagnostics)
                    AddIncludedDiagnostics(builder, analysisResult.GetDocumentDiagnostics(documentId, AnalysisKind.NonLocal));
            }
 
            // include project diagnostics if there is no target document
            if (includeProjectNonLocalResult)
                AddIncludedDiagnostics(builder, analysisResult.GetOtherDiagnostics());
        }
 
        return builder.ToImmutableAndClear();
 
        bool ShouldIncludeDiagnostic(DiagnosticData diagnostic)
            => diagnosticIds == null || diagnosticIds.Contains(diagnostic.Id);
 
        void AddIncludedDiagnostics(ArrayBuilder<DiagnosticData> builder, ImmutableArray<DiagnosticData> diagnostics)
        {
            foreach (var diagnostic in diagnostics)
            {
                if (ShouldIncludeDiagnostic(diagnostic))
                    builder.Add(diagnostic);
            }
        }
 
        async Task<ImmutableDictionary<DiagnosticAnalyzer, DiagnosticAnalysisResult>> GetOrComputeDiagnosticAnalysisResultsAsync(
            ImmutableArray<DiagnosticAnalyzer> analyzers)
        {
            // Otherwise, just compute for the analyzers we care about.
            var compilation = await GetOrCreateCompilationWithAnalyzers_OnlyCallInProcessAsync(
                project, analyzers, hostAnalyzerInfo, this.CrashOnAnalyzerException, cancellationToken).ConfigureAwait(false);
 
            var result = await ComputeDiagnosticAnalysisResultsInProcessAsync(
                compilation, project, [.. analyzers.OfType<DocumentDiagnosticAnalyzer>()], cancellationToken).ConfigureAwait(false);
            return result;
        }
    }
}