|
// 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.Composition;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics.EngineV2;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Threading;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
{
[Export(typeof(IDiagnosticAnalyzerService))]
[Shared]
internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService
{
private static readonly Option2<bool> s_crashOnAnalyzerException = new("dotnet_crash_on_analyzer_exception", defaultValue: false);
public DiagnosticAnalyzerInfoCache AnalyzerInfoCache { get; private set; }
public IAsynchronousOperationListener Listener { get; }
public IGlobalOptionService GlobalOptions { get; }
private readonly ConditionalWeakTable<Workspace, DiagnosticIncrementalAnalyzer> _map = new();
private readonly ConditionalWeakTable<Workspace, DiagnosticIncrementalAnalyzer>.CreateValueCallback _createIncrementalAnalyzer;
private readonly IDiagnosticsRefresher _diagnosticsRefresher;
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public DiagnosticAnalyzerService(
IAsynchronousOperationListenerProvider listenerProvider,
DiagnosticAnalyzerInfoCache.SharedGlobalCache globalCache,
IGlobalOptionService globalOptions,
IDiagnosticsRefresher diagnosticsRefresher)
{
AnalyzerInfoCache = globalCache.AnalyzerInfoCache;
Listener = listenerProvider.GetListener(FeatureAttribute.DiagnosticService);
GlobalOptions = globalOptions;
_diagnosticsRefresher = diagnosticsRefresher;
_createIncrementalAnalyzer = CreateIncrementalAnalyzerCallback;
globalOptions.AddOptionChangedHandler(this, (_, _, e) =>
{
if (e.HasOption(IsGlobalOptionAffectingDiagnostics))
{
RequestDiagnosticRefresh();
}
});
}
public bool CrashOnAnalyzerException
=> GlobalOptions.GetOption(s_crashOnAnalyzerException);
public static bool IsGlobalOptionAffectingDiagnostics(IOption2 option)
=> option == NamingStyleOptions.NamingPreferences ||
option.Definition.Group.Parent == CodeStyleOptionGroups.CodeStyle ||
option == SolutionCrawlerOptionsStorage.BackgroundAnalysisScopeOption ||
option == SolutionCrawlerOptionsStorage.SolutionBackgroundAnalysisScopeOption ||
option == SolutionCrawlerOptionsStorage.CompilerDiagnosticsScopeOption ||
option == s_crashOnAnalyzerException;
public void RequestDiagnosticRefresh()
=> _diagnosticsRefresher?.RequestWorkspaceRefresh();
public async Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForSpanAsync(
TextDocument document,
TextSpan? range,
Func<string, bool>? shouldIncludeDiagnostic,
bool includeCompilerDiagnostics,
bool includeSuppressedDiagnostics,
ICodeActionRequestPriorityProvider priorityProvider,
DiagnosticKind diagnosticKinds,
bool isExplicit,
CancellationToken cancellationToken)
{
var analyzer = CreateIncrementalAnalyzer(document.Project.Solution.Workspace);
// always make sure that analyzer is called on background thread.
await TaskScheduler.Default;
priorityProvider ??= new DefaultCodeActionRequestPriorityProvider();
return await analyzer.GetDiagnosticsForSpanAsync(
document, range, shouldIncludeDiagnostic, includeSuppressedDiagnostics, includeCompilerDiagnostics,
priorityProvider, diagnosticKinds, isExplicit, cancellationToken).ConfigureAwait(false);
}
public Task<ImmutableArray<DiagnosticData>> GetCachedDiagnosticsAsync(Workspace workspace, ProjectId? projectId, DocumentId? documentId, bool includeSuppressedDiagnostics, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken)
{
var analyzer = CreateIncrementalAnalyzer(workspace);
return analyzer.GetCachedDiagnosticsAsync(workspace.CurrentSolution, projectId, documentId, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken);
}
public async Task ForceAnalyzeProjectAsync(Project project, CancellationToken cancellationToken)
{
var analyzer = CreateIncrementalAnalyzer(project.Solution.Workspace);
await analyzer.ForceAnalyzeProjectAsync(project, cancellationToken).ConfigureAwait(false);
}
public Task<ImmutableArray<DiagnosticData>> GetDiagnosticsForIdsAsync(
Solution solution, ProjectId? projectId, DocumentId? documentId, ImmutableHashSet<string>? diagnosticIds, Func<DiagnosticAnalyzer, bool>? shouldIncludeAnalyzer, Func<Project, DocumentId?, IReadOnlyList<DocumentId>>? getDocuments, bool includeSuppressedDiagnostics, bool includeLocalDocumentDiagnostics, bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken)
{
var analyzer = CreateIncrementalAnalyzer(solution.Workspace);
return analyzer.GetDiagnosticsForIdsAsync(solution, projectId, documentId, diagnosticIds, shouldIncludeAnalyzer, getDocuments, includeSuppressedDiagnostics, includeLocalDocumentDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken);
}
public Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsForIdsAsync(
Solution solution, ProjectId? projectId, ImmutableHashSet<string>? diagnosticIds,
Func<DiagnosticAnalyzer, bool>? shouldIncludeAnalyzer, bool includeSuppressedDiagnostics,
bool includeNonLocalDocumentDiagnostics, CancellationToken cancellationToken)
{
var analyzer = CreateIncrementalAnalyzer(solution.Workspace);
return analyzer.GetProjectDiagnosticsForIdsAsync(solution, projectId, diagnosticIds, shouldIncludeAnalyzer, includeSuppressedDiagnostics, includeNonLocalDocumentDiagnostics, cancellationToken);
}
}
}
|