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

Add support for AnalyzerManager to manage analyzers for lifetime of an analyzer host.

It ensures the following for the lifetime of analyzer host:
1) DiagnosticAnalyzer.Initialize(AnalysisContext) is invoked only once per-analyzer.
2) DiagnosticAnalyzer.SupportedDiagnostics is invoked only once per-analyzer.
3) CompilationStartAnalyzerActions registered during Initialize are invoked only once per-analyzer per-compilation.
上级 2c239764
......@@ -2924,9 +2924,9 @@ public override IEnumerable<ISymbol> GetSymbolsWithName(Func<string, bool> predi
#endregion
internal override AnalyzerDriver AnalyzerForLanguage(ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, CancellationToken cancellationToken)
internal override AnalyzerDriver AnalyzerForLanguage(ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, CancellationToken cancellationToken)
{
return new AnalyzerDriver<SyntaxKind>(analyzers, n => n.Kind(), options, continueOnAnalyzerException, cancellationToken);
return new AnalyzerDriver<SyntaxKind>(analyzers, n => n.Kind(), options, analyzerManager, continueOnAnalyzerException, cancellationToken);
}
internal void SymbolDeclaredEvent(Symbol symbol)
......
......@@ -86,7 +86,7 @@ public void AnalyzerDriverIsSafeAgainstAnalyzerExceptions()
{
var compilation = CreateCompilationWithMscorlib45(TestResource.AllInOneCSharpCode, parseOptions: TestOptions.Regular);
ThrowingDiagnosticAnalyzer<SyntaxKind>.VerifyAnalyzerEngineIsSafeAgainstExceptions(analyzer =>
compilation.GetAnalyzerDiagnostics(new[] { analyzer }, null, CodeAnalysis.DiagnosticExtensions.AlwaysCatchAnalyzerExceptions), AnalyzerDriverHelper.DiagnosticId);
compilation.GetAnalyzerDiagnostics(new[] { analyzer }, null, CodeAnalysis.DiagnosticExtensions.AlwaysCatchAnalyzerExceptions));
}
[Fact]
......
......@@ -24,6 +24,7 @@
<Compile Include="$(MSBuildThisFileDirectory)DiagnosticAnalyzerAction.cs">
<ExcludeFromStyleCop>true</ExcludeFromStyleCop>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)AnalyzerManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)DiagnosticStartAnalysisScope.cs">
<ExcludeFromStyleCop>true</ExcludeFromStyleCop>
</Compile>
......
......@@ -13,76 +13,50 @@ namespace Microsoft.CodeAnalysis.Diagnostics
{
internal class AnalyzerDriverHelper
{
internal const string DiagnosticId = "AD0001";
private const string DiagnosticId = "AD0001";
private const string DiagnosticCategory = "Compiler";
/// <summary>
/// Executes the <see cref="DiagnosticAnalyzer.Initialize(AnalysisContext)"/> for the given analyzer and returns the set of registered sessions.
/// </summary>
/// <param name="analyzer">Analyzer to get session wide analyzer actions.</param>
/// <param name="addDiagnostic">Delegate to add diagnostics.</param>
/// <param name="continueOnAnalyzerException">Predicate to decide if exceptions from the action should be handled or not.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <remarks>
/// Note that this API doesn't execute any <see cref="CompilationStartAnalyzerAction"/> registered by the Initialize invocation.
/// Use <see cref="ExecuteCompilationStartActions(AnalyzerActions, DiagnosticAnalyzer, Compilation, AnalyzerOptions, Action{Diagnostic}, Func{Exception, DiagnosticAnalyzer, bool}, CancellationToken)"/> API
/// to get execute these actions to get the per-compilation analyzer actions.
/// </remarks>
public static AnalyzerActions GetSessionAnalyzerActions(
internal static void ExecuteInitializeMethod(
DiagnosticAnalyzer analyzer,
HostSessionStartAnalysisScope sessionScope,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
VerifyArguments(analyzer, addDiagnostic, continueOnAnalyzerException);
HostSessionStartAnalysisScope sessionScope = new HostSessionStartAnalysisScope();
ExecuteAndCatchIfThrows(analyzer, addDiagnostic, continueOnAnalyzerException, () =>
{
// The Initialize method should be run asynchronously in case it is not well behaved, e.g. does not terminate.
analyzer.Initialize(new AnalyzerAnalysisContext(analyzer, sessionScope));
}, cancellationToken);
return sessionScope.GetAnalyzerActions(analyzer);
{
// The Initialize method should be run asynchronously in case it is not well behaved, e.g. does not terminate.
analyzer.Initialize(new AnalyzerAnalysisContext(analyzer, sessionScope));
}, cancellationToken);
}
/// <summary>
/// Executes the compilation start actions and returns the per-compilation analyzer actions added by these actions.
/// </summary>
/// <param name="actions"><see cref="AnalyzerActions"/> whose compilation start actions are to be executed.</param>
/// <param name="analyzer">Analyzer on which compilation start actions have to be executed.</param>
/// <param name="compilationScope">HostCompilationStartAnalysisScope to be used.</param>
/// <param name="compilation">Compilation to be used in the analysis.</param>
/// <param name="analyzerOptions">Analyzer options.</param>
/// <param name="addDiagnostic">Delegate to add diagnostics.</param>
/// <param name="continueOnAnalyzerException">Predicate to decide if exceptions from the action should be handled or not.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public static AnalyzerActions ExecuteCompilationStartActions(
AnalyzerActions actions,
DiagnosticAnalyzer analyzer,
public static void ExecuteCompilationStartActions(
ImmutableArray<CompilationStartAnalyzerAction> actions,
HostCompilationStartAnalysisScope compilationScope,
Compilation compilation,
AnalyzerOptions analyzerOptions,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
VerifyArguments(compilation, actions, analyzerOptions, analyzer, addDiagnostic, continueOnAnalyzerException);
HostCompilationStartAnalysisScope compilationScope = new HostCompilationStartAnalysisScope(new HostSessionStartAnalysisScope());
foreach (var startAction in actions.CompilationStartActions)
foreach (var startAction in actions)
{
if (startAction.Analyzer == analyzer)
cancellationToken.ThrowIfCancellationRequested();
ExecuteAndCatchIfThrows(startAction.Analyzer, addDiagnostic, continueOnAnalyzerException, () =>
{
cancellationToken.ThrowIfCancellationRequested();
ExecuteAndCatchIfThrows(analyzer, addDiagnostic, continueOnAnalyzerException, () =>
{
startAction.Action(new AnalyzerCompilationStartAnalysisContext(analyzer, compilationScope, compilation, analyzerOptions, cancellationToken));
}, cancellationToken);
}
startAction.Action(new AnalyzerCompilationStartAnalysisContext(startAction.Analyzer, compilationScope, compilation, analyzerOptions, cancellationToken));
}, cancellationToken);
}
return compilationScope.GetAnalyzerActions(analyzer);
}
/// <summary>
......@@ -102,8 +76,6 @@ internal class AnalyzerDriverHelper
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
VerifyArguments(compilation, actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
HostCompilationStartAnalysisScope compilationScope = new HostCompilationStartAnalysisScope(new HostSessionStartAnalysisScope());
foreach (var endAction in actions.CompilationEndActions)
......@@ -136,8 +108,6 @@ internal class AnalyzerDriverHelper
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
VerifyArguments(symbols, compilation, actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
foreach (var symbol in symbols)
{
foreach (var symbolAction in actions.SymbolActions)
......@@ -173,8 +143,6 @@ internal class AnalyzerDriverHelper
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
VerifyArguments(semanticModel, actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
foreach (var semanticModelAction in actions.SemanticModelActions)
{
cancellationToken.ThrowIfCancellationRequested();
......@@ -205,8 +173,6 @@ internal class AnalyzerDriverHelper
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
VerifyArguments(syntaxTree, actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
foreach (var syntaxTreeAction in actions.SyntaxTreeActions)
{
cancellationToken.ThrowIfCancellationRequested();
......@@ -242,8 +208,6 @@ internal class AnalyzerDriverHelper
CancellationToken cancellationToken)
where TLanguageKindEnum : struct
{
VerifyArguments(nodes, getKind, semanticModel, actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
var syntaxNodeActions = actions.GetSyntaxNodeActions<TLanguageKindEnum>();
foreach (var syntaxNodeAction in syntaxNodeActions)
......@@ -299,8 +263,6 @@ internal class AnalyzerDriverHelper
CancellationToken cancellationToken)
where TLanguageKindEnum : struct
{
VerifyArguments(declarationsInNode, getKind, semanticModel, actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
var codeBlockStartActions = actions.GetCodeBlockStartActions<TLanguageKindEnum>();
var codeBlockEndActions = actions.CodeBlockEndActions;
......@@ -459,212 +421,13 @@ internal static bool CanHaveExecutableCodeBlock(ISymbol symbol)
}
}
private static void VerifyArguments(
IEnumerable<ISymbol> symbols,
Compilation compilation,
AnalyzerActions actions,
AnalyzerOptions analyzerOptions,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException)
{
if (symbols == null)
{
throw new ArgumentNullException(nameof(symbols));
}
if (symbols.Any(s => s == null))
{
throw new ArgumentException(AnalyzerDriverResources.ArgumentElementCannotBeNull, nameof(symbols));
}
VerifyArguments(compilation, actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
}
private static void VerifyArguments(
Compilation compilation,
AnalyzerActions actions,
AnalyzerOptions analyzerOptions,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException)
{
if (compilation == null)
{
throw new ArgumentNullException(nameof(compilation));
}
VerifyArguments(actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
}
internal protected static void VerifyArguments(
SemanticModel semanticModel,
AnalyzerActions actions,
AnalyzerOptions analyzerOptions,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException)
{
if (semanticModel == null)
{
throw new ArgumentNullException(nameof(semanticModel));
}
VerifyArguments(actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
}
private static void VerifyArguments(
SyntaxTree syntaxTree,
AnalyzerActions actions,
AnalyzerOptions analyzerOptions,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException)
{
if (syntaxTree == null)
{
throw new ArgumentNullException(nameof(syntaxTree));
}
VerifyArguments(actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
}
private static void VerifyArguments(
Compilation compilation,
AnalyzerActions actions,
AnalyzerOptions analyzerOptions,
DiagnosticAnalyzer analyzer,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException)
{
if (compilation == null)
{
throw new ArgumentNullException(nameof(compilation));
}
if (analyzer == null)
{
throw new ArgumentNullException(nameof(analyzer));
}
VerifyArguments(actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
}
internal protected static void VerifyArguments(
AnalyzerActions actions,
AnalyzerOptions analyzerOptions,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException)
{
if (actions == null)
{
throw new ArgumentNullException(nameof(actions));
}
VerifyArguments(analyzerOptions, addDiagnostic, continueOnAnalyzerException);
}
internal protected static void VerifyArguments(
AnalyzerOptions analyzerOptions,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException)
{
if (analyzerOptions == null)
{
throw new ArgumentNullException(nameof(analyzerOptions));
}
if (addDiagnostic == null)
{
throw new ArgumentNullException(nameof(addDiagnostic));
}
if (continueOnAnalyzerException == null)
{
throw new ArgumentNullException(nameof(continueOnAnalyzerException));
}
}
internal protected static void VerifyArguments(
DiagnosticAnalyzer analyzer,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException)
{
if (analyzer == null)
{
throw new ArgumentNullException(nameof(analyzer));
}
if (addDiagnostic == null)
{
throw new ArgumentNullException(nameof(addDiagnostic));
}
if (continueOnAnalyzerException == null)
{
throw new ArgumentNullException(nameof(continueOnAnalyzerException));
}
}
private static void VerifyArguments<TLanguageKindEnum>(
IEnumerable<DeclarationInfo> declarationsInNode,
Func<SyntaxNode, TLanguageKindEnum> getKind,
SemanticModel semanticModel,
AnalyzerActions actions,
AnalyzerOptions analyzerOptions,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException)
{
if (declarationsInNode == null)
{
throw new ArgumentNullException(nameof(declarationsInNode));
}
VerifyArguments(getKind, semanticModel, actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
}
private static void VerifyArguments<TLanguageKindEnum>(
IEnumerable<SyntaxNode> nodes,
Func<SyntaxNode, TLanguageKindEnum> getKind,
SemanticModel semanticModel,
AnalyzerActions actions,
AnalyzerOptions analyzerOptions,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException)
{
if (nodes == null)
{
throw new ArgumentNullException(nameof(nodes));
}
if (nodes.Any(n => n == null))
{
throw new ArgumentException(AnalyzerDriverResources.ArgumentElementCannotBeNull, nameof(nodes));
}
VerifyArguments(getKind, semanticModel, actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
}
private static void VerifyArguments<TLanguageKindEnum>(
Func<SyntaxNode, TLanguageKindEnum> getKind,
SemanticModel semanticModel,
AnalyzerActions actions,
AnalyzerOptions analyzerOptions,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException)
{
if (getKind == null)
{
throw new ArgumentNullException(nameof(getKind));
}
VerifyArguments(semanticModel, actions, analyzerOptions, addDiagnostic, continueOnAnalyzerException);
}
internal static void ExecuteAndCatchIfThrows(DiagnosticAnalyzer analyzer, Action<Diagnostic> addDiagnostic, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, Action analyze, CancellationToken cancellationToken)
{
try
{
analyze();
}
catch (OperationCanceledException oce) when(continueOnAnalyzerException(oce, analyzer))
catch (OperationCanceledException oce) when (continueOnAnalyzerException(oce, analyzer))
{
if (oce.CancellationToken != cancellationToken)
{
......@@ -672,12 +435,12 @@ internal static void ExecuteAndCatchIfThrows(DiagnosticAnalyzer analyzer, Action
addDiagnostic(GetAnalyzerDiagnostic(analyzer, oce));
}
}
catch (Exception e) when(continueOnAnalyzerException(e, analyzer))
catch (Exception e) when (continueOnAnalyzerException(e, analyzer))
{
// Create a info diagnostic saying that the analyzer failed
addDiagnostic(GetAnalyzerDiagnostic(analyzer, e));
}
}
}
internal static Diagnostic GetAnalyzerDiagnostic(DiagnosticAnalyzer analyzer, Exception e)
{
......@@ -694,5 +457,21 @@ internal static DiagnosticDescriptor GetDiagnosticDescriptor(string analyzerName
isEnabledByDefault: true,
customTags: WellKnownDiagnosticTags.AnalyzerException);
}
internal static bool IsAnalyzerExceptionDiagnostic(string diagnosticId, IEnumerable<string> customTags)
{
if (diagnosticId == DiagnosticId)
{
foreach (var tag in customTags)
{
if (tag == WellKnownDiagnosticTags.AnalyzerException)
{
return true;
}
}
}
return false;
}
}
}
// 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.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Microsoft.CodeAnalysis.Diagnostics
{
/// <summary>
/// Manages analyzers for analyzer host's lifetime.
///
/// It ensures the following for the lifetime of analyzer host:
/// 1) <see cref="DiagnosticAnalyzer.Initialize(AnalysisContext)"/> is invoked only once per-analyzer.
/// 2) <see cref="DiagnosticAnalyzer.SupportedDiagnostics"/> is invoked only once per-analyzer.
/// 3) <see cref="CompilationStartAnalyzerAction"/> registered during Initialize are invoked only once per-analyzer per-compilation.
/// </summary>
internal class AnalyzerManager
{
public static readonly AnalyzerManager Default = new AnalyzerManager();
// Session wide analyzer actions map that stores HostSessionStartAnalysisScope registered by running the Initialize method on every DiagnosticAnalyzer.
// These are run only once per every analyzer.
private ImmutableDictionary<DiagnosticAnalyzer, HostSessionStartAnalysisScope> _sessionScopeMap =
ImmutableDictionary<DiagnosticAnalyzer, HostSessionStartAnalysisScope>.Empty;
// This map stores the per-compilation HostCompilationStartAnalysisScope for per-compilation analyzer actions, i.e. AnalyzerActions registered by analyzer's CompilationStartActions.
// Compilation start actions will get executed once per-each compilation as user might want to return different set of custom actions for each compilation.
private readonly ConditionalWeakTable<Compilation, ConcurrentDictionary<DiagnosticAnalyzer, HostCompilationStartAnalysisScope>> _compilationScopeMap =
new ConditionalWeakTable<Compilation, ConcurrentDictionary<DiagnosticAnalyzer, HostCompilationStartAnalysisScope>>();
/// <summary>
/// Cache descriptors for each diagnostic analyzer. We do this since <see cref="DiagnosticAnalyzer.SupportedDiagnostics"/> is
/// a property rather than metadata. We expect it to be cheap and immutable, but we can't force them to be so, we cache them
/// and ask only once.
/// </summary>
private readonly ConditionalWeakTable<DiagnosticAnalyzer, IReadOnlyList<DiagnosticDescriptor>> _descriptorCache =
new ConditionalWeakTable<DiagnosticAnalyzer, IReadOnlyList<DiagnosticDescriptor>>();
internal HostCompilationStartAnalysisScope GetCompilationAnalysisScope(
DiagnosticAnalyzer analyzer,
HostSessionStartAnalysisScope sessionScope,
Compilation compilation,
Action<Diagnostic> addDiagnostic,
AnalyzerOptions analyzerOptions,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
var sessionActions = sessionScope.GetAnalyzerActions(analyzer);
Debug.Assert(sessionActions.CompilationStartActionsCount > 0);
var compilationActionsMap = _compilationScopeMap.GetOrCreateValue(compilation);
HostCompilationStartAnalysisScope result;
if (compilationActionsMap.TryGetValue(analyzer, out result))
{
return result;
}
result = new HostCompilationStartAnalysisScope(sessionScope);
AnalyzerDriverHelper.ExecuteCompilationStartActions(sessionActions.CompilationStartActions, result, compilation,
analyzerOptions, addDiagnostic, continueOnAnalyzerException, cancellationToken);
if (!compilationActionsMap.TryAdd(analyzer, result))
{
return compilationActionsMap[analyzer];
}
return result;
}
internal HostSessionStartAnalysisScope GetSessionAnalysisScope(
DiagnosticAnalyzer analyzer,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
return ImmutableInterlocked.GetOrAdd(ref _sessionScopeMap, analyzer, _ =>
{
var sessionScope = new HostSessionStartAnalysisScope();
AnalyzerDriverHelper.ExecuteInitializeMethod(analyzer, sessionScope, addDiagnostic, continueOnAnalyzerException, cancellationToken);
return sessionScope;
});
}
/// <summary>
/// Get all the analyzer actions to execute for the given analyzer against a given compilation.
/// </summary>
public AnalyzerActions GetAnalyzerActions(
DiagnosticAnalyzer analyzer,
Compilation compilation,
Action<Diagnostic> addDiagnostic,
AnalyzerOptions analyzerOptions,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
var sessionScope = GetSessionAnalysisScope(analyzer, addDiagnostic, continueOnAnalyzerException, cancellationToken);
var sessionActions = sessionScope.GetAnalyzerActions(analyzer);
if (sessionActions != null && sessionActions.CompilationStartActionsCount > 0 && compilation != null)
{
var compilationScope = GetCompilationAnalysisScope(analyzer, sessionScope,
compilation, addDiagnostic, analyzerOptions, continueOnAnalyzerException, cancellationToken);
var compilationActions = compilationScope.GetAnalyzerActions(analyzer);
if (compilationActions != null)
{
return sessionActions.Append(compilationActions);
}
}
return sessionActions;
}
/// <summary>
/// Return <see cref="DiagnosticAnalyzer.SupportedDiagnostics"/> of given <paramref name="analyzer"/>.
/// </summary>
public ImmutableArray<DiagnosticDescriptor> GetSupportedDiagnosticDescriptors(
DiagnosticAnalyzer analyzer,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
var descriptors = _descriptorCache.GetValue(analyzer, key =>
{
var supportedDiagnostics = ImmutableArray<DiagnosticDescriptor>.Empty;
// Catch Exception from analyzer.SupportedDiagnostics
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(analyzer, addDiagnostic, continueOnAnalyzerException, () => { supportedDiagnostics = analyzer.SupportedDiagnostics; }, cancellationToken);
return supportedDiagnostics;
});
return (ImmutableArray<DiagnosticDescriptor>)descriptors;
}
}
}
......@@ -408,6 +408,11 @@ protected AnalyzerActions GetOrCreateAnalyzerActions(DiagnosticAnalyzer analyzer
return actions;
}
public void RegisterAnalyzerActions(DiagnosticAnalyzer analyzer, AnalyzerActions actions)
{
_analyzerActions[analyzer] = actions;
}
}
/// <summary>
......
......@@ -334,7 +334,8 @@ private int RunCore(TextWriter consoleOutput, CancellationToken cancellationToke
AnalyzerDriver analyzerDriver = null;
if (!analyzers.IsDefaultOrEmpty)
{
analyzerDriver = AnalyzerDriver.Create(compilation, analyzers, analyzerOptions, out compilation, cancellationToken);
var analyzerManager = new AnalyzerManager();
analyzerDriver = AnalyzerDriver.Create(compilation, analyzers, analyzerOptions, analyzerManager, out compilation, cancellationToken);
}
// Print the diagnostics produced during the parsing stage and exit if there were any errors.
......
B// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
......
......@@ -27,6 +27,7 @@ internal abstract class AnalyzerDriver : IDisposable
private readonly Action<Diagnostic> _addDiagnostic;
private readonly ImmutableArray<DiagnosticAnalyzer> _analyzers;
private readonly CancellationTokenRegistration _queueRegistration;
private readonly AnalyzerManager _analyzerManager;
protected readonly AnalyzerOptions analyzerOptions;
internal readonly Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException;
......@@ -64,7 +65,7 @@ public AsyncQueue<Diagnostic> DiagnosticQueue
/// Finally, it initializes and starts the <see cref="_primaryTask"/> for the driver.
/// </summary>
/// <remarks>
/// NOTE: This method must only be invoked from <see cref="AnalyzerDriver.Create(Compilation, ImmutableArray{DiagnosticAnalyzer}, AnalyzerOptions, out Compilation, CancellationToken)"/>.
/// NOTE: This method must only be invoked from <see cref="AnalyzerDriver.Create(Compilation, ImmutableArray{DiagnosticAnalyzer}, AnalyzerOptions, AnalyzerManager, out Compilation, CancellationToken)"/>.
/// </remarks>
private void Initialize(Compilation comp, CancellationToken cancellationToken)
{
......@@ -76,8 +77,8 @@ private void Initialize(Compilation comp, CancellationToken cancellationToken)
_compilation = comp;
// Compute the set of effective actions based on suppression, and running the initial analyzers
var sessionAnalysisScope = GetSessionAnalysisScope(_analyzers, comp.Options, _addDiagnostic, continueOnAnalyzerException, cancellationToken);
this.compilationAnalysisScope = GetCompilationAnalysisScope(sessionAnalysisScope, comp, analyzerOptions, _addDiagnostic, continueOnAnalyzerException, cancellationToken);
var sessionAnalysisScope = GetSessionAnalysisScope(_analyzers, _analyzerManager, comp.Options, _addDiagnostic, continueOnAnalyzerException, cancellationToken);
this.compilationAnalysisScope = GetCompilationAnalysisScope(_analyzers, _analyzerManager, sessionAnalysisScope, comp, analyzerOptions, _addDiagnostic, continueOnAnalyzerException, cancellationToken);
_symbolActionsByKind = MakeSymbolActionsByKind();
_semanticModelActionsMap = MakeSemanticModelActionsByAnalyzer();
_compilationEndActionsMap = MakeCompilationEndActionsByAnalyzer();
......@@ -142,6 +143,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken)
/// <param name="compilation">The compilation to which the new driver should be attached.</param>
/// <param name="analyzers">The set of analyzers to include in the analysis.</param>
/// <param name="options">Options that are passed to analyzers.</param>
/// <param name="analyzerManager">AnalyzerManager to manage analyzers for the lifetime of analyzer host.</param>
/// <param name="newCompilation">The new compilation with the analyzer driver attached.</param>
/// <param name="cancellationToken">A cancellation token that can be used to abort analysis.</param>
/// <returns>A newly created analyzer driver</returns>
......@@ -149,7 +151,7 @@ private Task ExecuteSyntaxTreeActions(CancellationToken cancellationToken)
/// Note that since a compilation is immutable, the act of creating a driver and attaching it produces
/// a new compilation. Any further actions on the compilation should use the new compilation.
/// </remarks>
public static AnalyzerDriver Create(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, out Compilation newCompilation, CancellationToken cancellationToken)
public static AnalyzerDriver Create(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, out Compilation newCompilation, CancellationToken cancellationToken)
{
if (compilation == null)
{
......@@ -166,14 +168,14 @@ public static AnalyzerDriver Create(Compilation compilation, ImmutableArray<Diag
throw new ArgumentException(CodeAnalysisResources.ArgumentElementCannotBeNull, nameof(analyzers));
}
return Create(compilation, analyzers, options, out newCompilation, continueOnAnalyzerException: null, cancellationToken: cancellationToken);
return Create(compilation, analyzers, options, analyzerManager, out newCompilation, continueOnAnalyzerException: null, cancellationToken: cancellationToken);
}
// internal for testing purposes
internal static AnalyzerDriver Create(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, out Compilation newCompilation, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, CancellationToken cancellationToken)
internal static AnalyzerDriver Create(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, out Compilation newCompilation, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, CancellationToken cancellationToken)
{
options = options ?? AnalyzerOptions.Empty;
AnalyzerDriver analyzerDriver = compilation.AnalyzerForLanguage(analyzers, options, continueOnAnalyzerException, cancellationToken);
AnalyzerDriver analyzerDriver = compilation.AnalyzerForLanguage(analyzers, options, analyzerManager, continueOnAnalyzerException, cancellationToken);
newCompilation = compilation.WithEventQueue(analyzerDriver.CompilationEventQueue);
analyzerDriver.Initialize(newCompilation, cancellationToken);
return analyzerDriver;
......@@ -184,16 +186,18 @@ internal static AnalyzerDriver Create(Compilation compilation, ImmutableArray<Di
/// </summary>
/// <param name="analyzers">The set of analyzers to include in the analysis</param>
/// <param name="options">Options that are passed to analyzers</param>
/// <param name="analyzerManager">AnalyzerManager to manage analyzers for analyzer host's lifetime.</param>
/// <param name="cancellationToken">a cancellation token that can be used to abort analysis</param>
/// <param name="continueOnAnalyzerException">Delegate which is invoked when an analyzer throws an exception.
/// If a non-null delegate is provided and it returns true, then the exception is handled and converted into a diagnostic and driver continues with other analyzers.
/// Otherwise if it returns false, then the exception is not handled by the driver.
/// If null, then the driver always handles the exception.
/// </param>
protected AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, CancellationToken cancellationToken, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException = null)
protected AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, CancellationToken cancellationToken, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException = null)
{
_analyzers = analyzers;
this.analyzerOptions = options;
_analyzerManager = analyzerManager;
this.CompilationEventQueue = new AsyncQueue<CompilationEvent>();
this.DiagnosticQueue = new AsyncQueue<Diagnostic>();
......@@ -549,8 +553,8 @@ internal protected Action<Diagnostic> GetDiagnosticSinkWithSuppression(ISymbol s
private static HostSessionStartAnalysisScope GetSessionAnalysisScope(
IEnumerable<DiagnosticAnalyzer> analyzers,
AnalyzerManager analyzerManager,
CompilationOptions compilationOptions,
Func<DiagnosticAnalyzer, CompilationOptions, Action<Diagnostic>, Func<Exception, DiagnosticAnalyzer, bool>, CancellationToken, bool> isAnalyzerSuppressed,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
......@@ -560,24 +564,16 @@ internal protected Action<Diagnostic> GetDiagnosticSinkWithSuppression(ISymbol s
foreach (DiagnosticAnalyzer analyzer in analyzers)
{
cancellationToken.ThrowIfCancellationRequested();
if (!isAnalyzerSuppressed(analyzer, compilationOptions, addDiagnostic, continueOnAnalyzerException, cancellationToken))
if (!IsDiagnosticAnalyzerSuppressed(analyzer, analyzerManager, compilationOptions, addDiagnostic, continueOnAnalyzerException, cancellationToken))
{
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(analyzer, addDiagnostic, continueOnAnalyzerException, () =>
{
// The Initialize method should be run asynchronously in case it is not well behaved, e.g. does not terminate.
analyzer.Initialize(new AnalyzerAnalysisContext(analyzer, sessionScope));
}, cancellationToken);
var analyzerSessionScope = analyzerManager.GetSessionAnalysisScope(analyzer, addDiagnostic, continueOnAnalyzerException, cancellationToken);
sessionScope.RegisterAnalyzerActions(analyzer, analyzerSessionScope.GetAnalyzerActions(analyzer));
}
}
return sessionScope;
}
private static HostSessionStartAnalysisScope GetSessionAnalysisScope(IEnumerable<DiagnosticAnalyzer> analyzers, CompilationOptions compilationOptions, Action<Diagnostic> addDiagnostic, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, CancellationToken cancellationToken)
{
return GetSessionAnalysisScope(analyzers, compilationOptions, IsDiagnosticAnalyzerSuppressed, addDiagnostic, continueOnAnalyzerException, cancellationToken);
}
private static void VerifyArguments(
IEnumerable<ISymbol> symbols,
Compilation compilation,
......@@ -721,17 +717,22 @@ private static HostSessionStartAnalysisScope GetSessionAnalysisScope(IEnumerable
}
}
private static HostCompilationStartAnalysisScope GetCompilationAnalysisScope(HostSessionStartAnalysisScope session, Compilation compilation, AnalyzerOptions analyzerOptions, Action<Diagnostic> addDiagnostic, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, CancellationToken cancellationToken)
private static HostCompilationStartAnalysisScope GetCompilationAnalysisScope(
ImmutableArray<DiagnosticAnalyzer> analyzers,
AnalyzerManager analyzerManager,
HostSessionStartAnalysisScope session,
Compilation compilation,
AnalyzerOptions analyzerOptions,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
CancellationToken cancellationToken)
{
HostCompilationStartAnalysisScope compilationScope = new HostCompilationStartAnalysisScope(session);
foreach (CompilationStartAnalyzerAction startAction in session.CompilationStartActions)
foreach (var analyzer in analyzers)
{
cancellationToken.ThrowIfCancellationRequested();
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(startAction.Analyzer, addDiagnostic, continueOnAnalyzerException, () =>
{
startAction.Action(new AnalyzerCompilationStartAnalysisContext(startAction.Analyzer, compilationScope, compilation, analyzerOptions, cancellationToken));
}, cancellationToken);
var analyzerCompilationScope = analyzerManager.GetCompilationAnalysisScope(analyzer, session, compilation,
addDiagnostic, analyzerOptions, continueOnAnalyzerException, cancellationToken);
compilationScope.RegisterAnalyzerActions(analyzer, analyzerCompilationScope.GetAnalyzerActions(analyzer));
}
return compilationScope;
......@@ -742,6 +743,7 @@ private static HostCompilationStartAnalysisScope GetCompilationAnalysisScope(Hos
/// </summary>
internal static bool IsDiagnosticAnalyzerSuppressed(
DiagnosticAnalyzer analyzer,
AnalyzerManager analyzerManager,
CompilationOptions options,
Action<Diagnostic> addDiagnostic,
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException,
......@@ -753,11 +755,7 @@ private static HostCompilationStartAnalysisScope GetCompilationAnalysisScope(Hos
return false;
}
var supportedDiagnostics = ImmutableArray<DiagnosticDescriptor>.Empty;
// Catch Exception from analyzer.SupportedDiagnostics
AnalyzerDriverHelper.ExecuteAndCatchIfThrows(analyzer, addDiagnostic, continueOnAnalyzerException, () => { supportedDiagnostics = analyzer.SupportedDiagnostics; }, cancellationToken);
var supportedDiagnostics = analyzerManager.GetSupportedDiagnosticDescriptors(analyzer, addDiagnostic, continueOnAnalyzerException, cancellationToken);
var diagnosticOptions = options.SpecificDiagnosticOptions;
foreach (var diag in supportedDiagnostics)
......@@ -815,13 +813,14 @@ internal class AnalyzerDriver<TLanguageKindEnum> : AnalyzerDriver where TLanguag
/// <param name="analyzers">The set of analyzers to include in the analysis</param>
/// <param name="getKind">A delegate that returns the language-specific kind for a given syntax node</param>
/// <param name="options">Options that are passed to analyzers</param>
/// <param name="analyzerManager">AnalyzerManager to manage analyzers for the lifetime of analyzer host.</param>
/// <param name="continueOnAnalyzerException">Delegate which is invoked when an analyzer throws an exception.
/// If a non-null delegate is provided and it returns true, then the exception is handled and converted into a diagnostic and driver continues with other analyzers.
/// Otherwise if it returns false, then the exception is not handled by the driver.
/// If null, then the driver always handles the exception.
/// </param>
/// <param name="cancellationToken">a cancellation token that can be used to abort analysis</param>
internal AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, Func<SyntaxNode, TLanguageKindEnum> getKind, AnalyzerOptions options, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, CancellationToken cancellationToken) : base(analyzers, options, cancellationToken, continueOnAnalyzerException)
internal AnalyzerDriver(ImmutableArray<DiagnosticAnalyzer> analyzers, Func<SyntaxNode, TLanguageKindEnum> getKind, AnalyzerOptions options, AnalyzerManager analyzerManager, Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException, CancellationToken cancellationToken) : base(analyzers, options, analyzerManager, cancellationToken, continueOnAnalyzerException)
{
_getKind = getKind;
}
......
......@@ -29,7 +29,7 @@ public Compilation Compilation
public CompilationWithAnalyzers(Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, CancellationToken cancellationToken)
{
_cancellationToken = cancellationToken;
_driver = AnalyzerDriver.Create(compilation, analyzers, options, out _compilation, _cancellationToken);
_driver = AnalyzerDriver.Create(compilation, analyzers, options, AnalyzerManager.Default, out _compilation, _cancellationToken);
}
/// <summary>
......@@ -111,7 +111,7 @@ public static bool IsDiagnosticAnalyzerSuppressed(DiagnosticAnalyzer analyzer, C
}
Action<Diagnostic> dummy = _ => { };
return AnalyzerDriver.IsDiagnosticAnalyzerSuppressed(analyzer, options, dummy, continueOnAnalyzerException, CancellationToken.None);
return AnalyzerDriver.IsDiagnosticAnalyzerSuppressed(analyzer, AnalyzerManager.Default, options, dummy, continueOnAnalyzerException, CancellationToken.None);
}
}
}
......@@ -50,9 +50,9 @@
<Project>{F7712928-1175-47B3-8819-EE086753DEE2}</Project>
<Name>CompilerTestUtilities2</Name>
</ProjectReference>
<ProjectReference Include="..\..\VisualBasic\vbc2\vbc2.vcxproj">
<ProjectReference Include="..\..\CSharp\csc2\csc2.vcxproj">
<Project>{63d0f073-f0b3-42cd-829f-e48b977b48ba}</Project>
<Name>vbc2</Name>
<Name>csc2</Name>
</ProjectReference>
<ProjectReference Include="..\Portable\CodeAnalysis.csproj">
<Project>{1ee8cad3-55f9-4d91-96b2-084641da9a6c}</Project>
......@@ -133,4 +133,4 @@
<Import Project="..\..\..\..\build\Roslyn.Toolsets.Xunit.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
</ImportGroup>
</Project>
</Project>
\ No newline at end of file
......@@ -42,7 +42,7 @@ protected override void OnAbstractMember(string abstractMemberName, SyntaxNode n
}
}
public static void VerifyAnalyzerEngineIsSafeAgainstExceptions(Func<DiagnosticAnalyzer, IEnumerable<Diagnostic>> runAnalysis, string exceptionDiagnosticId)
public static void VerifyAnalyzerEngineIsSafeAgainstExceptions(Func<DiagnosticAnalyzer, IEnumerable<Diagnostic>> runAnalysis)
{
var handled = new bool?[AllAnalyzerMemberNames.Length];
for (int i = 0; i < AllAnalyzerMemberNames.Length; i++)
......@@ -52,18 +52,15 @@ public static void VerifyAnalyzerEngineIsSafeAgainstExceptions(Func<DiagnosticAn
analyzer.ThrowOn(member);
try
{
var diagnosticIds = runAnalysis(analyzer).Select(d => d.Id).Distinct();
var diagnostics = runAnalysis(analyzer).Distinct();
handled[i] = analyzer.Thrown ? true : (bool?)null;
if (analyzer.Thrown)
{
if (diagnosticIds.Any())
{
Assert.Equal(exceptionDiagnosticId, diagnosticIds.Single());
}
Assert.True(diagnostics.Any(d => AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic(d.Id, d.Descriptor.CustomTags)));
}
else
{
Assert.False(diagnosticIds.Any());
Assert.False(diagnostics.Any(d => AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic(d.Id, d.Descriptor.CustomTags)));
}
}
catch (DeliberateException)
......
......@@ -2077,9 +2077,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return Not hasError
End Function
Friend Overrides Function AnalyzerForLanguage(analyzers As ImmutableArray(Of DiagnosticAnalyzer), options As AnalyzerOptions, continueOnAnalyzerException As Func(Of Exception, DiagnosticAnalyzer, Boolean), cancellationToken As CancellationToken) As AnalyzerDriver
Friend Overrides Function AnalyzerForLanguage(analyzers As ImmutableArray(Of DiagnosticAnalyzer), options As AnalyzerOptions, analyzerManager As AnalyzerManager, continueOnAnalyzerException As Func(Of Exception, DiagnosticAnalyzer, Boolean), cancellationToken As CancellationToken) As AnalyzerDriver
Dim getKind As Func(Of SyntaxNode, SyntaxKind) = Function(node As SyntaxNode) node.Kind
Return New AnalyzerDriver(Of SyntaxKind)(analyzers, getKind, options, continueOnAnalyzerException, cancellationToken)
Return New AnalyzerDriver(Of SyntaxKind)(analyzers, getKind, options, analyzerManager, continueOnAnalyzerException, cancellationToken)
End Function
#End Region
......
......@@ -42,7 +42,7 @@ End Enum
Public Sub AnalyzerDriverIsSafeAgainstAnalyzerExceptions()
Dim compilation = CreateCompilationWithMscorlib({TestResource.AllInOneVisualBasicCode})
ThrowingDiagnosticAnalyzer(Of SyntaxKind).VerifyAnalyzerEngineIsSafeAgainstExceptions(
Function(analyzer) compilation.GetAnalyzerDiagnostics({analyzer}, Nothing, DiagnosticExtensions.AlwaysCatchAnalyzerExceptions), AnalyzerDriverHelper.DiagnosticId)
Function(analyzer) compilation.GetAnalyzerDiagnostics({analyzer}, Nothing, DiagnosticExtensions.AlwaysCatchAnalyzerExceptions))
End Sub
<Fact>
......
......@@ -54,7 +54,7 @@
</ProjectReference>
<ProjectReference Include="..\..\..\..\Scripting\Test\ScriptingTest.csproj">
<Project>{2DAE4406-7A89-4B5F-95C3-BC5472CE47CE}</Project>
<Name>CommonScriptingTest</Name>
<Name>ScriptingTest</Name>
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
......
......@@ -93,7 +93,7 @@ public void DiagnosticAnalyzerDriverIsSafeAgainstAnalyzerExceptions()
{
var document = workspace.CurrentSolution.Projects.Single().Documents.Single();
ThrowingDiagnosticAnalyzer<SyntaxKind>.VerifyAnalyzerEngineIsSafeAgainstExceptions(analyzer =>
DiagnosticProviderTestUtilities.GetAllDiagnostics(analyzer, document, new Text.TextSpan(0, document.GetTextAsync().Result.Length), donotCatchAnalyzerExceptions: false), AnalyzerDriverHelper.DiagnosticId);
DiagnosticProviderTestUtilities.GetAllDiagnostics(analyzer, document, new Text.TextSpan(0, document.GetTextAsync().Result.Length), donotCatchAnalyzerExceptions: false));
}
}
......
......@@ -426,7 +426,7 @@ Namespace Microsoft.CodeAnalysis.Editor.Implementation.Diagnostics.UnitTests
CancellationToken.None).WaitAndGetResult(CancellationToken.None)
Assert.Equal(1, diagnostics.Count())
Dim diagnostic = diagnostics.First()
Assert.Equal(AnalyzerDriverHelper.DiagnosticId, diagnostic.Id)
Assert.True(AnalyzerDriverHelper.IsAnalyzerExceptionDiagnostic(diagnostic.Id, diagnostic.CustomTags))
Assert.Contains("CodeBlockStartedAnalyzer", diagnostic.Message)
End Using
End Sub
......
......@@ -64,7 +64,7 @@ End Class
Using Workspace = VisualBasicWorkspaceFactory.CreateWorkspaceFromFile(source)
Dim document = Workspace.CurrentSolution.Projects.Single().Documents.Single()
ThrowingDiagnosticAnalyzer(Of SyntaxKind).VerifyAnalyzerEngineIsSafeAgainstExceptions(
Function(analyzer) DiagnosticProviderTestUtilities.GetAllDiagnostics(analyzer, document, New TextSpan(0, document.GetTextAsync().Result.Length), donotCatchAnalyzerExceptions:=False), AnalyzerDriverHelper.DiagnosticId)
Function(analyzer) DiagnosticProviderTestUtilities.GetAllDiagnostics(analyzer, document, New TextSpan(0, document.GetTextAsync().Result.Length), donotCatchAnalyzerExceptions:=False))
End Using
End Sub
......
......@@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics
[Shared]
internal partial class DiagnosticAnalyzerService : IDiagnosticAnalyzerService
{
private readonly AnalyzerManager _analyzerManager;
private readonly WorkspaceAnalyzerManager _workspaceAnalyzerManager;
[ImportingConstructor]
public DiagnosticAnalyzerService([Import(AllowDefault = true)]IWorkspaceDiagnosticAnalyzerProviderService diagnosticAnalyzerProviderService = null)
......@@ -27,28 +27,28 @@ public DiagnosticAnalyzerService([Import(AllowDefault = true)]IWorkspaceDiagnost
private DiagnosticAnalyzerService(IEnumerable<string> workspaceAnalyzerAssemblies) : this()
{
_analyzerManager = new AnalyzerManager(workspaceAnalyzerAssemblies);
_workspaceAnalyzerManager = new WorkspaceAnalyzerManager(workspaceAnalyzerAssemblies);
}
// internal for testing purposes.
internal DiagnosticAnalyzerService(ImmutableArray<AnalyzerReference> workspaceAnalyzers) : this()
{
_analyzerManager = new AnalyzerManager(workspaceAnalyzers);
_workspaceAnalyzerManager = new WorkspaceAnalyzerManager(workspaceAnalyzers);
}
public ImmutableDictionary<string, ImmutableArray<DiagnosticDescriptor>> GetDiagnosticDescriptors(Project projectOpt)
{
if (projectOpt == null)
{
return _analyzerManager.GetHostDiagnosticDescriptorsPerReference();
return _workspaceAnalyzerManager.GetHostDiagnosticDescriptorsPerReference();
}
return _analyzerManager.GetDiagnosticDescriptorsPerReference(projectOpt);
return _workspaceAnalyzerManager.GetDiagnosticDescriptorsPerReference(projectOpt);
}
public ImmutableArray<DiagnosticDescriptor> GetDiagnosticDescriptors(DiagnosticAnalyzer analyzer)
{
return _analyzerManager.GetDiagnosticDescriptors(analyzer);
return _workspaceAnalyzerManager.GetDiagnosticDescriptors(analyzer);
}
public void Reanalyze(Workspace workspace, IEnumerable<ProjectId> projectIds = null, IEnumerable<DocumentId> documentIds = null)
......
......@@ -49,7 +49,7 @@ private BaseDiagnosticIncrementalAnalyzer CreateIncrementalAnalyzerCallback(Work
{
// subscribe to active context changed event for new workspace
workspace.DocumentActiveContextChanged += OnDocumentActiveContextChanged;
return new IncrementalAnalyzerDelegatee(this, workspace, _analyzerManager);
return new IncrementalAnalyzerDelegatee(this, workspace, _workspaceAnalyzerManager);
}
private void OnDocumentActiveContextChanged(object sender, DocumentEventArgs e)
......@@ -61,7 +61,7 @@ private void OnDocumentActiveContextChanged(object sender, DocumentEventArgs e)
internal class IncrementalAnalyzerDelegatee : BaseDiagnosticIncrementalAnalyzer
{
private readonly Workspace _workspace;
private readonly AnalyzerManager _analyzerManager;
private readonly WorkspaceAnalyzerManager _workspaceAnalyzerManager;
private readonly DiagnosticAnalyzerService _owner;
// v1 diagnostic engine
......@@ -70,17 +70,17 @@ internal class IncrementalAnalyzerDelegatee : BaseDiagnosticIncrementalAnalyzer
// v2 diagnostic engine - for now v1
private readonly EngineV2.DiagnosticIncrementalAnalyzer _engineV2;
public IncrementalAnalyzerDelegatee(DiagnosticAnalyzerService owner, Workspace workspace, AnalyzerManager analyzerManager)
public IncrementalAnalyzerDelegatee(DiagnosticAnalyzerService owner, Workspace workspace, WorkspaceAnalyzerManager workspaceAnalyzerManager)
{
_workspace = workspace;
_analyzerManager = analyzerManager;
_workspaceAnalyzerManager = workspaceAnalyzerManager;
_owner = owner;
var v1CorrelationId = LogAggregator.GetNextId();
_engineV1 = new EngineV1.DiagnosticIncrementalAnalyzer(_owner, v1CorrelationId, _workspace, _analyzerManager);
_engineV1 = new EngineV1.DiagnosticIncrementalAnalyzer(_owner, v1CorrelationId, _workspace, _workspaceAnalyzerManager);
var v2CorrelationId = LogAggregator.GetNextId();
_engineV2 = new EngineV2.DiagnosticIncrementalAnalyzer(_owner, v2CorrelationId, _workspace, _analyzerManager);
_engineV2 = new EngineV2.DiagnosticIncrementalAnalyzer(_owner, v2CorrelationId, _workspace, _workspaceAnalyzerManager);
}
#region IIncrementalAnalyzer
......
// 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.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics.Log;
......@@ -46,17 +44,6 @@ internal class DiagnosticAnalyzerDriver
private AnalyzerOptions _analyzerOptions = null;
// Workspace wide session analyzer actions map that stores AnalyzerActions registered by running the Initialize method on every DiagnosticAnalyzer.
// These are run only once per every analyzer.
// The diagnostics generated by the Initialize invocation are also saved onto this map and reported later.
private static ImmutableDictionary<DiagnosticAnalyzer, Tuple<AnalyzerActions, ImmutableArray<Diagnostic>>> s_sessionActionsMap =
ImmutableDictionary<DiagnosticAnalyzer, Tuple<AnalyzerActions, ImmutableArray<Diagnostic>>>.Empty;
// This map stores the per-compilation analyzer actions, i.e. AnalyzerActions registered by analyzer's CompilationStartActions.
// Compilation start actions will get executed once per-each compilation as user might want to return different set of custom actions for each compilation.
private static readonly ConditionalWeakTable<Compilation, ConcurrentDictionary<DiagnosticAnalyzer, AnalyzerActions>> s_compilationActionsTable =
new ConditionalWeakTable<Compilation, ConcurrentDictionary<DiagnosticAnalyzer, AnalyzerActions>>();
public DiagnosticAnalyzerDriver(Document document, TextSpan? span, SyntaxNode root, LogAggregator logAggregator, CancellationToken cancellationToken)
: this(document, span, root, document.Project.LanguageServices.GetService<ISyntaxNodeAnalyzerService>(), cancellationToken)
{
......@@ -250,32 +237,6 @@ private ImmutableArray<SyntaxNode> GetSyntaxNodesToAnalyze()
return _lazyAllSyntaxNodesToAnalyze;
}
private AnalyzerActions GetPerCompilationAnalyzerActions(
DiagnosticAnalyzer analyzer,
AnalyzerActions sessionActions,
Compilation compilation,
Action<Diagnostic> addDiagnostic)
{
Contract.ThrowIfFalse(sessionActions.CompilationStartActionsCount > 0);
var compilationActionsMap = s_compilationActionsTable.GetOrCreateValue(compilation);
AnalyzerActions result;
if (compilationActionsMap.TryGetValue(analyzer, out result))
{
return result;
}
result = AnalyzerDriverHelper.ExecuteCompilationStartActions(sessionActions, analyzer, compilation,
_analyzerOptions, addDiagnostic, CatchAnalyzerException, _cancellationToken);
if (!compilationActionsMap.TryAdd(analyzer, result))
{
return compilationActionsMap[analyzer];
}
return result;
}
public async Task<IEnumerable<Diagnostic>> GetSyntaxDiagnosticsAsync(DiagnosticAnalyzer analyzer)
{
var compilation = _document.Project.SupportsCompilation ? await _document.Project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false) : null;
......@@ -296,34 +257,34 @@ public async Task<IEnumerable<Diagnostic>> GetSyntaxDiagnosticsAsync(DiagnosticA
await documentAnalyzer.AnalyzeSyntaxAsync(_document, diagnostics.Add, _cancellationToken).ConfigureAwait(false);
return diagnostics.ToImmutableArrayOrEmpty();
}
catch (Exception e) when(CatchAnalyzerException(e, analyzer))
catch (Exception e) when (CatchAnalyzerException(e, analyzer))
{
var exceptionDiagnostics = AnalyzerExceptionToDiagnostics(analyzer, e, _cancellationToken);
return GetFilteredDocumentDiagnostics(exceptionDiagnostics, compilation).ToImmutableArray();
}
}
}
var analyzerActions = await this.GetAnalyzerActionsAsync(analyzer, diagnostics.Add).ConfigureAwait(false);
var analyzerActions = await this.GetAnalyzerActionsAsync(analyzer, diagnostics.Add).ConfigureAwait(false);
DiagnosticAnalyzerLogger.UpdateAnalyzerTypeCount(analyzer, analyzerActions, (DiagnosticLogAggregator)_logAggregator);
DiagnosticAnalyzerLogger.UpdateAnalyzerTypeCount(analyzer, analyzerActions, (DiagnosticLogAggregator)_logAggregator);
if (analyzerActions != null)
{
if (_document.SupportsSyntaxTree)
{
AnalyzerDriverHelper.ExecuteSyntaxTreeActions(analyzerActions, _root.SyntaxTree,
_analyzerOptions, diagnostics.Add, CatchAnalyzerException, _cancellationToken);
}
}
if (diagnostics.Count == 0)
if (analyzerActions != null)
{
if (_document.SupportsSyntaxTree)
{
return SpecializedCollections.EmptyEnumerable<Diagnostic>();
AnalyzerDriverHelper.ExecuteSyntaxTreeActions(analyzerActions, _root.SyntaxTree,
_analyzerOptions, diagnostics.Add, CatchAnalyzerException, _cancellationToken);
}
}
return GetFilteredDocumentDiagnostics(diagnostics, compilation).ToImmutableArray();
if (diagnostics.Count == 0)
{
return SpecializedCollections.EmptyEnumerable<Diagnostic>();
}
return GetFilteredDocumentDiagnostics(diagnostics, compilation).ToImmutableArray();
}
}
private IEnumerable<Diagnostic> GetFilteredDocumentDiagnostics(IEnumerable<Diagnostic> diagnostics, Compilation compilation)
{
......@@ -346,47 +307,13 @@ private IEnumerable<Diagnostic> GetFilteredDocumentDiagnosticsCore(IEnumerable<D
: CompilationWithAnalyzers.GetEffectiveDiagnostics(diagsFilteredByLocation, compilation);
}
private Tuple<AnalyzerActions, ImmutableArray<Diagnostic>> GetSessionAnalyzerActionsAndDiagnosticsCore(DiagnosticAnalyzer analyzer)
{
using (var pooledObject = SharedPools.Default<List<Diagnostic>>().GetPooledObject())
{
var diagnostics = pooledObject.Object;
var actions = AnalyzerDriverHelper.GetSessionAnalyzerActions(analyzer, diagnostics.Add, CatchAnalyzerException, _cancellationToken);
var diagArray = diagnostics.ToImmutableArrayOrEmpty();
return Tuple.Create(actions, diagArray);
}
}
private Tuple<AnalyzerActions, ImmutableArray<Diagnostic>> GetSessionAnalyzerActionsAndDiagnostics(DiagnosticAnalyzer analyzer)
{
return ImmutableInterlocked.GetOrAdd(ref s_sessionActionsMap, analyzer, GetSessionAnalyzerActionsAndDiagnosticsCore);
}
public AnalyzerActions GetSessionAnalyzerActions(DiagnosticAnalyzer analyzer)
{
return GetSessionAnalyzerActionsAndDiagnostics(analyzer).Item1;
}
public async Task<AnalyzerActions> GetAnalyzerActionsAsync(DiagnosticAnalyzer analyzer, Action<Diagnostic> addDiagnostic)
{
var analyzerActions = GetSessionAnalyzerActions(analyzer);
DiagnosticAnalyzerLogger.UpdateAnalyzerTypeCount(analyzer, analyzerActions, (DiagnosticLogAggregator)_logAggregator);
if (analyzerActions != null)
{
if (_project.SupportsCompilation && analyzerActions.CompilationStartActionsCount > 0)
{
var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false);
var analyzerCompilationActions = GetPerCompilationAnalyzerActions(analyzer, analyzerActions, compilation, addDiagnostic);
if (analyzerCompilationActions != null)
{
analyzerActions = analyzerActions.Append(analyzerCompilationActions);
DiagnosticAnalyzerLogger.UpdateAnalyzerTypeCount(analyzer, analyzerActions, (DiagnosticLogAggregator)_logAggregator);
}
}
}
Contract.ThrowIfFalse(_project.SupportsCompilation);
var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false);
var analyzerActions = AnalyzerManager.Default.GetAnalyzerActions(analyzer, compilation, addDiagnostic, _analyzerOptions, CatchAnalyzerException, _cancellationToken);
DiagnosticAnalyzerLogger.UpdateAnalyzerTypeCount(analyzer, analyzerActions, (DiagnosticLogAggregator)_logAggregator);
return analyzerActions;
}
......@@ -414,61 +341,61 @@ public async Task<IEnumerable<Diagnostic>> GetSemanticDiagnosticsAsync(Diagnosti
{
await documentAnalyzer.AnalyzeSemanticsAsync(_document, diagnostics.Add, _cancellationToken).ConfigureAwait(false);
}
catch (Exception e) when(CatchAnalyzerException(e, analyzer))
catch (Exception e) when (CatchAnalyzerException(e, analyzer))
{
var exceptionDiagnostics = AnalyzerExceptionToDiagnostics(analyzer, e, _cancellationToken);
return model == null ? exceptionDiagnostics : GetFilteredDocumentDiagnostics(exceptionDiagnostics, model.Compilation);
}
}
}
else
{
var analyzerActions = await GetAnalyzerActionsAsync(analyzer, diagnostics.Add).ConfigureAwait(false);
var analyzerActions = await GetAnalyzerActionsAsync(analyzer, diagnostics.Add).ConfigureAwait(false);
if (analyzerActions != null)
if (analyzerActions != null)
{
// SemanticModel actions.
if (analyzerActions.SemanticModelActionsCount > 0)
{
// SemanticModel actions.
if (analyzerActions.SemanticModelActionsCount > 0)
{
AnalyzerDriverHelper.ExecuteSemanticModelActions(analyzerActions, model, _analyzerOptions,
diagnostics.Add, CatchAnalyzerException, _cancellationToken);
}
AnalyzerDriverHelper.ExecuteSemanticModelActions(analyzerActions, model, _analyzerOptions,
diagnostics.Add, CatchAnalyzerException, _cancellationToken);
}
var compilation = model.Compilation;
var compilation = model.Compilation;
// Symbol actions.
if (analyzerActions.SymbolActionsCount > 0)
// Symbol actions.
if (analyzerActions.SymbolActionsCount > 0)
{
var symbols = this.GetSymbolsToAnalyze(model);
AnalyzerDriverHelper.ExecuteSymbolActions(analyzerActions, symbols, compilation,
_analyzerOptions, diagnostics.Add, CatchAnalyzerException, _cancellationToken);
}
if (this.SyntaxNodeAnalyzerService != null)
{
// SyntaxNode actions.
if (analyzerActions.SyntaxNodeActionsCount > 0)
{
var symbols = this.GetSymbolsToAnalyze(model);
AnalyzerDriverHelper.ExecuteSymbolActions(analyzerActions, symbols, compilation,
_analyzerOptions, diagnostics.Add, CatchAnalyzerException, _cancellationToken);
this.SyntaxNodeAnalyzerService.ExecuteSyntaxNodeActions(analyzerActions, GetSyntaxNodesToAnalyze(), model,
_analyzerOptions, diagnostics.Add, CatchAnalyzerException, _cancellationToken);
}
if (this.SyntaxNodeAnalyzerService != null)
// CodeBlockStart, CodeBlockEnd, and generated SyntaxNode actions.
if (analyzerActions.CodeBlockStartActionsCount > 0 || analyzerActions.CodeBlockEndActionsCount > 0)
{
// SyntaxNode actions.
if (analyzerActions.SyntaxNodeActionsCount > 0)
{
this.SyntaxNodeAnalyzerService.ExecuteSyntaxNodeActions(analyzerActions, GetSyntaxNodesToAnalyze(), model,
_analyzerOptions, diagnostics.Add, CatchAnalyzerException, _cancellationToken);
}
// CodeBlockStart, CodeBlockEnd, and generated SyntaxNode actions.
if (analyzerActions.CodeBlockStartActionsCount > 0 || analyzerActions.CodeBlockEndActionsCount > 0)
{
this.SyntaxNodeAnalyzerService.ExecuteCodeBlockActions(analyzerActions, this.GetDeclarationInfos(model), model,
_analyzerOptions, diagnostics.Add, CatchAnalyzerException, _cancellationToken);
}
this.SyntaxNodeAnalyzerService.ExecuteCodeBlockActions(analyzerActions, this.GetDeclarationInfos(model), model,
_analyzerOptions, diagnostics.Add, CatchAnalyzerException, _cancellationToken);
}
}
}
}
var result = model == null
? diagnostics
: GetFilteredDocumentDiagnostics(diagnostics, model.Compilation);
var result = model == null
? diagnostics
: GetFilteredDocumentDiagnostics(diagnostics, model.Compilation);
return result.ToImmutableArray();
}
return result.ToImmutableArray();
}
}
public async Task<IEnumerable<Diagnostic>> GetProjectDiagnosticsAsync(DiagnosticAnalyzer analyzer, Action<Project, DiagnosticAnalyzer, CancellationToken> forceAnalyzeAllDocuments)
{
......@@ -501,7 +428,7 @@ private async Task GetProjectDiagnosticsWorkerAsync(DiagnosticAnalyzer analyzer,
{
await projectAnalyzer.AnalyzeProjectAsync(_project, diagnostics.Add, _cancellationToken).ConfigureAwait(false);
}
catch (Exception e) when(CatchAnalyzerException(e, analyzer))
catch (Exception e) when (CatchAnalyzerException(e, analyzer))
{
var exceptionDiagnostics = AnalyzerExceptionToDiagnostics(analyzer, e, _cancellationToken);
if (_project.SupportsCompilation)
......@@ -512,40 +439,27 @@ private async Task GetProjectDiagnosticsWorkerAsync(DiagnosticAnalyzer analyzer,
diagnostics.AddRange(exceptionDiagnostics);
}
}
}
private async Task GetCompilationDiagnosticsAsync(DiagnosticAnalyzer analyzer, List<Diagnostic> diagnostics, Action<Project, DiagnosticAnalyzer, CancellationToken> forceAnalyzeAllDocuments)
{
Contract.ThrowIfFalse(_project.SupportsCompilation);
var analyzerActionsAndDiagnostics = GetSessionAnalyzerActionsAndDiagnostics(analyzer);
var analyzerActions = analyzerActionsAndDiagnostics.Item1;
var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false);
diagnostics.AddRange(CompilationWithAnalyzers.GetEffectiveDiagnostics(analyzerActionsAndDiagnostics.Item2, compilation));
DiagnosticAnalyzerLogger.UpdateAnalyzerTypeCount(analyzer, analyzerActions, (DiagnosticLogAggregator)_logAggregator);
if (analyzerActions == null || (analyzerActions.CompilationStartActionsCount == 0 && analyzerActions.CompilationEndActionsCount == 0))
{
return;
}
using (var pooledObject = SharedPools.Default<List<Diagnostic>>().GetPooledObject())
{
var localDiagnostics = pooledObject.Object;
// Get all the analyzer actions, including the per-compilation actions.
var allAnalyzerActions = await GetAnalyzerActionsAsync(analyzer, localDiagnostics.Add).ConfigureAwait(false);
if (analyzerActions.CompilationEndActionsCount < allAnalyzerActions.CompilationEndActionsCount && forceAnalyzeAllDocuments != null)
var analyzerActions = await GetAnalyzerActionsAsync(analyzer, localDiagnostics.Add).ConfigureAwait(false);
if (analyzerActions.CompilationEndActionsCount > 0 && analyzerActions.CompilationStartActionsCount > 0 && forceAnalyzeAllDocuments != null)
{
if (allAnalyzerActions.CodeBlockEndActionsCount > analyzerActions.CodeBlockEndActionsCount ||
allAnalyzerActions.CodeBlockStartActionsCount > analyzerActions.CodeBlockStartActionsCount ||
allAnalyzerActions.SemanticModelActionsCount > analyzerActions.SemanticModelActionsCount ||
allAnalyzerActions.SymbolActionsCount > analyzerActions.SymbolActionsCount ||
allAnalyzerActions.SyntaxNodeActionsCount > analyzerActions.SyntaxNodeActionsCount ||
allAnalyzerActions.SyntaxTreeActionsCount > analyzerActions.SyntaxTreeActionsCount)
if (analyzerActions.CodeBlockEndActionsCount > 0 ||
analyzerActions.CodeBlockStartActionsCount > 0 ||
analyzerActions.SemanticModelActionsCount > 0 ||
analyzerActions.SymbolActionsCount > 0 ||
analyzerActions.SyntaxNodeActionsCount > 0 ||
analyzerActions.SyntaxTreeActionsCount > 0)
{
// Analyzer registered a compilation end action and at least one other analyzer action during it's compilation start action.
// We need to ensure that we have force analyzed all documents in this project for this analyzer before executing the end actions.
......@@ -554,7 +468,8 @@ private async Task GetCompilationDiagnosticsAsync(DiagnosticAnalyzer analyzer, L
}
// CompilationEnd actions.
AnalyzerDriverHelper.ExecuteCompilationEndActions(allAnalyzerActions, compilation, _analyzerOptions, localDiagnostics.Add, CatchAnalyzerException, _cancellationToken);
var compilation = await _project.GetCompilationAsync(_cancellationToken).ConfigureAwait(false);
AnalyzerDriverHelper.ExecuteCompilationEndActions(analyzerActions, compilation, _analyzerOptions, localDiagnostics.Add, CatchAnalyzerException, _cancellationToken);
diagnostics.AddRange(CompilationWithAnalyzers.GetEffectiveDiagnostics(localDiagnostics, compilation));
}
}
......
......@@ -30,10 +30,10 @@ private partial class DiagnosticAnalyzersAndStates
public readonly Workspace Workspace;
public DiagnosticAnalyzersAndStates(DiagnosticIncrementalAnalyzer owner, Workspace workspace, AnalyzerManager analyzerManager)
public DiagnosticAnalyzersAndStates(DiagnosticIncrementalAnalyzer owner, Workspace workspace, WorkspaceAnalyzerManager workspaceAnalyzerManager)
{
_owner = owner;
_sharedAnalyzersAndStates = new WorkspaceAnalyzersAndStates(analyzerManager);
_sharedAnalyzersAndStates = new WorkspaceAnalyzersAndStates(workspaceAnalyzerManager);
_projectAnalyzersAndStatesMap = new ConcurrentDictionary<ProjectId, ProjectAnalyzersAndStates>();
this.Workspace = workspace;
......
......@@ -26,23 +26,23 @@ private class PerLanguageAnalyzersAndStates
private readonly int _analyzerCount;
private readonly DiagnosticState[,] _diagnosticStateMaps;
public PerLanguageAnalyzersAndStates(AnalyzerManager analyzerManager, string language)
public PerLanguageAnalyzersAndStates(WorkspaceAnalyzerManager workspaceAnalyzerManager, string language)
{
_language = language;
// TODO: dynamically re-order providers so that cheap one runs first and slower runs later.
_diagnosticAnalyzerIdMap = CreateAnalyzerIdMap(analyzerManager, language);
_diagnosticAnalyzerIdMap = CreateAnalyzerIdMap(workspaceAnalyzerManager, language);
_analyzerCount = _diagnosticAnalyzerIdMap.Values.Flatten().Count();
_diagnosticStateMaps = new DiagnosticState[s_stateTypeCount, _analyzerCount];
}
private static ImmutableDictionary<string, ImmutableDictionary<DiagnosticAnalyzer, ProviderId>> CreateAnalyzerIdMap(
AnalyzerManager analyzerManager, string language)
WorkspaceAnalyzerManager workspaceAnalyzerManager, string language)
{
var index = 0;
var map = ImmutableDictionary.CreateBuilder<string, ImmutableDictionary<DiagnosticAnalyzer, ProviderId>>();
foreach (var kv in analyzerManager.GetHostDiagnosticAnalyzersPerReference(language))
foreach (var kv in workspaceAnalyzerManager.GetHostDiagnosticAnalyzersPerReference(language))
{
var perAnalyzerMap = ImmutableDictionary.CreateBuilder<DiagnosticAnalyzer, ProviderId>();
......
......@@ -19,18 +19,18 @@ private partial class DiagnosticAnalyzersAndStates
/// </summary>
private partial class WorkspaceAnalyzersAndStates
{
private readonly AnalyzerManager _analyzerManager;
private readonly WorkspaceAnalyzerManager _workspaceAnalyzerManager;
private ImmutableDictionary<string, PerLanguageAnalyzersAndStates> _perLanguageAnalyzersAndStatesMap;
public WorkspaceAnalyzersAndStates(AnalyzerManager analyzerManager)
public WorkspaceAnalyzersAndStates(WorkspaceAnalyzerManager workspaceAnalyzerManager)
{
_analyzerManager = analyzerManager;
_workspaceAnalyzerManager = workspaceAnalyzerManager;
_perLanguageAnalyzersAndStatesMap = ImmutableDictionary<string, PerLanguageAnalyzersAndStates>.Empty;
}
private static PerLanguageAnalyzersAndStates CreatePerLanguageAnalyzersAndStates(string language, WorkspaceAnalyzersAndStates @this)
{
return new PerLanguageAnalyzersAndStates(@this._analyzerManager, language);
return new PerLanguageAnalyzersAndStates(@this._workspaceAnalyzerManager, language);
}
private PerLanguageAnalyzersAndStates GetOrCreatePerLanguageAnalyzersAndStates(string language)
......
......@@ -35,12 +35,12 @@ internal partial class DiagnosticIncrementalAnalyzer : BaseDiagnosticIncremental
private DiagnosticLogAggregator _diagnosticLogAggregator;
public DiagnosticIncrementalAnalyzer(DiagnosticAnalyzerService owner, int correlationId, Workspace workspace, AnalyzerManager analyzerManager)
public DiagnosticIncrementalAnalyzer(DiagnosticAnalyzerService owner, int correlationId, Workspace workspace, WorkspaceAnalyzerManager workspaceAnalyzerManager)
{
_owner = owner;
_correlationId = correlationId;
_memberRangeMap = new MemberRangeMap();
_analyzersAndState = new DiagnosticAnalyzersAndStates(this, workspace, analyzerManager);
_analyzersAndState = new DiagnosticAnalyzersAndStates(this, workspace, workspaceAnalyzerManager);
_executor = new AnalyzerExecutor(this);
_diagnosticLogAggregator = new DiagnosticLogAggregator(_owner);
......
......@@ -14,14 +14,14 @@ internal class DiagnosticIncrementalAnalyzer : BaseDiagnosticIncrementalAnalyzer
private readonly int _correlationId;
private readonly DiagnosticAnalyzerService _owner;
private readonly Workspace _workspace;
private readonly AnalyzerManager _analyzerManager;
private readonly WorkspaceAnalyzerManager _workspaceAnalyzerManager;
public DiagnosticIncrementalAnalyzer(DiagnosticAnalyzerService owner, int correlationId, Workspace workspace, AnalyzerManager analyzerManager)
public DiagnosticIncrementalAnalyzer(DiagnosticAnalyzerService owner, int correlationId, Workspace workspace, WorkspaceAnalyzerManager workspaceAnalyzerManager)
{
_correlationId = correlationId;
_owner = owner;
_workspace = workspace;
_analyzerManager = analyzerManager;
_workspaceAnalyzerManager = workspaceAnalyzerManager;
}
#region IIncrementalAnalyzer
......
......@@ -7,6 +7,7 @@
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.CodeAnalysis.Diagnostics.Log;
using Roslyn.Utilities;
......@@ -21,7 +22,7 @@ namespace Microsoft.CodeAnalysis.Diagnostics
///
/// this should be alway thread-safe.
/// </summary>
internal sealed class AnalyzerManager
internal sealed class WorkspaceAnalyzerManager
{
/// <summary>
/// Key is analyzer reference identity <see cref="GetAnalyzerReferenceIdentity(AnalyzerReference)"/>.
......@@ -38,13 +39,6 @@ internal sealed class AnalyzerManager
/// </summary>
private readonly ConcurrentDictionary<string, ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>>> _hostDiagnosticAnalyzersPerLanguageMap;
/// <summary>
/// Cache descriptors for each diagnostic analyzer. We do this since <see cref="DiagnosticAnalyzer.SupportedDiagnostics"/> is
/// a property rather than metadata. We expect it to be cheap and immutable, but We can't force them to be so, we cache them
/// and ask only once.
/// </summary>
private readonly ConditionalWeakTable<DiagnosticAnalyzer, IReadOnlyList<DiagnosticDescriptor>> _descriptorCache;
/// <summary>
/// Key is analyzer reference identity <see cref="GetAnalyzerReferenceIdentity(AnalyzerReference)"/>.
///
......@@ -54,18 +48,15 @@ internal sealed class AnalyzerManager
/// </summary>
private readonly Lazy<ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>>> _lazyHostDiagnosticAnalyzersPerReferenceMap;
public AnalyzerManager(IEnumerable<string> hostAnalyzerAssemblies) :
public WorkspaceAnalyzerManager(IEnumerable<string> hostAnalyzerAssemblies) :
this(CreateAnalyzerReferencesFromAssemblies(hostAnalyzerAssemblies))
{
}
public AnalyzerManager(ImmutableArray<AnalyzerReference> hostAnalyzerReferences)
public WorkspaceAnalyzerManager(ImmutableArray<AnalyzerReference> hostAnalyzerReferences)
{
_hostAnalyzerReferencesMap = hostAnalyzerReferences.IsDefault ? ImmutableDictionary<string, AnalyzerReference>.Empty : CreateAnalyzerReferencesMap(hostAnalyzerReferences);
_hostDiagnosticAnalyzersPerLanguageMap = new ConcurrentDictionary<string, ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>>>(concurrencyLevel: 2, capacity: 2);
_descriptorCache = new ConditionalWeakTable<DiagnosticAnalyzer, IReadOnlyList<DiagnosticDescriptor>>();
_hostDiagnosticAnalyzersPerLanguageMap = new ConcurrentDictionary<string, ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>>>(concurrencyLevel: 2, capacity: 2);
_lazyHostDiagnosticAnalyzersPerReferenceMap = new Lazy<ImmutableDictionary<string, ImmutableArray<DiagnosticAnalyzer>>>(() => CreateDiagnosticAnalyzersPerReferenceMap(_hostAnalyzerReferencesMap), isThreadSafe: true);
DiagnosticAnalyzerLogger.LogWorkspaceAnalyzers(hostAnalyzerReferences);
......@@ -84,21 +75,11 @@ public string GetAnalyzerReferenceIdentity(AnalyzerReference reference)
/// </summary>
public ImmutableArray<DiagnosticDescriptor> GetDiagnosticDescriptors(DiagnosticAnalyzer analyzer)
{
var descriptors = _descriptorCache.GetValue(analyzer, key =>
{
try
{
return analyzer.SupportedDiagnostics;
}
catch when (!AnalyzerHelper.IsBuiltInAnalyzer(analyzer))
{
// TODO: here, we should report the issue to host update service
// If the SupportedDiagnostics throws an exception, then we don't want to run the analyzer.
return ImmutableArray<DiagnosticDescriptor>.Empty;
}
});
// TODO: report diagnostics from exceptions thrown in DiagnosticAnalyzer.SupportedDiagnostics
Action<Diagnostic> dummyAddDiagnostic = _ => { };
return (ImmutableArray<DiagnosticDescriptor>)descriptors;
Func<Exception, DiagnosticAnalyzer, bool> continueOnAnalyzerException = (ex, a) => !AnalyzerHelper.IsBuiltInAnalyzer(analyzer);
return AnalyzerManager.Default.GetSupportedDiagnosticDescriptors(analyzer, dummyAddDiagnostic, continueOnAnalyzerException, CancellationToken.None);
}
/// <summary>
......
......@@ -181,7 +181,7 @@
<Compile Include="Completion\Providers\RecommendedKeyword.cs" />
<Compile Include="Diagnostics\AnalyzerDriverResources.cs" />
<Compile Include="Diagnostics\AnalyzerHelper.cs" />
<Compile Include="Diagnostics\AnalyzerManager.cs" />
<Compile Include="Diagnostics\WorkspaceAnalyzerManager.cs" />
<Compile Include="Diagnostics\Analyzers\IDEDiagnosticIds.cs" />
<Compile Include="Diagnostics\BaseDiagnosticIncrementalAnalyzer.cs" />
<Compile Include="Diagnostics\EngineV2\DiagnosticIncrementalAnalyzer.cs" />
......@@ -536,7 +536,6 @@
<ItemGroup>
<Compile Include="CodeFixes\Suppression\SuppressionCodeAction.cs" />
<None Include="packages.config" />
<PublicAPI Include="PublicAPI.txt" />
</ItemGroup>
<ItemGroup />
<Import Project="..\..\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems" Label="Shared" />
......
......@@ -25,7 +25,13 @@ public static DiagnosticAnalyzerCategory GetDiagnosticAnalyzerCategory(this Diag
// to be able to operate on a limited span of the document. In practical terms, no analyzer
// can have both SemanticDocumentAnalysis and SemanticSpanAnalysis as categories.
bool cantSupportSemanticSpanAnalysis = false;
var analyzerActions = driver.GetSessionAnalyzerActions(analyzer);
var analyzerActions = AnalyzerManager.Default.GetAnalyzerActions(analyzer,
null,
_ => { },
null,
driver.CatchAnalyzerExceptionHandler,
driver.CancellationToken);
if (analyzerActions != null)
{
if (analyzerActions.SyntaxTreeActionsCount > 0)
......
......@@ -315,17 +315,23 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Setup", "Setup", "{4C81EBB2
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{998CAFE8-06E4-4683-A151-0F6AA4BFF6C6}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "AnalyzerDriver", "Compilers\Core\AnalyzerDriver\AnalyzerDriver.shproj", "{D0BC9BE7-24F6-40CA-8DC6-FCB93BD44B34}"
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "SharedCollections", "Compilers\Core\SharedCollections\SharedCollections.shproj", "{C1930979-C824-496B-A630-70F5369A636F}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{edc68a0e-c68d-4a74-91b7-bf38ec909888}*SharedItemsImports = 4
Compilers\Core\SharedCollections\SharedCollections.projitems*{5f8d2414-064a-4b3a-9b42-8e2a04246be5}*SharedItemsImports = 4
Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{d0bc9be7-24f6-40ca-8dc6-fcb93bd44b34}*SharedItemsImports = 13
Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{a1bcd0ce-6c2f-4f8c-9a48-d9d93928e26d}*SharedItemsImports = 4
Compilers\Core\SharedCollections\SharedCollections.projitems*{afde6bea-5038-4a4a-a88e-dbd2e4088eed}*SharedItemsImports = 4
Compilers\Core\SharedCollections\SharedCollections.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 4
Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 4
Compilers\Core\SharedCollections\SharedCollections.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 4
Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{3973b09a-4fbf-44a5-8359-3d22ceb71f71}*SharedItemsImports = 4
Compilers\CSharp\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems*{b501a547-c911-4a05-ac6e-274a50dff30e}*SharedItemsImports = 4
Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{2523d0e6-df32-4a3e-8ae0-a19bffae2ef6}*SharedItemsImports = 4
Compilers\Core\SharedCollections\SharedCollections.projitems*{c1930979-c824-496b-a630-70f5369a636f}*SharedItemsImports = 13
EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -1975,5 +1981,7 @@ Global
{151F6994-AEB3-4B12-B746-2ACFF26C7BBB} = {235A3418-A3B0-4844-BCEB-F1CF45069232}
{4C81EBB2-82E1-4C81-80C4-84CC40FA281B} = {235A3418-A3B0-4844-BCEB-F1CF45069232}
{998CAFE8-06E4-4683-A151-0F6AA4BFF6C6} = {235A3418-A3B0-4844-BCEB-F1CF45069232}
{D0BC9BE7-24F6-40CA-8DC6-FCB93BD44B34} = {A41D1B99-F489-4C43-BBDF-96D61B19A6B9}
{C1930979-C824-496B-A630-70F5369A636F} = {A41D1B99-F489-4C43-BBDF-96D61B19A6B9}
EndGlobalSection
EndGlobal
......@@ -134,7 +134,7 @@ public static ImmutableArray<Diagnostic> GetAnalyzerDiagnostics<TCompilation>(th
continueOnAnalyzerException = continueOnAnalyzerException ?? DonotCatchAnalyzerExceptions;
Compilation newCompilation;
var driver = AnalyzerDriver.Create(c, analyzers.ToImmutableArray(), options, out newCompilation, continueOnAnalyzerException, CancellationToken.None);
var driver = AnalyzerDriver.Create(c, analyzers.ToImmutableArray(), options, AnalyzerManager.Default, out newCompilation, continueOnAnalyzerException, CancellationToken.None);
var discarded = newCompilation.GetDiagnostics();
diagnostics = driver.GetDiagnosticsAsync().Result;
return (TCompilation)newCompilation; // note this is a new compilation
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册