提交 bfcdc0f4 编写于 作者: M Manish Vasani

Merge pull request #10616 from mavasani/Issue10530

Address performance regression in CompilationWithAnalyzers.GetAnalyze…
......@@ -1709,9 +1709,7 @@ private void CompleteTree(SyntaxTree tree)
if (_lazyCompilationUnitCompletedTrees.Count == this.SyntaxTrees.Length)
{
// if that was the last tree, signal the end of compilation
EventQueue.TryEnqueue(new CompilationCompletedEvent(this));
EventQueue.PromiseNotToEnqueue();
EventQueue.TryComplete();
CompleteCompilationEventQueue_NoLock();
}
}
}
......@@ -1950,6 +1948,11 @@ internal ImmutableArray<Diagnostic> GetDiagnostics(CompilationStage stage, bool
cancellationToken.ThrowIfCancellationRequested();
builder.AddRange(GetSourceDeclarationDiagnostics(cancellationToken: cancellationToken));
if (EventQueue != null && SyntaxTrees.Length == 0)
{
EnsureCompilationEventQueueCompleted();
}
}
cancellationToken.ThrowIfCancellationRequested();
......
......@@ -6,6 +6,7 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
......@@ -287,5 +288,16 @@ private static bool DequeueCompilationEvents(AsyncQueue<CompilationEvent> eventQ
return true;
}
[Fact]
public void TestEventQueueCompletionForEmptyCompilation()
{
var compilation = CreateCompilationWithMscorlib45(SpecializedCollections.EmptyEnumerable<SyntaxTree>()).WithEventQueue(new AsyncQueue<CompilationEvent>());
// Force complete compilation event queue
var unused = compilation.GetDiagnostics();
Assert.True(compilation.EventQueue.IsCompleted);
}
}
}
......@@ -62,6 +62,7 @@
<Compile Include="Binding\AbstractLookupSymbolsInfo.cs" />
<Compile Include="CaseInsensitiveComparison.cs" />
<Compile Include="CodeGen\ILEmitStyle.cs" />
<Compile Include="DiagnosticAnalyzer\AnalysisResult.cs" />
<Compile Include="InternalUtilities\EmptyComparer.cs" />
<Compile Include="InternalUtilities\StringOrdinalComparer.cs" />
<Compile Include="MetadataReference\AssemblyIdentityMap.cs" />
......@@ -177,7 +178,7 @@
<Compile Include="Compilation\CommonSyntaxAndDeclarationManager.cs" />
<Compile Include="Compilation\SymbolFilter.cs" />
<Compile Include="CompilerPathUtilities.cs" />
<Compile Include="DiagnosticAnalyzer\AnalysisResult.cs" />
<Compile Include="DiagnosticAnalyzer\AnalysisResultBuilder.cs" />
<Compile Include="DiagnosticAnalyzer\AnalysisScope.cs" />
<Compile Include="DiagnosticAnalyzer\AnalysisState.AnalyzerStateData.cs" />
<Compile Include="DiagnosticAnalyzer\AnalysisState.cs" />
......
......@@ -851,6 +851,29 @@ public INamedTypeSymbol GetTypeByMetadataName(string fullyQualifiedMetadataName)
/// </summary>
public abstract ImmutableArray<Diagnostic> GetDiagnostics(CancellationToken cancellationToken = default(CancellationToken));
internal void EnsureCompilationEventQueueCompleted()
{
Debug.Assert(EventQueue != null);
lock (EventQueue)
{
if (!EventQueue.IsCompleted)
{
CompleteCompilationEventQueue_NoLock();
}
}
}
internal void CompleteCompilationEventQueue_NoLock()
{
Debug.Assert(EventQueue != null);
// Signal the end of compilation.
EventQueue.TryEnqueue(new CompilationCompletedEvent(this));
EventQueue.PromiseNotToEnqueue();
EventQueue.TryComplete();
}
internal abstract CommonMessageProvider MessageProvider { get; }
/// <param name="accumulator">Bag to which filtered diagnostics will be added.</param>
......
......@@ -3,8 +3,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics.Telemetry;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
{
......@@ -13,195 +14,143 @@ namespace Microsoft.CodeAnalysis.Diagnostics
/// 1. Local and non-local diagnostics, per-analyzer.
/// 2. Analyzer execution times, if requested.
/// </summary>
internal partial class AnalysisResult
public class AnalysisResult
{
private readonly object _gate = new object();
private readonly Dictionary<DiagnosticAnalyzer, TimeSpan> _analyzerExecutionTimeOpt;
private Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, List<Diagnostic>>> _localSemanticDiagnosticsOpt = null;
private Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, List<Diagnostic>>> _localSyntaxDiagnosticsOpt = null;
private Dictionary<DiagnosticAnalyzer, List<Diagnostic>> _nonLocalDiagnosticsOpt = null;
public AnalysisResult(bool logAnalyzerExecutionTime, ImmutableArray<DiagnosticAnalyzer> analyzers)
internal AnalysisResult(
ImmutableArray<DiagnosticAnalyzer> analyzers,
ImmutableDictionary<SyntaxTree, ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>> localSyntaxDiagnostics,
ImmutableDictionary<SyntaxTree, ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>> localSemanticDiagnostics,
ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>> nonLocalDiagnostics,
ImmutableDictionary<DiagnosticAnalyzer, AnalyzerTelemetryInfo> analyzerTelemetryInfo)
{
_analyzerExecutionTimeOpt = logAnalyzerExecutionTime ? CreateAnalyzerExecutionTimeMap(analyzers) : null;
Analyzers = analyzers;
SyntaxDiagnostics = localSyntaxDiagnostics;
SemanticDiagnostics = localSemanticDiagnostics;
CompilationDiagnostics = nonLocalDiagnostics;
AnalyzerTelemetryInfo = analyzerTelemetryInfo;
}
private static Dictionary<DiagnosticAnalyzer, TimeSpan> CreateAnalyzerExecutionTimeMap(ImmutableArray<DiagnosticAnalyzer> analyzers)
/// <summary>
/// Analyzers corresponding to this analysis result.
/// </summary>
public ImmutableArray<DiagnosticAnalyzer> Analyzers { get; private set; }
/// <summary>
/// Syntax diagnostics reported by the <see cref="Analyzers"/>.
/// </summary>
public ImmutableDictionary<SyntaxTree, ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>> SyntaxDiagnostics { get; private set; }
/// <summary>
/// Semantic diagnostics reported by the <see cref="Analyzers"/>.
/// </summary>
public ImmutableDictionary<SyntaxTree, ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>> SemanticDiagnostics { get; private set; }
/// <summary>
/// Compilation diagnostics reported by the <see cref="Analyzers"/>.
/// </summary>
public ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>> CompilationDiagnostics { get; private set; }
/// <summary>
/// Analyzer telemetry info (register action counts and execution times).
/// </summary>
public ImmutableDictionary<DiagnosticAnalyzer, AnalyzerTelemetryInfo> AnalyzerTelemetryInfo { get; private set; }
/// <summary>
/// Gets all the diagnostics reported by the given <paramref name="analyzer"/>.
/// </summary>
public ImmutableArray<Diagnostic> GetAllDiagnostics(DiagnosticAnalyzer analyzer)
{
var map = new Dictionary<DiagnosticAnalyzer, TimeSpan>(analyzers.Length);
foreach (var analyzer in analyzers)
if (!Analyzers.Contains(analyzer))
{
map[analyzer] = default(TimeSpan);
throw new ArgumentException(CodeAnalysisResources.UnsupportedAnalyzerInstance, nameof(analyzer));
}
return map;
}
public TimeSpan GetAnalyzerExecutionTime(DiagnosticAnalyzer analyzer)
{
Debug.Assert(_analyzerExecutionTimeOpt != null);
lock (_gate)
{
return _analyzerExecutionTimeOpt[analyzer];
}
return GetDiagnostics(SpecializedCollections.SingletonEnumerable(analyzer), getLocalSyntaxDiagnostics: true, getLocalSemanticDiagnostics: true, getNonLocalDiagnostics: true);
}
public void StoreAnalysisResult(AnalysisScope analysisScope, AnalyzerDriver driver, Compilation compilation)
/// <summary>
/// Gets all the diagnostics reported by all the <see cref="Analyzers"/>.
/// </summary>
public ImmutableArray<Diagnostic> GetAllDiagnostics()
{
foreach (var analyzer in analysisScope.Analyzers)
{
// Dequeue reported analyzer diagnostics from the driver and store them in our maps.
var syntaxDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: true, compilation: compilation);
var semanticDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: false, compilation: compilation);
var compilationDiagnostics = driver.DequeueNonLocalDiagnostics(analyzer, compilation);
lock (_gate)
{
if (syntaxDiagnostics.Length > 0 || semanticDiagnostics.Length > 0 || compilationDiagnostics.Length > 0)
{
UpdateLocalDiagnostics_NoLock(analyzer, syntaxDiagnostics, ref _localSyntaxDiagnosticsOpt);
UpdateLocalDiagnostics_NoLock(analyzer, semanticDiagnostics, ref _localSemanticDiagnosticsOpt);
UpdateNonLocalDiagnostics_NoLock(analyzer, compilationDiagnostics);
}
if (_analyzerExecutionTimeOpt != null)
{
_analyzerExecutionTimeOpt[analyzer] += driver.ResetAnalyzerExecutionTime(analyzer);
}
}
}
return GetDiagnostics(Analyzers, getLocalSyntaxDiagnostics: true, getLocalSemanticDiagnostics: true, getNonLocalDiagnostics: true);
}
private void UpdateLocalDiagnostics_NoLock(DiagnosticAnalyzer analyzer, ImmutableArray<Diagnostic> diagnostics, ref Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, List<Diagnostic>>> lazyLocalDiagnostics)
internal ImmutableArray<Diagnostic> GetSyntaxDiagnostics(ImmutableArray<DiagnosticAnalyzer> analyzers)
{
if (diagnostics.IsEmpty)
{
return;
}
lazyLocalDiagnostics = lazyLocalDiagnostics ?? new Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, List<Diagnostic>>>();
foreach (var diagsByTree in diagnostics.GroupBy(d => d.Location.SourceTree))
{
var tree = diagsByTree.Key;
Dictionary<DiagnosticAnalyzer, List<Diagnostic>> allDiagnostics;
if (!lazyLocalDiagnostics.TryGetValue(tree, out allDiagnostics))
{
allDiagnostics = new Dictionary<DiagnosticAnalyzer, List<Diagnostic>>();
lazyLocalDiagnostics[tree] = allDiagnostics;
}
List<Diagnostic> analyzerDiagnostics;
if (!allDiagnostics.TryGetValue(analyzer, out analyzerDiagnostics))
{
analyzerDiagnostics = new List<Diagnostic>();
allDiagnostics[analyzer] = analyzerDiagnostics;
}
analyzerDiagnostics.AddRange(diagsByTree);
}
return GetDiagnostics(analyzers, getLocalSyntaxDiagnostics: true, getLocalSemanticDiagnostics: false, getNonLocalDiagnostics: false);
}
private void UpdateNonLocalDiagnostics_NoLock(DiagnosticAnalyzer analyzer, ImmutableArray<Diagnostic> diagnostics)
internal ImmutableArray<Diagnostic> GetSemanticDiagnostics(ImmutableArray<DiagnosticAnalyzer> analyzers)
{
if (diagnostics.IsEmpty)
{
return;
}
_nonLocalDiagnosticsOpt = _nonLocalDiagnosticsOpt ?? new Dictionary<DiagnosticAnalyzer, List<Diagnostic>>();
List<Diagnostic> currentDiagnostics;
if (!_nonLocalDiagnosticsOpt.TryGetValue(analyzer, out currentDiagnostics))
{
currentDiagnostics = new List<Diagnostic>();
_nonLocalDiagnosticsOpt[analyzer] = currentDiagnostics;
}
currentDiagnostics.AddRange(diagnostics);
return GetDiagnostics(analyzers, getLocalSyntaxDiagnostics: false, getLocalSemanticDiagnostics: true, getNonLocalDiagnostics: false);
}
public ImmutableArray<Diagnostic> GetDiagnostics(AnalysisScope analysisScope, bool getLocalDiagnostics, bool getNonLocalDiagnostics)
internal ImmutableArray<Diagnostic> GetDiagnostics(IEnumerable<DiagnosticAnalyzer> analyzers, bool getLocalSyntaxDiagnostics, bool getLocalSemanticDiagnostics, bool getNonLocalDiagnostics)
{
lock (_gate)
{
return GetDiagnostics_NoLock(analysisScope, getLocalDiagnostics, getNonLocalDiagnostics);
}
var excludedAnalyzers = Analyzers.Except(analyzers);
var excludedAnalyzersSet = excludedAnalyzers.Any() ? excludedAnalyzers.ToImmutableHashSet() : ImmutableHashSet<DiagnosticAnalyzer>.Empty;
return GetDiagnostics(excludedAnalyzersSet, getLocalSyntaxDiagnostics, getLocalSemanticDiagnostics, getNonLocalDiagnostics);
}
private ImmutableArray<Diagnostic> GetDiagnostics_NoLock(AnalysisScope analysisScope, bool getLocalDiagnostics, bool getNonLocalDiagnostics)
private ImmutableArray<Diagnostic> GetDiagnostics(ImmutableHashSet<DiagnosticAnalyzer> excludedAnalyzers, bool getLocalSyntaxDiagnostics, bool getLocalSemanticDiagnostics, bool getNonLocalDiagnostics)
{
Debug.Assert(getLocalDiagnostics || getNonLocalDiagnostics);
var builder = ImmutableArray.CreateBuilder<Diagnostic>();
if (getLocalDiagnostics)
if (SyntaxDiagnostics.Count > 0 || SemanticDiagnostics.Count > 0 || CompilationDiagnostics.Count > 0)
{
if (!analysisScope.IsTreeAnalysis)
var builder = ImmutableArray.CreateBuilder<Diagnostic>();
if (getLocalSyntaxDiagnostics)
{
AddAllLocalDiagnostics_NoLock(_localSyntaxDiagnosticsOpt, analysisScope, builder);
AddAllLocalDiagnostics_NoLock(_localSemanticDiagnosticsOpt, analysisScope, builder);
AddLocalDiagnostics(SyntaxDiagnostics, excludedAnalyzers, builder);
}
else if (analysisScope.IsSyntaxOnlyTreeAnalysis)
if (getLocalSemanticDiagnostics)
{
AddLocalDiagnosticsForPartialAnalysis_NoLock(_localSyntaxDiagnosticsOpt, analysisScope, builder);
AddLocalDiagnostics(SemanticDiagnostics, excludedAnalyzers, builder);
}
else
if (getNonLocalDiagnostics)
{
AddLocalDiagnosticsForPartialAnalysis_NoLock(_localSemanticDiagnosticsOpt, analysisScope, builder);
AddNonLocalDiagnostics(CompilationDiagnostics, excludedAnalyzers, builder);
}
}
if (getNonLocalDiagnostics && _nonLocalDiagnosticsOpt != null)
{
AddDiagnostics_NoLock(_nonLocalDiagnosticsOpt, analysisScope, builder);
return builder.ToImmutable();
}
return builder.ToImmutableArray();
return ImmutableArray<Diagnostic>.Empty;
}
private static void AddAllLocalDiagnostics_NoLock(
Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, List<Diagnostic>>> localDiagnostics,
AnalysisScope analysisScope,
private static void AddLocalDiagnostics(
ImmutableDictionary<SyntaxTree, ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>> localDiagnostics,
ImmutableHashSet<DiagnosticAnalyzer> excludedAnalyzers,
ImmutableArray<Diagnostic>.Builder builder)
{
if (localDiagnostics != null)
foreach (var diagnosticsByTree in localDiagnostics)
{
foreach (var localDiagsByTree in localDiagnostics.Values)
foreach (var diagnosticsByAnalyzer in diagnosticsByTree.Value)
{
AddDiagnostics_NoLock(localDiagsByTree, analysisScope, builder);
}
}
}
if (excludedAnalyzers.Contains(diagnosticsByAnalyzer.Key))
{
continue;
}
private static void AddLocalDiagnosticsForPartialAnalysis_NoLock(
Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, List<Diagnostic>>> localDiagnostics,
AnalysisScope analysisScope,
ImmutableArray<Diagnostic>.Builder builder)
{
Dictionary<DiagnosticAnalyzer, List<Diagnostic>> diagnosticsForTree;
if (localDiagnostics != null && localDiagnostics.TryGetValue(analysisScope.FilterTreeOpt, out diagnosticsForTree))
{
AddDiagnostics_NoLock(diagnosticsForTree, analysisScope, builder);
builder.AddRange(diagnosticsByAnalyzer.Value);
}
}
}
private static void AddDiagnostics_NoLock(
Dictionary<DiagnosticAnalyzer, List<Diagnostic>> diagnostics,
AnalysisScope analysisScope,
private static void AddNonLocalDiagnostics(
ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>> nonLocalDiagnostics,
ImmutableHashSet<DiagnosticAnalyzer> excludedAnalyzers,
ImmutableArray<Diagnostic>.Builder builder)
{
Debug.Assert(diagnostics != null);
foreach (var analyzer in analysisScope.Analyzers)
foreach (var diagnosticsByAnalyzer in nonLocalDiagnostics)
{
List<Diagnostic> diagnosticsByAnalyzer;
if (diagnostics.TryGetValue(analyzer, out diagnosticsByAnalyzer))
if (excludedAnalyzers.Contains(diagnosticsByAnalyzer.Key))
{
builder.AddRange(diagnosticsByAnalyzer);
continue;
}
builder.AddRange(diagnosticsByAnalyzer.Value);
}
}
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Diagnostics.Telemetry;
namespace Microsoft.CodeAnalysis.Diagnostics
{
/// <summary>
/// Stores the results of analyzer execution:
/// 1. Local and non-local diagnostics, per-analyzer.
/// 2. Analyzer execution times, if requested.
/// </summary>
internal sealed class AnalysisResultBuilder
{
private readonly object _gate = new object();
private readonly Dictionary<DiagnosticAnalyzer, TimeSpan> _analyzerExecutionTimeOpt;
private readonly HashSet<DiagnosticAnalyzer> _completedAnalyzers;
private readonly Dictionary<DiagnosticAnalyzer, AnalyzerActionCounts> _analyzerActionCounts;
private Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder>> _localSemanticDiagnosticsOpt = null;
private Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder>> _localSyntaxDiagnosticsOpt = null;
private Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder> _nonLocalDiagnosticsOpt = null;
internal AnalysisResultBuilder(bool logAnalyzerExecutionTime, ImmutableArray<DiagnosticAnalyzer> analyzers)
{
_analyzerExecutionTimeOpt = logAnalyzerExecutionTime ? CreateAnalyzerExecutionTimeMap(analyzers) : null;
_completedAnalyzers = new HashSet<DiagnosticAnalyzer>();
_analyzerActionCounts = new Dictionary<DiagnosticAnalyzer, AnalyzerActionCounts>(analyzers.Length);
}
private static Dictionary<DiagnosticAnalyzer, TimeSpan> CreateAnalyzerExecutionTimeMap(ImmutableArray<DiagnosticAnalyzer> analyzers)
{
var map = new Dictionary<DiagnosticAnalyzer, TimeSpan>(analyzers.Length);
foreach (var analyzer in analyzers)
{
map[analyzer] = default(TimeSpan);
}
return map;
}
public TimeSpan GetAnalyzerExecutionTime(DiagnosticAnalyzer analyzer)
{
Debug.Assert(_analyzerExecutionTimeOpt != null);
lock (_gate)
{
return _analyzerExecutionTimeOpt[analyzer];
}
}
internal ImmutableArray<DiagnosticAnalyzer> GetPendingAnalyzers(ImmutableArray<DiagnosticAnalyzer> analyzers)
{
lock (_gate)
{
ArrayBuilder<DiagnosticAnalyzer> builder = null;
foreach (var analyzer in analyzers)
{
if (!_completedAnalyzers.Contains(analyzer))
{
builder = builder ?? ArrayBuilder<DiagnosticAnalyzer>.GetInstance();
builder.Add(analyzer);
}
}
return builder != null ? builder.ToImmutableAndFree() : ImmutableArray<DiagnosticAnalyzer>.Empty;
}
}
internal void StoreAnalysisResult(AnalysisScope analysisScope, AnalyzerDriver driver, Compilation compilation, Func<DiagnosticAnalyzer, AnalyzerActionCounts> getAnalyzerActionCounts, bool fullAnalysisResultForAnalyzersInScope)
{
Debug.Assert(!fullAnalysisResultForAnalyzersInScope || analysisScope.FilterTreeOpt == null, "Full analysis result cannot come from partial (tree) analysis.");
foreach (var analyzer in analysisScope.Analyzers)
{
// Dequeue reported analyzer diagnostics from the driver and store them in our maps.
var syntaxDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: true, compilation: compilation);
var semanticDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: false, compilation: compilation);
var compilationDiagnostics = driver.DequeueNonLocalDiagnostics(analyzer, compilation);
lock (_gate)
{
if (_completedAnalyzers.Contains(analyzer))
{
// Already stored full analysis result for this analyzer.
continue;
}
if (syntaxDiagnostics.Length > 0 || semanticDiagnostics.Length > 0 || compilationDiagnostics.Length > 0 || fullAnalysisResultForAnalyzersInScope)
{
UpdateLocalDiagnostics_NoLock(analyzer, syntaxDiagnostics, fullAnalysisResultForAnalyzersInScope, ref _localSyntaxDiagnosticsOpt);
UpdateLocalDiagnostics_NoLock(analyzer, semanticDiagnostics, fullAnalysisResultForAnalyzersInScope, ref _localSemanticDiagnosticsOpt);
UpdateNonLocalDiagnostics_NoLock(analyzer, compilationDiagnostics, fullAnalysisResultForAnalyzersInScope);
}
if (_analyzerExecutionTimeOpt != null)
{
var timeSpan = driver.ResetAnalyzerExecutionTime(analyzer);
_analyzerExecutionTimeOpt[analyzer] = fullAnalysisResultForAnalyzersInScope ?
timeSpan :
_analyzerExecutionTimeOpt[analyzer] + timeSpan;
}
if (!_analyzerActionCounts.ContainsKey(analyzer))
{
_analyzerActionCounts.Add(analyzer, getAnalyzerActionCounts(analyzer));
}
if (fullAnalysisResultForAnalyzersInScope)
{
_completedAnalyzers.Add(analyzer);
}
}
}
}
private void UpdateLocalDiagnostics_NoLock(
DiagnosticAnalyzer analyzer,
ImmutableArray<Diagnostic> diagnostics,
bool overwrite,
ref Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder>> lazyLocalDiagnostics)
{
if (diagnostics.IsEmpty)
{
return;
}
lazyLocalDiagnostics = lazyLocalDiagnostics ?? new Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder>>();
foreach (var diagsByTree in diagnostics.GroupBy(d => d.Location.SourceTree))
{
var tree = diagsByTree.Key;
Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder> allDiagnostics;
if (!lazyLocalDiagnostics.TryGetValue(tree, out allDiagnostics))
{
allDiagnostics = new Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder>();
lazyLocalDiagnostics[tree] = allDiagnostics;
}
ImmutableArray<Diagnostic>.Builder analyzerDiagnostics;
if (!allDiagnostics.TryGetValue(analyzer, out analyzerDiagnostics))
{
analyzerDiagnostics = ImmutableArray.CreateBuilder<Diagnostic>();
allDiagnostics[analyzer] = analyzerDiagnostics;
}
if (overwrite)
{
analyzerDiagnostics.Clear();
}
analyzerDiagnostics.AddRange(diagsByTree);
}
}
private void UpdateNonLocalDiagnostics_NoLock(DiagnosticAnalyzer analyzer, ImmutableArray<Diagnostic> diagnostics, bool overwrite)
{
if (diagnostics.IsEmpty)
{
return;
}
_nonLocalDiagnosticsOpt = _nonLocalDiagnosticsOpt ?? new Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder>();
ImmutableArray<Diagnostic>.Builder currentDiagnostics;
if (!_nonLocalDiagnosticsOpt.TryGetValue(analyzer, out currentDiagnostics))
{
currentDiagnostics = ImmutableArray.CreateBuilder<Diagnostic>();
_nonLocalDiagnosticsOpt[analyzer] = currentDiagnostics;
}
if (overwrite)
{
currentDiagnostics.Clear();
}
currentDiagnostics.AddRange(diagnostics);
}
internal ImmutableArray<Diagnostic> GetDiagnostics(AnalysisScope analysisScope, bool getLocalDiagnostics, bool getNonLocalDiagnostics)
{
lock (_gate)
{
return GetDiagnostics_NoLock(analysisScope, getLocalDiagnostics, getNonLocalDiagnostics);
}
}
private ImmutableArray<Diagnostic> GetDiagnostics_NoLock(AnalysisScope analysisScope, bool getLocalDiagnostics, bool getNonLocalDiagnostics)
{
Debug.Assert(getLocalDiagnostics || getNonLocalDiagnostics);
var builder = ImmutableArray.CreateBuilder<Diagnostic>();
if (getLocalDiagnostics)
{
if (!analysisScope.IsTreeAnalysis)
{
AddAllLocalDiagnostics_NoLock(_localSyntaxDiagnosticsOpt, analysisScope, builder);
AddAllLocalDiagnostics_NoLock(_localSemanticDiagnosticsOpt, analysisScope, builder);
}
else if (analysisScope.IsSyntaxOnlyTreeAnalysis)
{
AddLocalDiagnosticsForPartialAnalysis_NoLock(_localSyntaxDiagnosticsOpt, analysisScope, builder);
}
else
{
AddLocalDiagnosticsForPartialAnalysis_NoLock(_localSemanticDiagnosticsOpt, analysisScope, builder);
}
}
if (getNonLocalDiagnostics && _nonLocalDiagnosticsOpt != null)
{
AddDiagnostics_NoLock(_nonLocalDiagnosticsOpt, analysisScope, builder);
}
return builder.ToImmutableArray();
}
private static void AddAllLocalDiagnostics_NoLock(
Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder>> localDiagnostics,
AnalysisScope analysisScope,
ImmutableArray<Diagnostic>.Builder builder)
{
if (localDiagnostics != null)
{
foreach (var localDiagsByTree in localDiagnostics.Values)
{
AddDiagnostics_NoLock(localDiagsByTree, analysisScope, builder);
}
}
}
private static void AddLocalDiagnosticsForPartialAnalysis_NoLock(
Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder>> localDiagnostics,
AnalysisScope analysisScope,
ImmutableArray<Diagnostic>.Builder builder)
{
Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder> diagnosticsForTree;
if (localDiagnostics != null && localDiagnostics.TryGetValue(analysisScope.FilterTreeOpt, out diagnosticsForTree))
{
AddDiagnostics_NoLock(diagnosticsForTree, analysisScope, builder);
}
}
private static void AddDiagnostics_NoLock(
Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder> diagnostics,
AnalysisScope analysisScope,
ImmutableArray<Diagnostic>.Builder builder)
{
Debug.Assert(diagnostics != null);
foreach (var analyzer in analysisScope.Analyzers)
{
ImmutableArray<Diagnostic>.Builder diagnosticsByAnalyzer;
if (diagnostics.TryGetValue(analyzer, out diagnosticsByAnalyzer))
{
builder.AddRange(diagnosticsByAnalyzer);
}
}
}
internal AnalysisResult ToAnalysisResult(ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ImmutableDictionary<SyntaxTree, ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>> localSyntaxDiagnostics;
ImmutableDictionary<SyntaxTree, ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>> localSemanticDiagnostics;
ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>> nonLocalDiagnostics;
var analyzersSet = analyzers.ToImmutableHashSet();
lock (_gate)
{
localSyntaxDiagnostics = GetImmutable(analyzersSet, _localSyntaxDiagnosticsOpt);
localSemanticDiagnostics = GetImmutable(analyzersSet, _localSemanticDiagnosticsOpt);
nonLocalDiagnostics = GetImmutable(analyzersSet, _nonLocalDiagnosticsOpt);
}
cancellationToken.ThrowIfCancellationRequested();
var analyzerTelemetryInfo = GetTelemetryInfo(analyzers, cancellationToken);
return new AnalysisResult(analyzers, localSyntaxDiagnostics, localSemanticDiagnostics, nonLocalDiagnostics, analyzerTelemetryInfo);
}
private static ImmutableDictionary<SyntaxTree, ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>> GetImmutable(
ImmutableHashSet<DiagnosticAnalyzer> analyzers,
Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder>> localDiagnosticsOpt)
{
if (localDiagnosticsOpt == null)
{
return ImmutableDictionary<SyntaxTree, ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>>.Empty;
}
var builder = ImmutableDictionary.CreateBuilder<SyntaxTree, ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>>();
var perTreeBuilder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>();
foreach (var diagnosticsByTree in localDiagnosticsOpt)
{
var tree = diagnosticsByTree.Key;
foreach (var diagnosticsByAnalyzer in diagnosticsByTree.Value)
{
if (analyzers.Contains(diagnosticsByAnalyzer.Key))
{
perTreeBuilder.Add(diagnosticsByAnalyzer.Key, diagnosticsByAnalyzer.Value.ToImmutable());
}
}
builder.Add(tree, perTreeBuilder.ToImmutable());
perTreeBuilder.Clear();
}
return builder.ToImmutable();
}
private static ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>> GetImmutable(
ImmutableHashSet<DiagnosticAnalyzer> analyzers,
Dictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>.Builder> nonLocalDiagnosticsOpt)
{
if (nonLocalDiagnosticsOpt == null)
{
return ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>.Empty;
}
var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ImmutableArray<Diagnostic>>();
foreach (var diagnosticsByAnalyzer in nonLocalDiagnosticsOpt)
{
if (analyzers.Contains(diagnosticsByAnalyzer.Key))
{
builder.Add(diagnosticsByAnalyzer.Key, diagnosticsByAnalyzer.Value.ToImmutable());
}
}
return builder.ToImmutable();
}
private ImmutableDictionary<DiagnosticAnalyzer, AnalyzerTelemetryInfo> GetTelemetryInfo(
ImmutableArray<DiagnosticAnalyzer> analyzers,
CancellationToken cancellationToken)
{
var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, AnalyzerTelemetryInfo>();
lock (_gate)
{
foreach (var analyzer in analyzers)
{
var actionCounts = _analyzerActionCounts[analyzer];
var executionTime = _analyzerExecutionTimeOpt != null ? _analyzerExecutionTimeOpt[analyzer] : default(TimeSpan);
var telemetryInfo = new AnalyzerTelemetryInfo(actionCounts, executionTime);
builder.Add(analyzer, telemetryInfo);
}
}
return builder.ToImmutable();
}
}
}
......@@ -44,30 +44,32 @@ internal class AnalysisScope
public bool IsTreeAnalysis => FilterTreeOpt != null;
public AnalysisScope(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, bool concurrentAnalysis, bool categorizeDiagnostics)
: this (compilation.SyntaxTrees, analyzers, filterTreeOpt: null, filterSpanOpt: null, isSyntaxOnlyTreeAnalysis: false, concurrentAnalysis: concurrentAnalysis, categorizeDiagnostics: categorizeDiagnostics)
{
SyntaxTrees = compilation.SyntaxTrees;
Analyzers = analyzers;
ConcurrentAnalysis = concurrentAnalysis;
CategorizeDiagnostics = categorizeDiagnostics;
FilterTreeOpt = null;
FilterSpanOpt = null;
IsSyntaxOnlyTreeAnalysis = false;
}
public AnalysisScope(ImmutableArray<DiagnosticAnalyzer> analyzers, SyntaxTree filterTree, TextSpan? filterSpan, bool syntaxAnalysis, bool concurrentAnalysis, bool categorizeDiagnostics)
: this (SpecializedCollections.SingletonEnumerable(filterTree), analyzers, filterTree, filterSpan, syntaxAnalysis, concurrentAnalysis, categorizeDiagnostics)
{
Debug.Assert(filterTree != null);
}
SyntaxTrees = SpecializedCollections.SingletonEnumerable(filterTree);
private AnalysisScope(IEnumerable<SyntaxTree> trees, ImmutableArray<DiagnosticAnalyzer> analyzers, SyntaxTree filterTreeOpt, TextSpan? filterSpanOpt, bool isSyntaxOnlyTreeAnalysis, bool concurrentAnalysis, bool categorizeDiagnostics)
{
SyntaxTrees = trees;
Analyzers = analyzers;
FilterTreeOpt = filterTree;
FilterSpanOpt = filterSpan;
IsSyntaxOnlyTreeAnalysis = syntaxAnalysis;
FilterTreeOpt = filterTreeOpt;
FilterSpanOpt = filterSpanOpt;
IsSyntaxOnlyTreeAnalysis = isSyntaxOnlyTreeAnalysis;
ConcurrentAnalysis = concurrentAnalysis;
CategorizeDiagnostics = categorizeDiagnostics;
}
public AnalysisScope WithAnalyzers(ImmutableArray<DiagnosticAnalyzer> analyzers)
{
return new AnalysisScope(SyntaxTrees, analyzers, FilterTreeOpt, FilterSpanOpt, IsSyntaxOnlyTreeAnalysis, ConcurrentAnalysis, CategorizeDiagnostics);
}
public static bool ShouldSkipSymbolAnalysis(SymbolDeclaredCompilationEvent symbolEvent)
{
// Skip symbol actions for implicitly declared symbols and non-source symbols.
......
......@@ -431,12 +431,18 @@ private async Task EnsureAnalyzerActionCountsInitializedAsync(AnalyzerDriver dri
}
}
internal async Task<AnalyzerActionCounts> GetAnalyzerActionCountsAsync(DiagnosticAnalyzer analyzer, AnalyzerDriver driver, CancellationToken cancellationToken)
internal async Task<AnalyzerActionCounts> GetOrComputeAnalyzerActionCountsAsync(DiagnosticAnalyzer analyzer, AnalyzerDriver driver, CancellationToken cancellationToken)
{
await EnsureAnalyzerActionCountsInitializedAsync(driver, cancellationToken).ConfigureAwait(false);
return _lazyAnalyzerActionCountsMap[analyzer];
}
internal AnalyzerActionCounts GetAnalyzerActionCounts(DiagnosticAnalyzer analyzer)
{
Debug.Assert(_lazyAnalyzerActionCountsMap != null);
return _lazyAnalyzerActionCountsMap[analyzer];
}
private static bool HasActionsForEvent(CompilationEvent compilationEvent, AnalyzerActionCounts actionCounts)
{
if (compilationEvent is CompilationStartedEvent)
......
......@@ -1186,8 +1186,8 @@ private static bool IsCompilerAnalyzer(DiagnosticAnalyzer analyzer)
public void Dispose()
{
this.CompilationEventQueue.TryComplete();
this.DiagnosticQueue.TryComplete();
this.CompilationEventQueue?.TryComplete();
this.DiagnosticQueue?.TryComplete();
_queueRegistration.Dispose();
}
}
......
Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.RegisterOperationAction(System.Action<Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext> action, params Microsoft.CodeAnalysis.OperationKind[] operationKinds) -> void
Microsoft.CodeAnalysis.Diagnostics.AnalysisResult
Microsoft.CodeAnalysis.Diagnostics.AnalysisResult.AnalyzerTelemetryInfo.get -> System.Collections.Immutable.ImmutableDictionary<Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, Microsoft.CodeAnalysis.Diagnostics.Telemetry.AnalyzerTelemetryInfo>
Microsoft.CodeAnalysis.Diagnostics.AnalysisResult.Analyzers.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer>
Microsoft.CodeAnalysis.Diagnostics.AnalysisResult.CompilationDiagnostics.get -> System.Collections.Immutable.ImmutableDictionary<Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic>>
Microsoft.CodeAnalysis.Diagnostics.AnalysisResult.GetAllDiagnostics() -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic>
Microsoft.CodeAnalysis.Diagnostics.AnalysisResult.GetAllDiagnostics(Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer analyzer) -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic>
Microsoft.CodeAnalysis.Diagnostics.AnalysisResult.SemanticDiagnostics.get -> System.Collections.Immutable.ImmutableDictionary<Microsoft.CodeAnalysis.SyntaxTree, System.Collections.Immutable.ImmutableDictionary<Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic>>>
Microsoft.CodeAnalysis.Diagnostics.AnalysisResult.SyntaxDiagnostics.get -> System.Collections.Immutable.ImmutableDictionary<Microsoft.CodeAnalysis.SyntaxTree, System.Collections.Immutable.ImmutableDictionary<Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostic>>>
Microsoft.CodeAnalysis.Diagnostics.CompilationStartAnalysisContext.RegisterOperationAction(System.Action<Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext> action, params Microsoft.CodeAnalysis.OperationKind[] operationKinds) -> void
Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers.GetAnalysisResultAsync(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer> analyzers, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Diagnostics.AnalysisResult>
Microsoft.CodeAnalysis.Diagnostics.CompilationWithAnalyzers.GetAnalysisResultAsync(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Diagnostics.AnalysisResult>
Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext
Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext.CancellationToken.get -> System.Threading.CancellationToken
Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext.Compilation.get -> Microsoft.CodeAnalysis.Compilation
......
......@@ -1644,9 +1644,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
If _lazyCompilationUnitCompletedTrees.Count = SyntaxTrees.Length Then
' if that was the last tree, signal the end of compilation
EventQueue.TryEnqueue(New CompilationCompletedEvent(Me))
EventQueue.PromiseNotToEnqueue()
EventQueue.TryComplete()
CompleteCompilationEventQueue_NoLock()
End If
End If
End SyncLock
......@@ -1925,6 +1923,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
builder.AddRange(GetBoundReferenceManager().Diagnostics)
builder.AddRange(SourceAssembly.GetAllDeclarationErrors(cancellationToken))
builder.AddRange(GetClsComplianceDiagnostics(cancellationToken))
If EventQueue IsNot Nothing AndAlso SyntaxTrees.Length = 0 Then
EnsureCompilationEventQueueCompleted()
End If
End If
' Add method body compilation errors.
......
......@@ -255,5 +255,15 @@ End Namespace
Return True
End Function
<Fact>
Public Sub TestEventQueueCompletionForEmptyCompilation()
Dim compilation = CreateCompilationWithMscorlib45(SpecializedCollections.EmptyEnumerable(Of SyntaxTree)()).WithEventQueue(New AsyncQueue(Of CompilationEvent)())
' Force complete compilation event queue
Dim unused = compilation.GetDiagnostics()
Assert.True(compilation.EventQueue.IsCompleted)
End Sub
End Class
End Namespace
\ No newline at end of file
......@@ -336,7 +336,8 @@ private async Task GetCompilationDiagnosticsAsync(DiagnosticAnalyzer analyzer, L
var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false);
var compilationWithAnalyzers = GetCompilationWithAnalyzers(compilation);
var compilationDiagnostics = await compilationWithAnalyzers.GetAnalyzerCompilationDiagnosticsAsync(ImmutableArray.Create(analyzer), _cancellationToken).ConfigureAwait(false);
var analysisResult = await compilationWithAnalyzers.GetAnalysisResultAsync(ImmutableArray.Create(analyzer), _cancellationToken).ConfigureAwait(false);
var compilationDiagnostics = analysisResult.CompilationDiagnostics.Count == 1 ? analysisResult.CompilationDiagnostics[analyzer] : ImmutableArray<Diagnostic>.Empty;
await UpdateAnalyzerTelemetryDataAsync(analyzer, compilationWithAnalyzers).ConfigureAwait(false);
diagnostics.AddRange(compilationDiagnostics);
}
......
......@@ -412,26 +412,42 @@ private async Task AnalyzeProjectAsync(Project project, CancellationToken cancel
return;
}
// PERF: Ensure that we explicitly ignore the skipped analyzers while creating the analyzer driver, otherwise we might end up running hidden analyzers on closed files.
var stateSets = _stateManager.GetOrUpdateStateSets(project).ToImmutableArray();
var skipAnalyzersMap = new Dictionary<DiagnosticAnalyzer, bool>(stateSets.Length);
var shouldRunAnalyzersMap = new Dictionary<DiagnosticAnalyzer, bool>(stateSets.Length);
var analyzersBuilder = ImmutableArray.CreateBuilder<DiagnosticAnalyzer>(stateSets.Length);
foreach (var stateSet in stateSets)
{
var skip = await SkipRunningAnalyzerAsync(project, stateSet.Analyzer, openedDocument: false, skipClosedFileCheck: true, cancellationToken: cancellationToken).ConfigureAwait(false);
skipAnalyzersMap.Add(stateSet.Analyzer, skip);
var shouldRun = !skip && ShouldRunAnalyzerForStateType(stateSet.Analyzer, StateType.Project, diagnosticIds: null);
shouldRunAnalyzersMap.Add(stateSet.Analyzer, shouldRun);
if (shouldRun)
{
analyzersBuilder.Add(stateSet.Analyzer);
}
}
var analyzerDriver = new DiagnosticAnalyzerDriver(project, this, analyzersBuilder.ToImmutable(), ConcurrentAnalysis, ReportSuppressedDiagnostics, cancellationToken);
var projectTextVersion = await project.GetLatestDocumentVersionAsync(cancellationToken).ConfigureAwait(false);
var semanticVersion = await project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false);
var projectVersion = await project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false);
var versions = new VersionArgument(projectTextVersion, semanticVersion, projectVersion);
var stateSets = _stateManager.GetOrUpdateStateSets(project);
var analyzers = stateSets.Select(s => s.Analyzer);
var analyzerDriver = new DiagnosticAnalyzerDriver(project, this, analyzers, ConcurrentAnalysis, ReportSuppressedDiagnostics, cancellationToken);
foreach (var stateSet in stateSets)
{
// Compilation actions can report diagnostics on open files, so we skipClosedFileChecks.
if (await SkipRunningAnalyzerAsync(project, stateSet.Analyzer, openedDocument: false, skipClosedFileCheck: true, cancellationToken: cancellationToken).ConfigureAwait(false))
if (skipAnalyzersMap[stateSet.Analyzer])
{
await ClearExistingDiagnostics(project, stateSet, cancellationToken).ConfigureAwait(false);
continue;
}
if (ShouldRunAnalyzerForStateType(stateSet.Analyzer, StateType.Project, diagnosticIds: null))
if (shouldRunAnalyzersMap[stateSet.Analyzer])
{
var data = await _executor.GetProjectAnalysisDataAsync(analyzerDriver, stateSet, versions).ConfigureAwait(false);
if (data.FromCache)
......
......@@ -19,41 +19,45 @@ internal static class CompilerDiagnosticExecutor
{
var version = await DiagnosticIncrementalAnalyzer.GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false);
// Run all analyzers at once.
// REVIEW: why there are 2 different cancellation token? one that I can give to constructor and one I can give in to each method?
// REVIEW: we drop all those allocations for the diagnostics returned. can we avoid this?
await analyzerDriver.GetAnalyzerDiagnosticsAsync(cancellationToken).ConfigureAwait(false);
// this is wierd, but now we iterate through each analyzer for each tree to get cached result.
// REVIEW: no better way to do this?
var noSpanFilter = default(TextSpan?);
// PERF: Run all analyzers at once using the new GetAnalysisResultAsync API.
var analysisResult = await analyzerDriver.GetAnalysisResultAsync(cancellationToken).ConfigureAwait(false);
var analyzers = analyzerDriver.Analyzers;
var compilation = analyzerDriver.Compilation;
var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, AnalysisResult>();
foreach (var analyzer in analyzers)
{
var result = new Builder(project, version);
// REVIEW: more unnecessary allocations just to get diagnostics per analyzer
var oneAnalyzers = ImmutableArray.Create(analyzer);
foreach (var tree in compilation.SyntaxTrees)
{
var model = compilation.GetSemanticModel(tree);
var syntax = await analyzerDriver.GetAnalyzerSyntaxDiagnosticsAsync(tree, oneAnalyzers, cancellationToken).ConfigureAwait(false);
Contract.Requires(syntax.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(syntax, analyzerDriver.Compilation).Count());
result.AddSyntaxDiagnostics(tree, syntax);
ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>> syntaxDiagnosticsByAnalyzer;
ImmutableArray<Diagnostic> syntaxDiagnostics;
if (analysisResult.SyntaxDiagnostics.TryGetValue(tree, out syntaxDiagnosticsByAnalyzer) &&
syntaxDiagnosticsByAnalyzer.TryGetValue(analyzer, out syntaxDiagnostics))
{
Contract.Requires(syntaxDiagnostics.Length == CompilationWithAnalyzers.GetEffectiveDiagnostics(syntaxDiagnostics, analyzerDriver.Compilation).Count());
result.AddSyntaxDiagnostics(tree, syntaxDiagnostics);
}
var semantic = await analyzerDriver.GetAnalyzerSemanticDiagnosticsAsync(model, noSpanFilter, oneAnalyzers, cancellationToken).ConfigureAwait(false);
Contract.Requires(semantic.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(semantic, analyzerDriver.Compilation).Count());
result.AddSemanticDiagnostics(tree, semantic);
ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<Diagnostic>> semanticDiagnosticsByAnalyzer;
ImmutableArray<Diagnostic> semanticDiagnostics;
if (analysisResult.SemanticDiagnostics.TryGetValue(tree, out semanticDiagnosticsByAnalyzer) &&
semanticDiagnosticsByAnalyzer.TryGetValue(analyzer, out semanticDiagnostics))
{
Contract.Requires(semanticDiagnostics.Length == CompilationWithAnalyzers.GetEffectiveDiagnostics(semanticDiagnostics, analyzerDriver.Compilation).Count());
result.AddSemanticDiagnostics(tree, semanticDiagnostics);
}
}
var rest = await analyzerDriver.GetAnalyzerCompilationDiagnosticsAsync(oneAnalyzers, cancellationToken).ConfigureAwait(false);
Contract.Requires(rest.Count() == CompilationWithAnalyzers.GetEffectiveDiagnostics(rest, analyzerDriver.Compilation).Count());
result.AddCompilationDiagnostics(rest);
ImmutableArray<Diagnostic> compilationDiagnostics;
if (analysisResult.CompilationDiagnostics.TryGetValue(analyzer, out compilationDiagnostics))
{
Contract.Requires(compilationDiagnostics.Length == CompilationWithAnalyzers.GetEffectiveDiagnostics(compilationDiagnostics, analyzerDriver.Compilation).Count());
result.AddCompilationDiagnostics(compilationDiagnostics);
}
builder.Add(analyzer, result.ToResult());
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册