提交 1acb322b 编写于 作者: M Manish Vasani

Restructure code based on recent code refactoring for HostAnalyzers and DiagnosticAnalyzerInfoCache

上级 5bd87bff
......@@ -310,6 +310,7 @@ private static void AssertCompilation(Project project, Compilation compilation1)
Document document,
AnalysisKind kind,
CompilationWithAnalyzers? compilationWithAnalyzers,
Func<Project, ISkippedAnalyzersInfo>? getSkippedAnalyzersInfo,
TextSpan? span,
CancellationToken cancellationToken)
{
......@@ -362,7 +363,7 @@ private static void AssertCompilation(Project project, Compilation compilation1)
// REVIEW: more unnecessary allocations just to get diagnostics per analyzer
var singleAnalyzer = ImmutableArray.Create(analyzer);
var skippedAnalyzerInfo = analyzerInfoCache.GetOrCreateSkippedAnalyzersInfo(document.Project);
var skippedAnalyzerInfo = getSkippedAnalyzersInfo?.Invoke(document.Project) ?? SkippedHostAnalyzersInfo.Default;
ImmutableArray<string> filteredIds;
switch (kind)
......
......@@ -164,7 +164,7 @@ private async Task AnalyzeForKindAsync(Document document, AnalysisKind kind, Can
foreach (var analyzer in analyzers)
{
builder.AddRange(await AnalyzerHelper.ComputeDiagnosticsAsync(analyzer,
document, kind, compilationWithAnalyzers, span: null, cancellationToken).ConfigureAwait(false));
document, kind, compilationWithAnalyzers, getSkippedAnalyzersInfo: null, span: null, cancellationToken).ConfigureAwait(false));
}
return builder.ToImmutableAndFree();
......
......@@ -4,6 +4,7 @@
#nullable enable
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
......@@ -44,14 +45,14 @@ public DiagnosticDescriptorsInfo(ImmutableArray<DiagnosticDescriptor> supportedD
}
/// <summary>
/// Information about host analyzers that can be skipped for the given project ID.
/// Information about host analyzers that can be skipped for the given project analyzers.
/// </summary>
private readonly ConditionalWeakTable<ProjectId, ISkippedAnalyzersInfo> _skippedAnalyzersInfo;
private readonly ConditionalWeakTable<IReadOnlyList<AnalyzerReference>, ISkippedAnalyzersInfo> _skippedAnalyzersInfo;
internal DiagnosticAnalyzerInfoCache()
{
_descriptorsInfo = new ConditionalWeakTable<DiagnosticAnalyzer, DiagnosticDescriptorsInfo>();
_skippedAnalyzersInfo = new ConditionalWeakTable<ProjectId, ISkippedAnalyzersInfo>();
_skippedAnalyzersInfo = new ConditionalWeakTable<IReadOnlyList<AnalyzerReference>, ISkippedAnalyzersInfo>();
}
/// <summary>
......@@ -121,19 +122,28 @@ public bool IsAnalyzerSuppressed(DiagnosticAnalyzer analyzer, Project project)
return false;
}
public ISkippedAnalyzersInfo GetOrCreateSkippedAnalyzersInfo(Project project)
public ISkippedAnalyzersInfo GetOrCreateSkippedAnalyzersInfo(Project project, HostDiagnosticAnalyzers hostAnalyzers)
{
if (_skippedAnalyzersInfo.TryGetValue(project.Id, out var skippedAnalyzersInfo))
if (_skippedAnalyzersInfo.TryGetValue(project.AnalyzerReferences, out var skippedAnalyzersInfo))
{
return skippedAnalyzersInfo;
}
var createValueCallback = new ConditionalWeakTable<ProjectId, ISkippedAnalyzersInfo>.CreateValueCallback(
_ => SkippedHostAnalyzersInfo.Create(project, this));
return _skippedAnalyzersInfo.GetValue(project.Id, createValueCallback);
var createValueCallback = new ConditionalWeakTable<IReadOnlyList<AnalyzerReference>, ISkippedAnalyzersInfo>.CreateValueCallback(
_ => SkippedHostAnalyzersInfo.Create(project, hostAnalyzers, this));
return _skippedAnalyzersInfo.GetValue(project.AnalyzerReferences, createValueCallback);
}
public void ClearProjectCache(ProjectId projectId)
=> _skippedAnalyzersInfo.Remove(projectId);
public ISkippedAnalyzersInfo GetOrCreateSkippedAnalyzersInfo(Project project, IEnumerable<AnalyzerReference> hostAnalyzers)
{
if (_skippedAnalyzersInfo.TryGetValue(project.AnalyzerReferences, out var skippedAnalyzersInfo))
{
return skippedAnalyzersInfo;
}
var createValueCallback = new ConditionalWeakTable<IReadOnlyList<AnalyzerReference>, ISkippedAnalyzersInfo>.CreateValueCallback(
_ => SkippedHostAnalyzersInfo.Create(project, hostAnalyzers, this));
return _skippedAnalyzersInfo.GetValue(project.AnalyzerReferences, createValueCallback);
}
}
}
......@@ -54,7 +54,7 @@ internal partial class DiagnosticIncrementalAnalyzer
return new DocumentAnalysisData(version, existingData.Items, ImmutableArray<DiagnosticData>.Empty);
}
var diagnostics = await AnalyzerHelper.ComputeDiagnosticsAsync(stateSet.Analyzer, document, kind, compilation, span: null, cancellationToken).ConfigureAwait(false);
var diagnostics = await AnalyzerHelper.ComputeDiagnosticsAsync(stateSet.Analyzer, document, kind, compilation, GetOrCreateSkippedAnalyzersInfo, span: null, cancellationToken).ConfigureAwait(false);
// this is no-op in product. only run in test environment
Logger.Log(functionId, (t, d, a, ds) => $"{GetDocumentLogMessage(t, d, a)}, {string.Join(Environment.NewLine, ds)}",
......@@ -180,7 +180,7 @@ private static bool CompilationHasOpenFileOnlyAnalyzers(CompilationWithAnalyzers
if (compilation != null && compilation.Analyzers.Length != 0)
{
// calculate regular diagnostic analyzers diagnostics
var resultMap = await _diagnosticAnalyzerRunner.AnalyzeAsync(compilation, project, forcedAnalysis, cancellationToken).ConfigureAwait(false);
var resultMap = await _diagnosticAnalyzerRunner.AnalyzeAsync(compilation, project, GetOrCreateSkippedAnalyzersInfo, forcedAnalysis, cancellationToken).ConfigureAwait(false);
result = resultMap.AnalysisResult;
......@@ -197,6 +197,9 @@ private static bool CompilationHasOpenFileOnlyAnalyzers(CompilationWithAnalyzers
}
}
private ISkippedAnalyzersInfo GetOrCreateSkippedAnalyzersInfo(Project project)
=> DiagnosticAnalyzerInfoCache.GetOrCreateSkippedAnalyzersInfo(project, HostAnalyzers);
private async Task<ImmutableDictionary<DiagnosticAnalyzer, DiagnosticAnalysisResult>> ComputeDiagnosticsAsync(
CompilationWithAnalyzers? compilation, Project project, IEnumerable<StateSet> stateSets, bool forcedAnalysis,
ImmutableDictionary<DiagnosticAnalyzer, DiagnosticAnalysisResult> existing, CancellationToken cancellationToken)
......
......@@ -48,7 +48,7 @@ public InProcOrRemoteHostAnalyzerRunner(IAsynchronousOperationListener operation
_lastOptionSetPerLanguage = new ConcurrentDictionary<string, ValueTuple<OptionSet, CustomAsset>>();
}
public async Task<DiagnosticAnalysisResultMap<DiagnosticAnalyzer, DiagnosticAnalysisResult>> AnalyzeAsync(CompilationWithAnalyzers compilation, Project project, bool forcedAnalysis, CancellationToken cancellationToken)
public async Task<DiagnosticAnalysisResultMap<DiagnosticAnalyzer, DiagnosticAnalysisResult>> AnalyzeAsync(CompilationWithAnalyzers compilation, Project project, Func<Project, ISkippedAnalyzersInfo>? getSkippedAnalyzersInfo, bool forcedAnalysis, CancellationToken cancellationToken)
{
Contract.ThrowIfFalse(compilation.Analyzers.Length != 0);
......@@ -57,14 +57,14 @@ public InProcOrRemoteHostAnalyzerRunner(IAsynchronousOperationListener operation
if (service == null)
{
// host doesn't support RemoteHostService such as under unit test
return await AnalyzeInProcAsync(compilation, project, client: null, cancellationToken).ConfigureAwait(false);
return await AnalyzeInProcAsync(compilation, project, client: null, getSkippedAnalyzersInfo, cancellationToken).ConfigureAwait(false);
}
var remoteHostClient = await service.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
if (remoteHostClient == null)
{
// remote host is not running. this can happen if remote host is disabled.
return await AnalyzeInProcAsync(compilation, project, client: null, cancellationToken).ConfigureAwait(false);
return await AnalyzeInProcAsync(compilation, project, client: null, getSkippedAnalyzersInfo, cancellationToken).ConfigureAwait(false);
}
// out of proc analysis will use 2 source of analyzers. one is AnalyzerReference from project (nuget). and the other is host analyzers (vsix)
......@@ -73,7 +73,7 @@ public InProcOrRemoteHostAnalyzerRunner(IAsynchronousOperationListener operation
}
private async Task<DiagnosticAnalysisResultMap<DiagnosticAnalyzer, DiagnosticAnalysisResult>> AnalyzeInProcAsync(
CompilationWithAnalyzers compilation, Project project, RemoteHostClient? client, CancellationToken cancellationToken)
CompilationWithAnalyzers compilation, Project project, RemoteHostClient? client, Func<Project, ISkippedAnalyzersInfo>? getSkippedAnalyzersInfo, CancellationToken cancellationToken)
{
Debug.Assert(compilation.Analyzers.Length != 0);
......@@ -87,7 +87,7 @@ public InProcOrRemoteHostAnalyzerRunner(IAsynchronousOperationListener operation
var _ = FireAndForgetReportAnalyzerPerformanceAsync(project, client, analysisResult, cancellationToken).CompletesAsyncOperation(asyncToken);
// get skipped analyzers info
var skippedAnalyzersInfo = _analyzerInfoCache.GetOrCreateSkippedAnalyzersInfo(project);
var skippedAnalyzersInfo = getSkippedAnalyzersInfo?.Invoke(project) ?? SkippedHostAnalyzersInfo.Default;
// get compiler result builder map
var builderMap = analysisResult.ToResultBuilderMap(project, version, compilation.Compilation, compilation.Analyzers, skippedAnalyzersInfo, cancellationToken);
......
......@@ -49,11 +49,12 @@ private partial class StateManager
Project project,
ImmutableDictionary<object, ImmutableArray<DiagnosticAnalyzer>> mapPerReferences,
ImmutableDictionary<DiagnosticAnalyzer, StateSet> analyzerMap,
DiagnosticAnalyzerInfoCache analyzerInfoCache)
DiagnosticAnalyzerInfoCache analyzerInfoCache,
HostDiagnosticAnalyzers hostAnalyzers)
: this(project.AnalyzerReferences,
mapPerReferences,
analyzerMap,
analyzerInfoCache.GetOrCreateSkippedAnalyzersInfo(project))
analyzerInfoCache.GetOrCreateSkippedAnalyzersInfo(project, hostAnalyzers))
{
Contract.ThrowIfNull(project);
Contract.ThrowIfNull(mapPerReferences);
......@@ -110,7 +111,7 @@ private ProjectAnalyzerStateSets CreateProjectStateSets(Project project)
}
var newMap = CreateStateSetMap(project.Language, analyzersPerReference.Values, includeFileContentLoadAnalyzer: false);
return new ProjectAnalyzerStateSets(project, analyzersPerReference, newMap, _analyzerInfoCache);
return new ProjectAnalyzerStateSets(project, analyzersPerReference, newMap, _analyzerInfoCache, _hostAnalyzers);
}
/// <summary>
......@@ -118,7 +119,6 @@ private ProjectAnalyzerStateSets CreateProjectStateSets(Project project)
/// </summary>
private ProjectAnalyzerStateSets UpdateProjectStateSets(Project project)
{
_analyzerInfoCache.ClearProjectCache(project.Id);
var projectStateSets = CreateProjectStateSets(project);
RaiseProjectAnalyzerReferenceChangedIfNeeded(project, projectStateSets.MapPerReferences, projectStateSets.StateSetMap);
......
......@@ -253,7 +253,6 @@ public bool OnProjectRemoved(IEnumerable<StateSet> stateSets, ProjectId projectI
}
_projectAnalyzerStateMap.TryRemove(projectId, out _);
_analyzerInfoCache.ClearProjectCache(projectId);
return removed;
}
......
......@@ -215,7 +215,7 @@ private async Task<IEnumerable<DiagnosticData>> GetCompilerSemanticDiagnosticsAs
private Task<IEnumerable<DiagnosticData>> GetSyntaxDiagnosticsAsync(DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
{
return AnalyzerHelper.ComputeDiagnosticsAsync(analyzer, _document, AnalysisKind.Syntax, _compilation, _range, cancellationToken);
return AnalyzerHelper.ComputeDiagnosticsAsync(analyzer, _document, AnalysisKind.Syntax, _compilation, _owner.GetOrCreateSkippedAnalyzersInfo, _range, cancellationToken);
}
private Task<IEnumerable<DiagnosticData>> GetSemanticDiagnosticsAsync(DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
......@@ -223,7 +223,7 @@ private Task<IEnumerable<DiagnosticData>> GetSemanticDiagnosticsAsync(Diagnostic
var supportsSemanticInSpan = analyzer.SupportsSpanBasedSemanticDiagnosticAnalysis();
var analysisSpan = supportsSemanticInSpan ? (TextSpan?)_range : null;
return AnalyzerHelper.ComputeDiagnosticsAsync(analyzer, _document, AnalysisKind.Semantic, _compilation, analysisSpan, cancellationToken);
return AnalyzerHelper.ComputeDiagnosticsAsync(analyzer, _document, AnalysisKind.Semantic, _compilation, _owner.GetOrCreateSkippedAnalyzersInfo, analysisSpan, cancellationToken);
}
private async Task<ImmutableArray<DiagnosticData>> GetProjectDiagnosticsAsync(DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
......
......@@ -46,15 +46,40 @@ internal sealed class SkippedHostAnalyzersInfo : ISkippedAnalyzersInfo
public static SkippedHostAnalyzersInfo Create(
Project project,
HostDiagnosticAnalyzers hostAnalyzers,
DiagnosticAnalyzerInfoCache analyzerInfoCache)
{
var projectAnalyzers = analyzerInfoCache.CreateProjectDiagnosticAnalyzersPerReference(project).SelectMany(v => v.Value);
var projectAnalyzers = hostAnalyzers.CreateProjectDiagnosticAnalyzersPerReference(project).SelectMany(v => v.Value);
if (projectAnalyzers.IsEmpty())
{
return Default;
}
ComputeSkippedHostAnalyzers(project, projectAnalyzers, analyzerInfoCache,
var hostAnalyzersPerReference = hostAnalyzers.GetOrCreateHostDiagnosticAnalyzersPerReference(project.Language).SelectMany(v => v.Value);
return Create(projectAnalyzers, hostAnalyzersPerReference, analyzerInfoCache);
}
public static SkippedHostAnalyzersInfo Create(
Project project,
IEnumerable<AnalyzerReference> hostAnalyzerReferences,
DiagnosticAnalyzerInfoCache analyzerInfoCache)
{
var projectAnalyzers = project.AnalyzerReferences.SelectMany(p => p.GetAnalyzers(project.Language));
if (projectAnalyzers.IsEmpty())
{
return Default;
}
var hostAnalyzers = hostAnalyzerReferences.SelectMany(p => p.GetAnalyzers(project.Language));
return Create(projectAnalyzers, hostAnalyzers, analyzerInfoCache);
}
private static SkippedHostAnalyzersInfo Create(
IEnumerable<DiagnosticAnalyzer> projectAnalyzers,
IEnumerable<DiagnosticAnalyzer> hostAnalyzers,
DiagnosticAnalyzerInfoCache analyzerInfoCache)
{
ComputeSkippedHostAnalyzers(projectAnalyzers, hostAnalyzers, analyzerInfoCache,
out var fullySkippedHostAnalyzers, out var filteredDiagnosticIdsForAnalyzers);
if (fullySkippedHostAnalyzers.IsEmpty && filteredDiagnosticIdsForAnalyzers.IsEmpty)
{
......@@ -64,13 +89,12 @@ internal sealed class SkippedHostAnalyzersInfo : ISkippedAnalyzersInfo
return new SkippedHostAnalyzersInfo(fullySkippedHostAnalyzers, filteredDiagnosticIdsForAnalyzers);
static void ComputeSkippedHostAnalyzers(
Project project,
IEnumerable<DiagnosticAnalyzer> projectAnalyzers,
IEnumerable<DiagnosticAnalyzer> hostAnalyzers,
DiagnosticAnalyzerInfoCache analyzerInfoCache,
out ImmutableHashSet<DiagnosticAnalyzer> fullySkippedHostAnalyzers,
out ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<string>> filteredDiagnosticIdsForAnalyzers)
{
var hostAnalyzers = analyzerInfoCache.GetOrCreateHostDiagnosticAnalyzersPerReference(project.Language).SelectMany(v => v.Value);
var idsReportedByProjectAnalyzers = GetIdsReportedByProjectAnalyzers(projectAnalyzers, analyzerInfoCache);
var fullySkippedHostAnalyzersBuilder = ImmutableHashSet.CreateBuilder<DiagnosticAnalyzer>();
var partiallySkippedHostAnalyzersBuilder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableArray<string>>();
......
......@@ -201,10 +201,10 @@ void Method()
new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution));
// no result for open file only analyzer unless forced
var result = await runner.AnalyzeAsync(compilationWithAnalyzers, project, forcedAnalysis: false, cancellationToken: CancellationToken.None);
var result = await runner.AnalyzeAsync(compilationWithAnalyzers, project, getSkippedAnalyzersInfo: null, forcedAnalysis: false, cancellationToken: CancellationToken.None);
Assert.Empty(result.AnalysisResult);
result = await runner.AnalyzeAsync(compilationWithAnalyzers, project, forcedAnalysis: true, cancellationToken: CancellationToken.None);
result = await runner.AnalyzeAsync(compilationWithAnalyzers, project, getSkippedAnalyzersInfo: null, forcedAnalysis: true, cancellationToken: CancellationToken.None);
var analyzerResult = result.AnalysisResult[compilationWithAnalyzers.Analyzers[0]];
// check result
......@@ -253,7 +253,7 @@ void Method()
analyzerReference.GetAnalyzers(project.Language).Where(a => a.GetType() == analyzerType).ToImmutableArray(),
new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution));
var result = await runner.AnalyzeAsync(compilationWithAnalyzers, project, forcedAnalysis: false, cancellationToken: CancellationToken.None);
var result = await runner.AnalyzeAsync(compilationWithAnalyzers, project, getSkippedAnalyzersInfo: null, forcedAnalysis: false, cancellationToken: CancellationToken.None);
var analyzerResult = result.AnalysisResult[compilationWithAnalyzers.Analyzers[0]];
......@@ -282,7 +282,7 @@ private static async Task<DiagnosticAnalysisResult> AnalyzeAsync(TestWorkspace w
analyzerReference.GetAnalyzers(project.Language).Where(a => a.GetType() == analyzerType).ToImmutableArray(),
new WorkspaceAnalyzerOptions(project.AnalyzerOptions, project.Solution));
var result = await executor.AnalyzeAsync(analyzerDriver, project, forcedAnalysis: true, cancellationToken: cancellationToken);
var result = await executor.AnalyzeAsync(analyzerDriver, project, getSkippedAnalyzersInfo: null, forcedAnalysis: true, cancellationToken: cancellationToken);
return result.AnalysisResult[analyzerDriver.Analyzers[0]];
}
......
......@@ -50,12 +50,14 @@ public DiagnosticComputer(Project project, DiagnosticAnalyzerInfoCache analyzerI
var cacheService = _project.Solution.Workspace.Services.GetRequiredService<IProjectCacheService>();
using var cache = cacheService.EnableCaching(_project.Id);
return await AnalyzeAsync(analyzerMap, analyzers, reportSuppressedDiagnostics, logAnalyzerExecutionTime, cancellationToken).ConfigureAwait(false);
var skippedAnalyzersInfo = _analyzerInfoCache.GetOrCreateSkippedAnalyzersInfo(_project, hostAnalyzers);
return await AnalyzeAsync(analyzerMap, analyzers, skippedAnalyzersInfo, reportSuppressedDiagnostics, logAnalyzerExecutionTime, cancellationToken).ConfigureAwait(false);
}
private async Task<DiagnosticAnalysisResultMap<string, DiagnosticAnalysisResultBuilder>> AnalyzeAsync(
BidirectionalMap<string, DiagnosticAnalyzer> analyzerMap,
ImmutableArray<DiagnosticAnalyzer> analyzers,
ISkippedAnalyzersInfo skippedAnalyzersInfo,
bool reportSuppressedDiagnostics,
bool logAnalyzerExecutionTime,
CancellationToken cancellationToken)
......@@ -93,9 +95,6 @@ public DiagnosticComputer(Project project, DiagnosticAnalyzerInfoCache analyzerI
_performanceTracker.AddSnapshot(analysisResult.AnalyzerTelemetryInfo.ToAnalyzerPerformanceInfo(_analyzerInfoCache), _project.DocumentIds.Count + 1);
}
// get skipped analyzers info
var skippedAnalyzersInfo = _analyzerInfoCache.GetOrCreateSkippedAnalyzersInfo(_project);
var builderMap = analysisResult.ToResultBuilderMap(_project, VersionStamp.Default, compilation, analysisResult.Analyzers, skippedAnalyzersInfo, cancellationToken);
return DiagnosticAnalysisResultMap.Create(
......
......@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册