提交 101cf358 编写于 作者: M Manish Vasani

Add support for registering an analyzer for concurrent execution of its actions.

By default, analyzer driver never makes concurrent callbacks into a single analyzer instance. This change adds a new public API 'AnalysisContext.RegisterConcurrentExecution' to enable thread-safe analyzers to receive concurrent callbacks, which should theoretically improve performance of such analyzers for concurrent builds.

Fixes #6737
上级 8d758155
......@@ -4,11 +4,11 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.Serialization;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.CSharp;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
......@@ -1133,5 +1133,75 @@ class D
Diagnostic(CSharpCodeBlockObjectCreationAnalyzer.DiagnosticDescriptor.Id, "new C()").WithLocation(5, 18)
});
}
private static Compilation GetCompilationWithConcurrentBuildEnabled(string source)
{
var compilation = CreateCompilationWithMscorlib45(source);
// NOTE: We set the concurrentBuild option to true after creating the compilation as CreateCompilationWithMscorlib
// always sets concurrentBuild to false if debugger is attached, even if we had passed options with concurrentBuild = true to that API.
// We want the tests using GetCompilationWithConcurrentBuildEnabled to have identical behavior with and without debugger being attached.
var options = compilation.Options.WithConcurrentBuild(true);
return compilation.WithOptions(options);
}
[Fact, WorkItem(6737, "https://github.com/dotnet/roslyn/issues/6737")]
public void TestNonConcurrentAnalyzer()
{
var source = string.Empty;
var typeCount = 100;
for (int i = 1; i <= typeCount; i++)
{
var typeName = $"C{i}";
source = source + $"\r\nclass {typeName} {{ }}";
}
var analyzers = new DiagnosticAnalyzer[] { new NonConcurrentAnalyzer() };
// Verify no diagnostics.
var compilation = GetCompilationWithConcurrentBuildEnabled(source);
compilation.VerifyDiagnostics();
compilation.VerifyAnalyzerDiagnostics(analyzers);
}
[Fact, WorkItem(6737, "https://github.com/dotnet/roslyn/issues/6737")]
public void TestConcurrentAnalyzer()
{
if (Environment.ProcessorCount <= 1)
{
// Don't test for non-concurrent environment.
return;
}
var source = string.Empty;
var typeCount = 100;
var typeNames = new string[typeCount];
for (int i = 1; i <= typeCount; i++)
{
var typeName = $"C{i}";
typeNames[i - 1] = typeName;
source = source + $"\r\nclass {typeName} {{ }}";
}
var compilation = GetCompilationWithConcurrentBuildEnabled(source);
compilation.VerifyDiagnostics();
// Verify analyzer diagnostics for Concurrent analyzer only.
var analyzers = new DiagnosticAnalyzer[] { new ConcurrentAnalyzer(typeNames) };
var expected = new DiagnosticDescription[typeCount];
for (int i = 0; i < typeCount; i++)
{
var typeName = $"C{i + 1}";
expected[i] = Diagnostic(ConcurrentAnalyzer.Descriptor.Id, typeName)
.WithArguments(typeName)
.WithLocation(i + 2, 7);
}
compilation.VerifyAnalyzerDiagnostics(analyzers, expected: expected);
// Verify analyzer diagnostics for Concurrent and NonConcurrent analyzer together (latter reports diagnostics only for error cases).
analyzers = new DiagnosticAnalyzer[] { new ConcurrentAnalyzer(typeNames), new NonConcurrentAnalyzer() };
compilation.VerifyAnalyzerDiagnostics(analyzers, expected: expected);
}
}
}
......@@ -38,6 +38,11 @@ internal abstract partial class AnalyzerDriver : IDisposable
private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CompilationAnalyzerAction>> _compilationActionsMap;
private ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CompilationAnalyzerAction>> _compilationEndActionsMap;
/// <summary>
/// Map from non-concurrent analyzers to the gate guarding callback into the analyzer.
/// </summary>
private ImmutableDictionary<DiagnosticAnalyzer, SemaphoreSlim> _analyzerGateMap = ImmutableDictionary<DiagnosticAnalyzer, SemaphoreSlim>.Empty;
/// <summary>
/// Driver task which initializes all analyzers.
/// This task is initialized and executed only once at start of analysis.
......@@ -98,7 +103,8 @@ private void Initialize(AnalyzerExecutor analyzerExecutor, DiagnosticQueue diagn
var analyzerActionsTask = GetAnalyzerActionsAsync(analyzers, analyzerManager, analyzerExecutor);
_initializeTask = analyzerActionsTask.ContinueWith(t =>
{
this.analyzerActions = t.Result;
this.analyzerActions = t.Result.Item1;
this._analyzerGateMap = t.Result.Item2;
_symbolActionsByKind = MakeSymbolActionsByKind();
_semanticModelActionsMap = MakeSemanticModelActionsByAnalyzer();
_syntaxTreeActionsMap = MakeSyntaxTreeActionsByAnalyzer();
......@@ -158,9 +164,6 @@ private void Initialize(AnalyzerExecutor analyzerExecutor, DiagnosticQueue diagn
newOnAnalyzerException = (ex, analyzer, diagnostic) => addDiagnostic(diagnostic);
}
// Assume all analyzers are non-thread safe.
var singleThreadedAnalyzerToGateMap = ImmutableDictionary.CreateRange(analyzers.Select(a => KeyValuePair.Create(a, new SemaphoreSlim(initialCount: 1))));
if (analysisOptions.LogAnalyzerExecutionTime)
{
// If we are reporting detailed analyzer performance numbers, then do a dummy invocation of Compilation.GetTypeByMetadataName API upfront.
......@@ -168,13 +171,26 @@ private void Initialize(AnalyzerExecutor analyzerExecutor, DiagnosticQueue diagn
var unused = compilation.GetTypeByMetadataName("System.Object");
}
Func<DiagnosticAnalyzer, SemaphoreSlim> getAnalyzerGate = analyzer => singleThreadedAnalyzerToGateMap[analyzer];
var analyzerExecutor = AnalyzerExecutor.Create(compilation, analysisOptions.Options ?? AnalyzerOptions.Empty, addDiagnostic, newOnAnalyzerException, IsCompilerAnalyzer,
analyzerManager, getAnalyzerGate, analysisOptions.LogAnalyzerExecutionTime, addLocalDiagnosticOpt, addNonLocalDiagnosticOpt, cancellationToken);
analyzerManager, GetAnalyzerGate, analysisOptions.LogAnalyzerExecutionTime, addLocalDiagnosticOpt, addNonLocalDiagnosticOpt, cancellationToken);
Initialize(analyzerExecutor, diagnosticQueue, cancellationToken);
}
private SemaphoreSlim GetAnalyzerGate(DiagnosticAnalyzer analyzer)
{
SemaphoreSlim gate;
if (_analyzerGateMap.TryGetValue(analyzer, out gate))
{
// Non-concurrent analyzer, needs all the callbacks guarded by a gate.
Debug.Assert(gate != null);
return gate;
}
// Concurrent analyzer.
return null;
}
/// <summary>
/// Attaches a pre-populated event queue to the driver and processes all events in the queue.
/// </summary>
......@@ -396,7 +412,7 @@ public async Task<ImmutableArray<Diagnostic>> GetDiagnosticsAsync(Compilation co
if (reportSuppressedDiagnostics || !d.IsSuppressed)
{
allDiagnostics.Add(d);
}
}
}
return allDiagnostics.ToReadOnlyAndFree();
......@@ -530,7 +546,7 @@ private async Task ProcessCompilationEventsAsync(AnalysisScope analysisScope, An
var workerTasks = new Task<CompilationCompletedEvent>[workerCount];
for (int i = 0; i < workerCount; i++)
{
workerTasks[i] = ProcessCompilationEventsCoreAsync(analysisScope, analysisStateOpt, prePopulatedEventQueue, cancellationToken);
workerTasks[i] = Task.Run(async() => await ProcessCompilationEventsCoreAsync(analysisScope, analysisStateOpt, prePopulatedEventQueue, cancellationToken).ConfigureAwait(false));
}
cancellationToken.ThrowIfCancellationRequested();
......@@ -635,7 +651,7 @@ private async Task ProcessEventAsync(CompilationEvent e, AnalysisScope analysisS
if (analysisStateOpt != null)
{
await analysisStateOpt.OnCompilationEventProcessedAsync(e, analysisScope, cancellationToken).ConfigureAwait(false);
}
}
}
private async Task ProcessEventCoreAsync(CompilationEvent e, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken)
......@@ -845,35 +861,66 @@ private static Diagnostic GetFilteredDiagnostic(Diagnostic diagnostic, Compilati
return compilation.Options.FilterDiagnostic(diagnostic);
}
private static Task<AnalyzerActions> GetAnalyzerActionsAsync(
private static Task<Tuple<AnalyzerActions, ImmutableDictionary<DiagnosticAnalyzer, SemaphoreSlim>>> GetAnalyzerActionsAsync(
ImmutableArray<DiagnosticAnalyzer> analyzers,
AnalyzerManager analyzerManager,
AnalyzerExecutor analyzerExecutor)
{
return Task.Run(async () =>
{
AnalyzerActions allAnalyzerActions = new AnalyzerActions();
var allAnalyzerActions = new AnalyzerActions();
var concurrentAnalyzers = new HashSet<DiagnosticAnalyzer>();
foreach (var analyzer in analyzers)
{
if (!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerExecutor.Compilation.Options, analyzerManager, analyzerExecutor))
{
var analyzerActions = await analyzerManager.GetAnalyzerActionsAsync(analyzer, analyzerExecutor).ConfigureAwait(false);
var tuple = await analyzerManager.GetAnalyzerActionsAsync(analyzer, analyzerExecutor).ConfigureAwait(false);
var analyzerActions = tuple.Item1;
if (analyzerActions != null)
{
allAnalyzerActions = allAnalyzerActions.Append(analyzerActions);
var isConcurrentAnalyzer = tuple.Item2;
if (isConcurrentAnalyzer)
{
concurrentAnalyzers.Add(analyzer);
}
}
}
}
return allAnalyzerActions;
var analyzerGateMap = GetAnalyzerGateMap(analyzers, concurrentAnalyzers);
return Tuple.Create(allAnalyzerActions, analyzerGateMap);
}, analyzerExecutor.CancellationToken);
}
private static ImmutableDictionary<DiagnosticAnalyzer, SemaphoreSlim> GetAnalyzerGateMap(ImmutableArray<DiagnosticAnalyzer> allAnalyzers, HashSet<DiagnosticAnalyzer> concurrentAnalyzers)
{
// Non-concurrent analyzers need their action callbacks from the analyzer drive to be guarded by a gate.
if (allAnalyzers.Length == concurrentAnalyzers.Count)
{
// All concurrent analyzers, so we need no gates.
return ImmutableDictionary<DiagnosticAnalyzer, SemaphoreSlim>.Empty;
}
var builder = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, SemaphoreSlim>();
foreach (var analyzer in allAnalyzers)
{
if (!concurrentAnalyzers.Contains(analyzer))
{
var gate = new SemaphoreSlim(initialCount: 1);
builder.Add(analyzer, gate);
}
}
return builder.ToImmutable();
}
internal async Task<AnalyzerActionCounts> GetAnalyzerActionCountsAsync(DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
{
var executor = analyzerExecutor.WithCancellationToken(cancellationToken);
var analyzerActions = await analyzerManager.GetAnalyzerActionsAsync(analyzer, executor).ConfigureAwait(false);
return AnalyzerActionCounts.Create(analyzerActions);
return AnalyzerActionCounts.Create(analyzerActions.Item1);
}
/// <summary>
......@@ -991,14 +1038,14 @@ internal AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, Func<Synta
if (analyzerAndActions.Any())
{
actionsByKind = AnalyzerExecutor.GetOperationActionsByKind(analyzerAndActions);
}
}
else
{
actionsByKind = ImmutableDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>>.Empty;
}
builder.Add(analyzerAndActions.Key, actionsByKind);
}
}
analyzerActionsByKind = builder.ToImmutable();
}
......
......@@ -132,20 +132,27 @@ internal partial class AnalyzerManager
}
/// <summary>
/// Get all the analyzer actions to execute for the given analyzer against a given compilation.
/// Get tuple with:
/// (a) All the analyzer actions to execute for the given analyzer against a given compilation and
/// (b) A flag indicating if these actions can be executed concurrently.
/// The returned actions include the actions registered during <see cref="DiagnosticAnalyzer.Initialize(AnalysisContext)"/> method as well as
/// the actions registered during <see cref="CompilationStartAnalyzerAction"/> for the given compilation.
/// </summary>
public async Task<AnalyzerActions> GetAnalyzerActionsAsync(DiagnosticAnalyzer analyzer, AnalyzerExecutor analyzerExecutor)
public async Task<Tuple<AnalyzerActions, bool>> GetAnalyzerActionsAsync(DiagnosticAnalyzer analyzer, AnalyzerExecutor analyzerExecutor)
{
var sessionScope = await GetSessionAnalysisScopeAsync(analyzer, analyzerExecutor).ConfigureAwait(false);
AnalyzerActions allActions;
if (sessionScope.CompilationStartActions.Length > 0 && analyzerExecutor.Compilation != null)
{
var compilationScope = await GetCompilationAnalysisScopeAsync(analyzer, sessionScope, analyzerExecutor).ConfigureAwait(false);
return compilationScope.GetAnalyzerActions(analyzer);
allActions = compilationScope.GetAnalyzerActions(analyzer);
}
else
{
allActions = sessionScope.GetAnalyzerActions(analyzer);
}
return sessionScope.GetAnalyzerActions(analyzer);
return Tuple.Create(allActions, sessionScope.IsConcurrentAnalyzer(analyzer));
}
/// <summary>
......
......@@ -162,6 +162,18 @@ public void RegisterOperationAction(Action<OperationAnalysisContext> action, par
/// <param name="action">Action to be executed at completion of semantic analysis of an <see cref="IOperation"/>.</param>
/// <param name="operationKinds">Action will be executed only if an <see cref="IOperation"/>'s Kind matches one of the operation kind values.</param>
public abstract void RegisterOperationAction(Action<OperationAnalysisContext> action, ImmutableArray<OperationKind> operationKinds);
/// <summary>
/// Register for concurrent execution of analyzer actions registered by this analyzer.
/// An analyzer that registers for concurrent execution should be more performant then a non-concurrent analyzer.
/// However, such analyzers must ensure that its actions are implemented in a thread-safe manner.
/// </summary>
/// <remarks>
/// Even when an analyzer registers for concurrent execution, certain related actions are *never* executed concurrently.
/// For example, end actions registered on any analysis unit (compilation, code block, operation block, etc.) are by definition semantically dependent on analysis from non-end actions registered on the same analysis unit.
/// Hence, end actions are never executed concurrently with non-end actions operating on the same analysis unit.
/// </remarks>
public abstract void RegisterConcurrentExecution();
}
/// <summary>
......
......@@ -74,19 +74,24 @@ public override void RegisterSyntaxNodeAction<TLanguageKindEnum>(Action<SyntaxNo
public override void RegisterOperationAction(Action<OperationAnalysisContext> action, ImmutableArray<OperationKind> operationKinds)
{
DiagnosticAnalysisContextHelpers.VerifyArguments(action, operationKinds);
_scope.RegisterOperationAction(this._analyzer, action, operationKinds);
_scope.RegisterOperationAction(_analyzer, action, operationKinds);
}
public override void RegisterOperationBlockStartAction(Action<OperationBlockStartAnalysisContext> action)
{
DiagnosticAnalysisContextHelpers.VerifyArguments(action);
_scope.RegisterOperationBlockStartAction(this._analyzer, action);
_scope.RegisterOperationBlockStartAction(_analyzer, action);
}
public override void RegisterOperationBlockAction(Action<OperationBlockAnalysisContext> action)
{
DiagnosticAnalysisContextHelpers.VerifyArguments(action);
_scope.RegisterOperationBlockAction(this._analyzer, action);
_scope.RegisterOperationBlockAction(_analyzer, action);
}
public override void RegisterConcurrentExecution()
{
_scope.RegisterConcurrentExecution(_analyzer);
}
}
......@@ -226,18 +231,29 @@ public override void RegisterOperationAction(Action<OperationAnalysisContext> ac
internal sealed class HostSessionStartAnalysisScope : HostAnalysisScope
{
private ImmutableArray<CompilationStartAnalyzerAction> _compilationStartActions = ImmutableArray<CompilationStartAnalyzerAction>.Empty;
private ImmutableHashSet<DiagnosticAnalyzer> _concurrentAnalyzers = ImmutableHashSet<DiagnosticAnalyzer>.Empty;
public ImmutableArray<CompilationStartAnalyzerAction> CompilationStartActions
{
get { return _compilationStartActions; }
}
public bool IsConcurrentAnalyzer(DiagnosticAnalyzer analyzer)
{
return _concurrentAnalyzers.Contains(analyzer);
}
public void RegisterCompilationStartAction(DiagnosticAnalyzer analyzer, Action<CompilationStartAnalysisContext> action)
{
CompilationStartAnalyzerAction analyzerAction = new CompilationStartAnalyzerAction(action, analyzer);
this.GetOrCreateAnalyzerActions(analyzer).AddCompilationStartAction(analyzerAction);
_compilationStartActions = _compilationStartActions.Add(analyzerAction);
}
public void RegisterConcurrentExecution(DiagnosticAnalyzer analyzer)
{
_concurrentAnalyzers = _concurrentAnalyzers.Add(analyzer);
}
}
/// <summary>
......@@ -567,6 +583,7 @@ public void RegisterOperationBlockAction(DiagnosticAnalyzer analyzer, Action<Ope
this.GetOrCreateAnalyzerActions(analyzer).AddOperationBlockAction(analyzerAction);
_operationBlockActions = _operationBlockActions.Add(analyzerAction);
}
public void RegisterOperationAction(DiagnosticAnalyzer analyzer, Action<OperationAnalysisContext> action, ImmutableArray<OperationKind> operationKinds)
{
OperationAnalyzerAction analyzerAction = new OperationAnalyzerAction(action, operationKinds, analyzer);
......@@ -766,7 +783,7 @@ internal void AddOperationBlockEndAction(OperationBlockAnalyzerAction action)
internal void AddOperationAction(OperationAnalyzerAction action)
{
this._operationActions = this._operationActions.Add(action);
_operationActions = _operationActions.Add(action);
}
/// <summary>
......
......@@ -76,6 +76,7 @@ virtual Microsoft.CodeAnalysis.MetadataReferenceResolver.ResolveMissingAssembly(
virtual Microsoft.CodeAnalysis.SourceReferenceResolver.ReadText(string resolvedPath) -> Microsoft.CodeAnalysis.Text.SourceText
Microsoft.CodeAnalysis.SemanticModel.GetOperation(Microsoft.CodeAnalysis.SyntaxNode node, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.Semantics.IOperation
abstract Microsoft.CodeAnalysis.SemanticModel.GetOperationCore(Microsoft.CodeAnalysis.SyntaxNode node, System.Threading.CancellationToken cancellationToken) -> Microsoft.CodeAnalysis.Semantics.IOperation
abstract Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.RegisterConcurrentExecution() -> void
Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.RegisterOperationAction(System.Action<Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext> action, params Microsoft.CodeAnalysis.Semantics.OperationKind[] operationKinds) -> void
Microsoft.CodeAnalysis.Diagnostics.CompilationStartAnalysisContext.RegisterOperationAction(System.Action<Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext> action, params Microsoft.CodeAnalysis.Semantics.OperationKind[] operationKinds) -> void
abstract Microsoft.CodeAnalysis.Diagnostics.AnalysisContext.RegisterOperationAction(System.Action<Microsoft.CodeAnalysis.Diagnostics.OperationAnalysisContext> action, System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Semantics.OperationKind> operationKinds) -> void
......
......@@ -4,10 +4,11 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslyn.Utilities;
using System.IO;
using Xunit;
namespace Microsoft.CodeAnalysis
{
......@@ -479,5 +480,120 @@ private void OnCompilation(CompilationAnalysisContext context)
}
}
}
/// <summary>
/// This analyzer is intended to be used only when concurrent execution is enabled for analyzers.
/// This analyzer will deadlock if the driver runs analyzers on a single thread OR takes a lock around callbacks into this analyzer to prevent concurrent analyzer execution
/// Former indicates a bug in the test using this analyzer and the latter indicates a bug in the analyzer driver.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public class ConcurrentAnalyzer : DiagnosticAnalyzer
{
private readonly ImmutableHashSet<string> _symbolNames;
private int _token;
public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
"ConcurrentAnalyzerId",
"Title",
"ConcurrentAnalyzerMessage for symbol '{0}'",
"Category",
DiagnosticSeverity.Warning,
true);
public ConcurrentAnalyzer(IEnumerable<string> symbolNames)
{
Assert.True(Environment.ProcessorCount > 1, "This analyzer is intended to be used only in a concurrent environment.");
_symbolNames = symbolNames.ToImmutableHashSet();
_token = 0;
}
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
{
context.RegisterCompilationStartAction(this.OnCompilationStart);
// Register concurrent action callbacks on analyzer.
context.RegisterConcurrentExecution();
}
private void OnCompilationStart(CompilationStartAnalysisContext context)
{
Assert.True(context.Compilation.Options.ConcurrentBuild, "This analyzer is intended to be used only when concurrent build is enabled.");
var pendingSymbols = new ConcurrentSet<INamedTypeSymbol>();
foreach (var type in context.Compilation.GlobalNamespace.GetTypeMembers())
{
if (_symbolNames.Contains(type.Name))
{
pendingSymbols.Add(type);
}
}
context.RegisterSymbolAction(symbolContext =>
{
if (!pendingSymbols.Remove((INamedTypeSymbol)symbolContext.Symbol))
{
return;
}
var myToken = Interlocked.Increment(ref _token);
if (myToken == 1)
{
// Wait for all symbol callbacks to execute.
// This analyzer will deadlock if the driver doesn't attempt concurrent callbacks.
while (pendingSymbols.Any())
{
Thread.Sleep(10);
}
}
// ok, now report diagnostic on the symbol.
var diagnostic = Diagnostic.Create(Descriptor, symbolContext.Symbol.Locations[0], symbolContext.Symbol.Name);
symbolContext.ReportDiagnostic(diagnostic);
}, SymbolKind.NamedType);
}
}
/// <summary>
/// This analyzer will report diagnostics only if it receives any concurrent action callbacks, which would be a
/// bug in the analyzer driver as this analyzer doesn't invoke <see cref="AnalysisContext.RegisterConcurrentExecution"/>.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
public class NonConcurrentAnalyzer : DiagnosticAnalyzer
{
public static readonly DiagnosticDescriptor Descriptor = new DiagnosticDescriptor(
"NonConcurrentAnalyzerId",
"Title",
"Analyzer driver made concurrent action callbacks, when analyzer didn't register for concurrent execution",
"Category",
DiagnosticSeverity.Warning,
true);
private const int registeredActionCounts = 1000;
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor);
public override void Initialize(AnalysisContext context)
{
SemaphoreSlim gate = new SemaphoreSlim(initialCount: registeredActionCounts);
for (var i = 0; i < registeredActionCounts; i++)
{
context.RegisterSymbolAction(symbolContext =>
{
using (gate.DisposableWait(symbolContext.CancellationToken))
{
ReportDiagnosticIfActionInvokedConcurrently(gate, symbolContext);
}
}, SymbolKind.NamedType);
}
}
private void ReportDiagnosticIfActionInvokedConcurrently(SemaphoreSlim gate, SymbolAnalysisContext symbolContext)
{
if (gate.CurrentCount != registeredActionCounts - 1)
{
var diagnostic = Diagnostic.Create(Descriptor, symbolContext.Symbol.Locations[0]);
symbolContext.ReportDiagnostic(diagnostic);
}
}
}
}
}
\ No newline at end of file
......@@ -233,6 +233,468 @@
}
}
},
".NETFramework,Version=v4.5/osx.10.10": {
"Microsoft.CodeAnalysis.Test.Resources.Proprietary/1.2.0-beta-20151016-11": {
"compile": {
"lib/net45/Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll": {}
},
"runtime": {
"lib/net45/Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll": {}
}
},
"Microsoft.DiaSymReader/1.0.6": {
"compile": {
"lib/net20/Microsoft.DiaSymReader.dll": {}
},
"runtime": {
"lib/net20/Microsoft.DiaSymReader.dll": {}
}
},
"Microsoft.DiaSymReader.Native/1.3.3": {},
"System.Collections/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Collections.Immutable/1.1.37": {
"dependencies": {
"System.Collections": "[4.0.0, )",
"System.Diagnostics.Debug": "[4.0.0, )",
"System.Globalization": "[4.0.0, )",
"System.Linq": "[4.0.0, )",
"System.Resources.ResourceManager": "[4.0.0, )",
"System.Runtime": "[4.0.0, )",
"System.Runtime.Extensions": "[4.0.0, )",
"System.Threading": "[4.0.0, )"
},
"compile": {
"lib/dotnet/System.Collections.Immutable.dll": {}
},
"runtime": {
"lib/dotnet/System.Collections.Immutable.dll": {}
}
},
"System.Diagnostics.Debug/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Globalization/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.IO/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Linq/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Reflection/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Reflection.Extensions/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Reflection.Metadata/1.1.0": {
"dependencies": {
"System.Collections": "[4.0.0, )",
"System.Collections.Immutable": "[1.1.37, )",
"System.Diagnostics.Debug": "[4.0.0, )",
"System.IO": "[4.0.0, )",
"System.Reflection": "[4.0.0, )",
"System.Reflection.Extensions": "[4.0.0, )",
"System.Reflection.Primitives": "[4.0.0, )",
"System.Resources.ResourceManager": "[4.0.0, )",
"System.Runtime": "[4.0.0, )",
"System.Runtime.Extensions": "[4.0.0, )",
"System.Runtime.InteropServices": "[4.0.0, )",
"System.Text.Encoding": "[4.0.0, )",
"System.Text.Encoding.Extensions": "[4.0.0, )",
"System.Threading": "[4.0.0, )"
},
"compile": {
"lib/dotnet5.2/System.Reflection.Metadata.dll": {}
},
"runtime": {
"lib/dotnet5.2/System.Reflection.Metadata.dll": {}
}
},
"System.Reflection.Primitives/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Resources.ResourceManager/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Runtime/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Runtime.Extensions/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Runtime.InteropServices/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Text.Encoding/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Text.Encoding.Extensions/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Threading/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"xunit/2.1.0": {
"dependencies": {
"xunit.assert": "[2.1.0, 2.1.0]",
"xunit.core": "[2.1.0, 2.1.0]"
}
},
"xunit.abstractions/2.0.0": {
"compile": {
"lib/net35/xunit.abstractions.dll": {}
},
"runtime": {
"lib/net35/xunit.abstractions.dll": {}
}
},
"xunit.assert/2.1.0": {
"compile": {
"lib/dotnet/xunit.assert.dll": {}
},
"runtime": {
"lib/dotnet/xunit.assert.dll": {}
}
},
"xunit.core/2.1.0": {
"dependencies": {
"xunit.extensibility.core": "[2.1.0, 2.1.0]",
"xunit.extensibility.execution": "[2.1.0, 2.1.0]"
}
},
"xunit.extensibility.core/2.1.0": {
"dependencies": {
"xunit.abstractions": "[2.0.0, 2.0.0]"
},
"compile": {
"lib/dotnet/xunit.core.dll": {}
},
"runtime": {
"lib/dotnet/xunit.core.dll": {}
}
},
"xunit.extensibility.execution/2.1.0": {
"dependencies": {
"xunit.extensibility.core": "[2.1.0, 2.1.0]"
},
"compile": {
"lib/net45/xunit.execution.desktop.dll": {}
},
"runtime": {
"lib/net45/xunit.execution.desktop.dll": {}
}
}
},
".NETFramework,Version=v4.5/osx.10.10-anycpu": {
"Microsoft.CodeAnalysis.Test.Resources.Proprietary/1.2.0-beta-20151016-11": {
"compile": {
"lib/net45/Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll": {}
},
"runtime": {
"lib/net45/Microsoft.CodeAnalysis.Test.Resources.Proprietary.dll": {}
}
},
"Microsoft.DiaSymReader/1.0.6": {
"compile": {
"lib/net20/Microsoft.DiaSymReader.dll": {}
},
"runtime": {
"lib/net20/Microsoft.DiaSymReader.dll": {}
}
},
"Microsoft.DiaSymReader.Native/1.3.3": {},
"System.Collections/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Collections.Immutable/1.1.37": {
"dependencies": {
"System.Collections": "[4.0.0, )",
"System.Diagnostics.Debug": "[4.0.0, )",
"System.Globalization": "[4.0.0, )",
"System.Linq": "[4.0.0, )",
"System.Resources.ResourceManager": "[4.0.0, )",
"System.Runtime": "[4.0.0, )",
"System.Runtime.Extensions": "[4.0.0, )",
"System.Threading": "[4.0.0, )"
},
"compile": {
"lib/dotnet/System.Collections.Immutable.dll": {}
},
"runtime": {
"lib/dotnet/System.Collections.Immutable.dll": {}
}
},
"System.Diagnostics.Debug/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Globalization/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.IO/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Linq/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Reflection/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Reflection.Extensions/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Reflection.Metadata/1.1.0": {
"dependencies": {
"System.Collections": "[4.0.0, )",
"System.Collections.Immutable": "[1.1.37, )",
"System.Diagnostics.Debug": "[4.0.0, )",
"System.IO": "[4.0.0, )",
"System.Reflection": "[4.0.0, )",
"System.Reflection.Extensions": "[4.0.0, )",
"System.Reflection.Primitives": "[4.0.0, )",
"System.Resources.ResourceManager": "[4.0.0, )",
"System.Runtime": "[4.0.0, )",
"System.Runtime.Extensions": "[4.0.0, )",
"System.Runtime.InteropServices": "[4.0.0, )",
"System.Text.Encoding": "[4.0.0, )",
"System.Text.Encoding.Extensions": "[4.0.0, )",
"System.Threading": "[4.0.0, )"
},
"compile": {
"lib/dotnet5.2/System.Reflection.Metadata.dll": {}
},
"runtime": {
"lib/dotnet5.2/System.Reflection.Metadata.dll": {}
}
},
"System.Reflection.Primitives/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Resources.ResourceManager/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Runtime/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Runtime.Extensions/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Runtime.InteropServices/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Text.Encoding/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Text.Encoding.Extensions/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"System.Threading/4.0.0": {
"compile": {
"ref/net45/_._": {}
},
"runtime": {
"lib/net45/_._": {}
}
},
"xunit/2.1.0": {
"dependencies": {
"xunit.assert": "[2.1.0, 2.1.0]",
"xunit.core": "[2.1.0, 2.1.0]"
}
},
"xunit.abstractions/2.0.0": {
"compile": {
"lib/net35/xunit.abstractions.dll": {}
},
"runtime": {
"lib/net35/xunit.abstractions.dll": {}
}
},
"xunit.assert/2.1.0": {
"compile": {
"lib/dotnet/xunit.assert.dll": {}
},
"runtime": {
"lib/dotnet/xunit.assert.dll": {}
}
},
"xunit.core/2.1.0": {
"dependencies": {
"xunit.extensibility.core": "[2.1.0, 2.1.0]",
"xunit.extensibility.execution": "[2.1.0, 2.1.0]"
}
},
"xunit.extensibility.core/2.1.0": {
"dependencies": {
"xunit.abstractions": "[2.0.0, 2.0.0]"
},
"compile": {
"lib/dotnet/xunit.core.dll": {}
},
"runtime": {
"lib/dotnet/xunit.core.dll": {}
}
},
"xunit.extensibility.execution/2.1.0": {
"dependencies": {
"xunit.extensibility.core": "[2.1.0, 2.1.0]"
},
"compile": {
"lib/net45/xunit.execution.desktop.dll": {}
},
"runtime": {
"lib/net45/xunit.execution.desktop.dll": {}
}
}
},
".NETFramework,Version=v4.5/ubuntu.14.04": {
"Microsoft.CodeAnalysis.Test.Resources.Proprietary/1.2.0-beta-20151016-11": {
"compile": {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册