1016 lines
59 KiB
C#
1016 lines
59 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Threading;
|
|
using Microsoft.CodeAnalysis.Collections;
|
|
using Microsoft.CodeAnalysis.FlowAnalysis;
|
|
using Microsoft.CodeAnalysis.Operations;
|
|
using Microsoft.CodeAnalysis.PooledObjects;
|
|
using Microsoft.CodeAnalysis.Text;
|
|
using Roslyn.Utilities;
|
|
|
|
namespace Microsoft.CodeAnalysis.Diagnostics;
|
|
|
|
internal class AnalyzerExecutor
|
|
{
|
|
private sealed class AnalyzerDiagnosticReporter
|
|
{
|
|
public readonly Action<Diagnostic> AddDiagnosticAction;
|
|
|
|
private static readonly ObjectPool<AnalyzerDiagnosticReporter> s_objectPool = new ObjectPool<AnalyzerDiagnosticReporter>(() => new AnalyzerDiagnosticReporter(), 10);
|
|
|
|
private SourceOrAdditionalFile? _contextFile;
|
|
|
|
private Compilation _compilation;
|
|
|
|
private DiagnosticAnalyzer _analyzer;
|
|
|
|
private bool _isSyntaxDiagnostic;
|
|
|
|
private Action<Diagnostic, CancellationToken>? _addNonCategorizedDiagnostic;
|
|
|
|
private Action<Diagnostic, DiagnosticAnalyzer, bool, CancellationToken>? _addCategorizedLocalDiagnostic;
|
|
|
|
private Action<Diagnostic, DiagnosticAnalyzer, CancellationToken>? _addCategorizedNonLocalDiagnostic;
|
|
|
|
private Func<Diagnostic, DiagnosticAnalyzer, Compilation, CancellationToken, bool> _shouldSuppressGeneratedCodeDiagnostic;
|
|
|
|
private CancellationToken _cancellationToken;
|
|
|
|
public TextSpan? FilterSpanForLocalDiagnostics;
|
|
|
|
public static AnalyzerDiagnosticReporter GetInstance(SourceOrAdditionalFile contextFile, TextSpan? span, Compilation compilation, DiagnosticAnalyzer analyzer, bool isSyntaxDiagnostic, Action<Diagnostic, CancellationToken>? addNonCategorizedDiagnostic, Action<Diagnostic, DiagnosticAnalyzer, bool, CancellationToken>? addCategorizedLocalDiagnostic, Action<Diagnostic, DiagnosticAnalyzer, CancellationToken>? addCategorizedNonLocalDiagnostic, Func<Diagnostic, DiagnosticAnalyzer, Compilation, CancellationToken, bool> shouldSuppressGeneratedCodeDiagnostic, CancellationToken cancellationToken)
|
|
{
|
|
AnalyzerDiagnosticReporter analyzerDiagnosticReporter = s_objectPool.Allocate();
|
|
analyzerDiagnosticReporter._contextFile = contextFile;
|
|
analyzerDiagnosticReporter.FilterSpanForLocalDiagnostics = span;
|
|
analyzerDiagnosticReporter._compilation = compilation;
|
|
analyzerDiagnosticReporter._analyzer = analyzer;
|
|
analyzerDiagnosticReporter._isSyntaxDiagnostic = isSyntaxDiagnostic;
|
|
analyzerDiagnosticReporter._addNonCategorizedDiagnostic = addNonCategorizedDiagnostic;
|
|
analyzerDiagnosticReporter._addCategorizedLocalDiagnostic = addCategorizedLocalDiagnostic;
|
|
analyzerDiagnosticReporter._addCategorizedNonLocalDiagnostic = addCategorizedNonLocalDiagnostic;
|
|
analyzerDiagnosticReporter._shouldSuppressGeneratedCodeDiagnostic = shouldSuppressGeneratedCodeDiagnostic;
|
|
analyzerDiagnosticReporter._cancellationToken = cancellationToken;
|
|
return analyzerDiagnosticReporter;
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
_contextFile = null;
|
|
FilterSpanForLocalDiagnostics = null;
|
|
_compilation = null;
|
|
_analyzer = null;
|
|
_isSyntaxDiagnostic = false;
|
|
_addNonCategorizedDiagnostic = null;
|
|
_addCategorizedLocalDiagnostic = null;
|
|
_addCategorizedNonLocalDiagnostic = null;
|
|
_shouldSuppressGeneratedCodeDiagnostic = null;
|
|
_cancellationToken = default(CancellationToken);
|
|
s_objectPool.Free(this);
|
|
}
|
|
|
|
private AnalyzerDiagnosticReporter()
|
|
{
|
|
AddDiagnosticAction = AddDiagnostic;
|
|
}
|
|
|
|
private void AddDiagnostic(Diagnostic diagnostic)
|
|
{
|
|
if (!_shouldSuppressGeneratedCodeDiagnostic(diagnostic, _analyzer, _compilation, _cancellationToken))
|
|
{
|
|
if (_addCategorizedLocalDiagnostic == null)
|
|
{
|
|
_addNonCategorizedDiagnostic(diagnostic, _cancellationToken);
|
|
}
|
|
else if (isLocalDiagnostic(diagnostic) && (!FilterSpanForLocalDiagnostics.HasValue || FilterSpanForLocalDiagnostics.Value.IntersectsWith(diagnostic.Location.SourceSpan)))
|
|
{
|
|
_addCategorizedLocalDiagnostic(diagnostic, _analyzer, _isSyntaxDiagnostic, _cancellationToken);
|
|
}
|
|
else
|
|
{
|
|
_addCategorizedNonLocalDiagnostic(diagnostic, _analyzer, _cancellationToken);
|
|
}
|
|
}
|
|
bool isLocalDiagnostic(Diagnostic diagnostic2)
|
|
{
|
|
if (diagnostic2.Location.IsInSource)
|
|
{
|
|
if (_contextFile?.SourceTree != null)
|
|
{
|
|
return _contextFile.Value.SourceTree == diagnostic2.Location.SourceTree;
|
|
}
|
|
return false;
|
|
}
|
|
if (_contextFile?.AdditionalFile != null && diagnostic2.Location is ExternalFileLocation externalFileLocation)
|
|
{
|
|
return PathUtilities.Comparer.Equals(_contextFile.Value.AdditionalFile.Path, externalFileLocation.GetLineSpan().Path);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private const string DiagnosticCategory = "Compiler";
|
|
|
|
internal const string AnalyzerExceptionDiagnosticId = "AD0001";
|
|
|
|
internal const string AnalyzerDriverExceptionDiagnosticId = "AD0002";
|
|
|
|
private readonly Action<Diagnostic, CancellationToken>? _addNonCategorizedDiagnostic;
|
|
|
|
private readonly Action<Diagnostic, DiagnosticAnalyzer, bool, CancellationToken>? _addCategorizedLocalDiagnostic;
|
|
|
|
private readonly Action<Diagnostic, DiagnosticAnalyzer, CancellationToken>? _addCategorizedNonLocalDiagnostic;
|
|
|
|
private readonly Action<Suppression>? _addSuppression;
|
|
|
|
private readonly Func<Exception, bool>? _analyzerExceptionFilter;
|
|
|
|
private readonly AnalyzerManager _analyzerManager;
|
|
|
|
private readonly Func<DiagnosticAnalyzer, bool> _isCompilerAnalyzer;
|
|
|
|
private readonly Func<DiagnosticAnalyzer, object?> _getAnalyzerGate;
|
|
|
|
private readonly Func<SyntaxTree, SemanticModel> _getSemanticModel;
|
|
|
|
private readonly Func<DiagnosticAnalyzer, bool> _shouldSkipAnalysisOnGeneratedCode;
|
|
|
|
private readonly Func<Diagnostic, DiagnosticAnalyzer, Compilation, CancellationToken, bool> _shouldSuppressGeneratedCodeDiagnostic;
|
|
|
|
private readonly Func<SyntaxTree, TextSpan, CancellationToken, bool> _isGeneratedCodeLocation;
|
|
|
|
private readonly Func<DiagnosticAnalyzer, SyntaxTree, SyntaxTreeOptionsProvider?, CancellationToken, bool> _isAnalyzerSuppressedForTree;
|
|
|
|
private readonly ConcurrentDictionary<DiagnosticAnalyzer, StrongBox<long>>? _analyzerExecutionTimeMap;
|
|
|
|
private readonly CompilationAnalysisValueProviderFactory _compilationAnalysisValueProviderFactory;
|
|
|
|
private Func<IOperation, ControlFlowGraph>? _lazyGetControlFlowGraph;
|
|
|
|
private ConcurrentDictionary<IOperation, ControlFlowGraph>? _lazyControlFlowGraphMap;
|
|
|
|
private Func<IOperation, ControlFlowGraph> GetControlFlowGraph => GetControlFlowGraphImpl;
|
|
|
|
internal Compilation Compilation { get; }
|
|
|
|
internal AnalyzerOptions AnalyzerOptions { get; }
|
|
|
|
internal Action<Exception, DiagnosticAnalyzer, Diagnostic, CancellationToken> OnAnalyzerException { get; }
|
|
|
|
internal ImmutableDictionary<DiagnosticAnalyzer, TimeSpan> AnalyzerExecutionTimes => _analyzerExecutionTimeMap.ToImmutableDictionary<KeyValuePair<DiagnosticAnalyzer, StrongBox<long>>, DiagnosticAnalyzer, TimeSpan>((KeyValuePair<DiagnosticAnalyzer, StrongBox<long>> pair) => pair.Key, (KeyValuePair<DiagnosticAnalyzer, StrongBox<long>> pair) => TimeSpan.FromTicks(pair.Value.Value));
|
|
|
|
private bool IsAnalyzerSuppressedForTree(DiagnosticAnalyzer analyzer, SyntaxTree tree, CancellationToken cancellationToken)
|
|
{
|
|
return _isAnalyzerSuppressedForTree(analyzer, tree, Compilation.Options.SyntaxTreeOptionsProvider, cancellationToken);
|
|
}
|
|
|
|
public static AnalyzerExecutor Create(Compilation compilation, AnalyzerOptions analyzerOptions, Action<Diagnostic, CancellationToken>? addNonCategorizedDiagnostic, Action<Exception, DiagnosticAnalyzer, Diagnostic, CancellationToken> onAnalyzerException, Func<Exception, bool>? analyzerExceptionFilter, Func<DiagnosticAnalyzer, bool> isCompilerAnalyzer, AnalyzerManager analyzerManager, Func<DiagnosticAnalyzer, bool> shouldSkipAnalysisOnGeneratedCode, Func<Diagnostic, DiagnosticAnalyzer, Compilation, CancellationToken, bool> shouldSuppressGeneratedCodeDiagnostic, Func<SyntaxTree, TextSpan, CancellationToken, bool> isGeneratedCodeLocation, Func<DiagnosticAnalyzer, SyntaxTree, SyntaxTreeOptionsProvider?, CancellationToken, bool> isAnalyzerSuppressedForTree, Func<DiagnosticAnalyzer, object?> getAnalyzerGate, Func<SyntaxTree, SemanticModel> getSemanticModel, bool logExecutionTime = false, Action<Diagnostic, DiagnosticAnalyzer, bool, CancellationToken>? addCategorizedLocalDiagnostic = null, Action<Diagnostic, DiagnosticAnalyzer, CancellationToken>? addCategorizedNonLocalDiagnostic = null, Action<Suppression>? addSuppression = null)
|
|
{
|
|
ConcurrentDictionary<DiagnosticAnalyzer, StrongBox<long>> analyzerExecutionTimeMap = (logExecutionTime ? new ConcurrentDictionary<DiagnosticAnalyzer, StrongBox<long>>() : null);
|
|
return new AnalyzerExecutor(compilation, analyzerOptions, addNonCategorizedDiagnostic, onAnalyzerException, analyzerExceptionFilter, isCompilerAnalyzer, analyzerManager, shouldSkipAnalysisOnGeneratedCode, shouldSuppressGeneratedCodeDiagnostic, isGeneratedCodeLocation, isAnalyzerSuppressedForTree, getAnalyzerGate, getSemanticModel, analyzerExecutionTimeMap, addCategorizedLocalDiagnostic, addCategorizedNonLocalDiagnostic, addSuppression);
|
|
}
|
|
|
|
private AnalyzerExecutor(Compilation compilation, AnalyzerOptions analyzerOptions, Action<Diagnostic, CancellationToken>? addNonCategorizedDiagnosticOpt, Action<Exception, DiagnosticAnalyzer, Diagnostic, CancellationToken> onAnalyzerException, Func<Exception, bool>? analyzerExceptionFilter, Func<DiagnosticAnalyzer, bool> isCompilerAnalyzer, AnalyzerManager analyzerManager, Func<DiagnosticAnalyzer, bool> shouldSkipAnalysisOnGeneratedCode, Func<Diagnostic, DiagnosticAnalyzer, Compilation, CancellationToken, bool> shouldSuppressGeneratedCodeDiagnostic, Func<SyntaxTree, TextSpan, CancellationToken, bool> isGeneratedCodeLocation, Func<DiagnosticAnalyzer, SyntaxTree, SyntaxTreeOptionsProvider?, CancellationToken, bool> isAnalyzerSuppressedForTree, Func<DiagnosticAnalyzer, object?> getAnalyzerGate, Func<SyntaxTree, SemanticModel> getSemanticModel, ConcurrentDictionary<DiagnosticAnalyzer, StrongBox<long>>? analyzerExecutionTimeMap, Action<Diagnostic, DiagnosticAnalyzer, bool, CancellationToken>? addCategorizedLocalDiagnostic, Action<Diagnostic, DiagnosticAnalyzer, CancellationToken>? addCategorizedNonLocalDiagnostic, Action<Suppression>? addSuppression)
|
|
{
|
|
Compilation = compilation;
|
|
AnalyzerOptions = analyzerOptions;
|
|
_addNonCategorizedDiagnostic = addNonCategorizedDiagnosticOpt;
|
|
OnAnalyzerException = onAnalyzerException;
|
|
_analyzerExceptionFilter = analyzerExceptionFilter;
|
|
_isCompilerAnalyzer = isCompilerAnalyzer;
|
|
_analyzerManager = analyzerManager;
|
|
_shouldSkipAnalysisOnGeneratedCode = shouldSkipAnalysisOnGeneratedCode;
|
|
_shouldSuppressGeneratedCodeDiagnostic = shouldSuppressGeneratedCodeDiagnostic;
|
|
_isGeneratedCodeLocation = isGeneratedCodeLocation;
|
|
_isAnalyzerSuppressedForTree = isAnalyzerSuppressedForTree;
|
|
_getAnalyzerGate = getAnalyzerGate;
|
|
_getSemanticModel = getSemanticModel;
|
|
_analyzerExecutionTimeMap = analyzerExecutionTimeMap;
|
|
_addCategorizedLocalDiagnostic = addCategorizedLocalDiagnostic;
|
|
_addCategorizedNonLocalDiagnostic = addCategorizedNonLocalDiagnostic;
|
|
_addSuppression = addSuppression;
|
|
_compilationAnalysisValueProviderFactory = new CompilationAnalysisValueProviderFactory();
|
|
}
|
|
|
|
public void ExecuteInitializeMethod(DiagnosticAnalyzer analyzer, HostSessionStartAnalysisScope sessionScope, CancellationToken cancellationToken)
|
|
{
|
|
AnalyzerAnalysisContext item = new AnalyzerAnalysisContext(analyzer, sessionScope);
|
|
ExecuteAndCatchIfThrows(analyzer, delegate((DiagnosticAnalyzer analyzer, AnalyzerAnalysisContext context) data)
|
|
{
|
|
data.analyzer.Initialize(data.context);
|
|
}, (analyzer, item), null, cancellationToken);
|
|
}
|
|
|
|
public void ExecuteCompilationStartActions(ImmutableArray<CompilationStartAnalyzerAction> actions, HostCompilationStartAnalysisScope compilationScope, CancellationToken cancellationToken)
|
|
{
|
|
ImmutableArray<CompilationStartAnalyzerAction>.Enumerator enumerator = actions.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
CompilationStartAnalyzerAction current = enumerator.Current;
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
AnalyzerCompilationStartAnalysisContext item = new AnalyzerCompilationStartAnalysisContext(current.Analyzer, compilationScope, Compilation, AnalyzerOptions, _compilationAnalysisValueProviderFactory, cancellationToken);
|
|
ExecuteAndCatchIfThrows(current.Analyzer, delegate((Action<CompilationStartAnalysisContext> action, AnalyzerCompilationStartAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, (current.Action, item), new AnalysisContextInfo(Compilation), cancellationToken);
|
|
}
|
|
}
|
|
|
|
public void ExecuteSymbolStartActions(ISymbol symbol, DiagnosticAnalyzer analyzer, ImmutableArray<SymbolStartAnalyzerAction> actions, HostSymbolStartAnalysisScope symbolScope, bool isGeneratedCodeSymbol, SyntaxTree? filterTree, TextSpan? filterSpan, CancellationToken cancellationToken)
|
|
{
|
|
if ((isGeneratedCodeSymbol && _shouldSkipAnalysisOnGeneratedCode(analyzer)) || IsAnalyzerSuppressedForSymbol(analyzer, symbol, cancellationToken))
|
|
{
|
|
return;
|
|
}
|
|
ImmutableArray<SymbolStartAnalyzerAction>.Enumerator enumerator = actions.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
SymbolStartAnalyzerAction current = enumerator.Current;
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
AnalyzerSymbolStartAnalysisContext item = new AnalyzerSymbolStartAnalysisContext(current.Analyzer, symbolScope, symbol, Compilation, AnalyzerOptions, isGeneratedCodeSymbol, filterTree, filterSpan, cancellationToken);
|
|
ExecuteAndCatchIfThrows(current.Analyzer, delegate((Action<SymbolStartAnalysisContext> action, AnalyzerSymbolStartAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, (current.Action, item), new AnalysisContextInfo(Compilation, symbol), cancellationToken);
|
|
}
|
|
}
|
|
|
|
public void ExecuteSuppressionAction(DiagnosticSuppressor suppressor, ImmutableArray<Diagnostic> reportedDiagnostics, CancellationToken cancellationToken)
|
|
{
|
|
if (!reportedDiagnostics.IsEmpty)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
Func<SuppressionDescriptor, bool> isSupportedSuppressionDescriptor = _analyzerManager.GetSupportedSuppressionDescriptors(suppressor, this, cancellationToken).Contains;
|
|
Action<SuppressionAnalysisContext> item = suppressor.ReportSuppressions;
|
|
ExecuteAndCatchIfThrows(argument: (item, new SuppressionAnalysisContext(Compilation, AnalyzerOptions, reportedDiagnostics, _addSuppression, isSupportedSuppressionDescriptor, _getSemanticModel, cancellationToken)), analyzer: suppressor, analyze: delegate((Action<SuppressionAnalysisContext> action, SuppressionAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, contextInfo: new AnalysisContextInfo(Compilation), cancellationToken: cancellationToken);
|
|
}
|
|
}
|
|
|
|
public void ExecuteCompilationActions(ImmutableArray<CompilationAnalyzerAction> compilationActions, DiagnosticAnalyzer analyzer, CompilationEvent compilationEvent, CancellationToken cancellationToken)
|
|
{
|
|
Action<Diagnostic> addCompilationDiagnostic = GetAddCompilationDiagnostic(analyzer, cancellationToken);
|
|
Func<Diagnostic, CancellationToken, bool> boundFunction;
|
|
using (PooledDelegates.GetPooledFunction((Diagnostic d, CancellationToken ct, (AnalyzerExecutor self, DiagnosticAnalyzer analyzer) arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), (this, analyzer), out boundFunction))
|
|
{
|
|
ImmutableArray<CompilationAnalyzerAction>.Enumerator enumerator = compilationActions.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
CompilationAnalyzerAction current = enumerator.Current;
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
CompilationAnalysisContext item = new CompilationAnalysisContext(Compilation, AnalyzerOptions, addCompilationDiagnostic, boundFunction, _compilationAnalysisValueProviderFactory, cancellationToken);
|
|
ExecuteAndCatchIfThrows(current.Analyzer, delegate((Action<CompilationAnalysisContext> action, CompilationAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, (current.Action, item), new AnalysisContextInfo(Compilation), cancellationToken);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ExecuteSymbolActions(ImmutableArray<SymbolAnalyzerAction> symbolActions, DiagnosticAnalyzer analyzer, SymbolDeclaredCompilationEvent symbolDeclaredEvent, Func<ISymbol, SyntaxReference, Compilation, CancellationToken, SyntaxNode> getTopMostNodeForAnalysis, bool isGeneratedCodeSymbol, SyntaxTree? filterTree, TextSpan? filterSpan, CancellationToken cancellationToken)
|
|
{
|
|
if ((isGeneratedCodeSymbol && _shouldSkipAnalysisOnGeneratedCode(analyzer)) || IsAnalyzerSuppressedForSymbol(analyzer, symbolDeclaredEvent.Symbol, cancellationToken))
|
|
{
|
|
return;
|
|
}
|
|
ISymbol symbol = symbolDeclaredEvent.Symbol;
|
|
Action<Diagnostic> addDiagnostic = GetAddDiagnostic(symbol, symbolDeclaredEvent.DeclaringSyntaxReferences, analyzer, getTopMostNodeForAnalysis, cancellationToken);
|
|
Func<Diagnostic, CancellationToken, bool> boundFunction;
|
|
using (PooledDelegates.GetPooledFunction((Diagnostic d, CancellationToken ct, (AnalyzerExecutor self, DiagnosticAnalyzer analyzer) arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), (this, analyzer), out boundFunction))
|
|
{
|
|
ImmutableArray<SymbolAnalyzerAction>.Enumerator enumerator = symbolActions.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
SymbolAnalyzerAction current = enumerator.Current;
|
|
Action<SymbolAnalysisContext> action = current.Action;
|
|
if (current.Kinds.Contains(symbol.Kind))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ExecuteAndCatchIfThrows(argument: (action, new SymbolAnalysisContext(symbol, Compilation, AnalyzerOptions, addDiagnostic, boundFunction, isGeneratedCodeSymbol, filterTree, filterSpan, cancellationToken)), analyzer: current.Analyzer, analyze: delegate((Action<SymbolAnalysisContext> action, SymbolAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, contextInfo: new AnalysisContextInfo(Compilation, symbol), cancellationToken: cancellationToken);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool TryExecuteSymbolEndActionsForContainer(INamespaceOrTypeSymbol containingSymbol, ISymbol processedMemberSymbol, DiagnosticAnalyzer analyzer, Func<ISymbol, SyntaxReference, Compilation, CancellationToken, SyntaxNode> getTopMostNodeForAnalysis, bool isGeneratedCode, SyntaxTree? filterTree, TextSpan? filterSpan, CancellationToken cancellationToken, [NotNullWhen(true)] out SymbolDeclaredCompilationEvent? containingSymbolDeclaredEvent)
|
|
{
|
|
containingSymbolDeclaredEvent = null;
|
|
if (!_analyzerManager.TryProcessCompletedMemberAndGetPendingSymbolEndActionsForContainer(containingSymbol, processedMemberSymbol, analyzer, out (ImmutableArray<SymbolEndAnalyzerAction>, SymbolDeclaredCompilationEvent) containerEndActionsAndEvent))
|
|
{
|
|
return false;
|
|
}
|
|
ImmutableArray<SymbolEndAnalyzerAction> symbolEndActions;
|
|
(symbolEndActions, containingSymbolDeclaredEvent) = containerEndActionsAndEvent;
|
|
ExecuteSymbolEndActionsCore(symbolEndActions, analyzer, containingSymbolDeclaredEvent, getTopMostNodeForAnalysis, isGeneratedCode, filterTree, filterSpan, cancellationToken);
|
|
return true;
|
|
}
|
|
|
|
public bool TryExecuteSymbolEndActions(ImmutableArray<SymbolEndAnalyzerAction> symbolEndActions, DiagnosticAnalyzer analyzer, SymbolDeclaredCompilationEvent symbolDeclaredEvent, Func<ISymbol, SyntaxReference, Compilation, CancellationToken, SyntaxNode> getTopMostNodeForAnalysis, bool isGeneratedCode, SyntaxTree? filterTree, TextSpan? filterSpan, CancellationToken cancellationToken)
|
|
{
|
|
if (!_analyzerManager.TryStartExecuteSymbolEndActions(symbolEndActions, analyzer, symbolDeclaredEvent))
|
|
{
|
|
return false;
|
|
}
|
|
ExecuteSymbolEndActionsCore(symbolEndActions, analyzer, symbolDeclaredEvent, getTopMostNodeForAnalysis, isGeneratedCode, filterTree, filterSpan, cancellationToken);
|
|
return true;
|
|
}
|
|
|
|
private void ExecuteSymbolEndActionsCore(ImmutableArray<SymbolEndAnalyzerAction> symbolEndActions, DiagnosticAnalyzer analyzer, SymbolDeclaredCompilationEvent symbolDeclaredEvent, Func<ISymbol, SyntaxReference, Compilation, CancellationToken, SyntaxNode> getTopMostNodeForAnalysis, bool isGeneratedCode, SyntaxTree? filterTree, TextSpan? filterSpan, CancellationToken cancellationToken)
|
|
{
|
|
ISymbol symbol = symbolDeclaredEvent.Symbol;
|
|
Action<Diagnostic> addDiagnostic = GetAddDiagnostic(symbol, symbolDeclaredEvent.DeclaringSyntaxReferences, analyzer, getTopMostNodeForAnalysis, cancellationToken);
|
|
Func<Diagnostic, CancellationToken, bool> boundFunction;
|
|
using (PooledDelegates.GetPooledFunction((Diagnostic d, CancellationToken ct, (AnalyzerExecutor self, DiagnosticAnalyzer analyzer) arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), (this, analyzer), out boundFunction))
|
|
{
|
|
ImmutableArray<SymbolEndAnalyzerAction>.Enumerator enumerator = symbolEndActions.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
SymbolEndAnalyzerAction current = enumerator.Current;
|
|
Action<SymbolAnalysisContext> action = current.Action;
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ExecuteAndCatchIfThrows(argument: (action, new SymbolAnalysisContext(symbol, Compilation, AnalyzerOptions, addDiagnostic, boundFunction, isGeneratedCode, filterTree, filterSpan, cancellationToken)), analyzer: current.Analyzer, analyze: delegate((Action<SymbolAnalysisContext> action, SymbolAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, contextInfo: new AnalysisContextInfo(Compilation, symbol), cancellationToken: cancellationToken);
|
|
}
|
|
_analyzerManager.MarkSymbolEndAnalysisComplete(symbol, analyzer);
|
|
}
|
|
}
|
|
|
|
public void ExecuteSemanticModelActions(ImmutableArray<SemanticModelAnalyzerAction> semanticModelActions, DiagnosticAnalyzer analyzer, SemanticModel semanticModel, TextSpan? filterSpan, bool isGeneratedCode, CancellationToken cancellationToken)
|
|
{
|
|
if ((isGeneratedCode && _shouldSkipAnalysisOnGeneratedCode(analyzer)) || IsAnalyzerSuppressedForTree(analyzer, semanticModel.SyntaxTree, cancellationToken))
|
|
{
|
|
return;
|
|
}
|
|
AnalyzerDiagnosticReporter addSemanticDiagnostic = GetAddSemanticDiagnostic(semanticModel.SyntaxTree, analyzer, cancellationToken);
|
|
Func<Diagnostic, CancellationToken, bool> boundFunction;
|
|
using (PooledDelegates.GetPooledFunction((Diagnostic d, CancellationToken ct, (AnalyzerExecutor self, DiagnosticAnalyzer analyzer) arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), (this, analyzer), out boundFunction))
|
|
{
|
|
ImmutableArray<SemanticModelAnalyzerAction>.Enumerator enumerator = semanticModelActions.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
SemanticModelAnalyzerAction current = enumerator.Current;
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
SemanticModelAnalysisContext item = new SemanticModelAnalysisContext(semanticModel, AnalyzerOptions, addSemanticDiagnostic.AddDiagnosticAction, boundFunction, filterSpan, isGeneratedCode, cancellationToken);
|
|
ExecuteAndCatchIfThrows(current.Analyzer, delegate((Action<SemanticModelAnalysisContext> action, SemanticModelAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, (current.Action, item), new AnalysisContextInfo(semanticModel), cancellationToken);
|
|
}
|
|
addSemanticDiagnostic.Free();
|
|
}
|
|
}
|
|
|
|
public void ExecuteSyntaxTreeActions(ImmutableArray<SyntaxTreeAnalyzerAction> syntaxTreeActions, DiagnosticAnalyzer analyzer, SourceOrAdditionalFile file, TextSpan? filterSpan, bool isGeneratedCode, CancellationToken cancellationToken)
|
|
{
|
|
SyntaxTree sourceTree = file.SourceTree;
|
|
if ((isGeneratedCode && _shouldSkipAnalysisOnGeneratedCode(analyzer)) || IsAnalyzerSuppressedForTree(analyzer, sourceTree, cancellationToken))
|
|
{
|
|
return;
|
|
}
|
|
AnalyzerDiagnosticReporter addSyntaxDiagnostic = GetAddSyntaxDiagnostic(file, analyzer, cancellationToken);
|
|
Func<Diagnostic, CancellationToken, bool> boundFunction;
|
|
using (PooledDelegates.GetPooledFunction((Diagnostic d, CancellationToken ct, (AnalyzerExecutor self, DiagnosticAnalyzer analyzer) arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), (this, analyzer), out boundFunction))
|
|
{
|
|
ImmutableArray<SyntaxTreeAnalyzerAction>.Enumerator enumerator = syntaxTreeActions.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
SyntaxTreeAnalyzerAction current = enumerator.Current;
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
SyntaxTreeAnalysisContext item = new SyntaxTreeAnalysisContext(sourceTree, AnalyzerOptions, addSyntaxDiagnostic.AddDiagnosticAction, boundFunction, Compilation, filterSpan, isGeneratedCode, cancellationToken);
|
|
ExecuteAndCatchIfThrows(current.Analyzer, delegate((Action<SyntaxTreeAnalysisContext> action, SyntaxTreeAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, (current.Action, item), new AnalysisContextInfo(Compilation, file), cancellationToken);
|
|
}
|
|
addSyntaxDiagnostic.Free();
|
|
}
|
|
}
|
|
|
|
public void ExecuteAdditionalFileActions(ImmutableArray<AdditionalFileAnalyzerAction> additionalFileActions, DiagnosticAnalyzer analyzer, SourceOrAdditionalFile file, TextSpan? filterSpan, CancellationToken cancellationToken)
|
|
{
|
|
AdditionalText additionalFile = file.AdditionalFile;
|
|
AnalyzerDiagnosticReporter addSyntaxDiagnostic = GetAddSyntaxDiagnostic(file, analyzer, cancellationToken);
|
|
Func<Diagnostic, CancellationToken, bool> boundFunction;
|
|
using (PooledDelegates.GetPooledFunction((Diagnostic d, CancellationToken ct, (AnalyzerExecutor self, DiagnosticAnalyzer analyzer) arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), (this, analyzer), out boundFunction))
|
|
{
|
|
ImmutableArray<AdditionalFileAnalyzerAction>.Enumerator enumerator = additionalFileActions.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
AdditionalFileAnalyzerAction current = enumerator.Current;
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
AdditionalFileAnalysisContext item = new AdditionalFileAnalysisContext(additionalFile, AnalyzerOptions, addSyntaxDiagnostic.AddDiagnosticAction, boundFunction, Compilation, filterSpan, cancellationToken);
|
|
ExecuteAndCatchIfThrows(current.Analyzer, delegate((Action<AdditionalFileAnalysisContext> action, AdditionalFileAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, (current.Action, item), new AnalysisContextInfo(Compilation, file), cancellationToken);
|
|
}
|
|
addSyntaxDiagnostic.Free();
|
|
}
|
|
}
|
|
|
|
private void ExecuteSyntaxNodeAction<TLanguageKindEnum>(SyntaxNodeAnalyzerAction<TLanguageKindEnum> syntaxNodeAction, SyntaxNode node, ISymbol containingSymbol, SemanticModel semanticModel, Action<Diagnostic> addDiagnostic, Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic, TextSpan? filterSpan, bool isGeneratedCode, CancellationToken cancellationToken) where TLanguageKindEnum : struct
|
|
{
|
|
SyntaxNodeAnalysisContext item = new SyntaxNodeAnalysisContext(node, containingSymbol, semanticModel, AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken);
|
|
ExecuteAndCatchIfThrows(syntaxNodeAction.Analyzer, delegate((Action<SyntaxNodeAnalysisContext> action, SyntaxNodeAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, (syntaxNodeAction.Action, item), new AnalysisContextInfo(Compilation, node), cancellationToken);
|
|
}
|
|
|
|
private void ExecuteOperationAction(OperationAnalyzerAction operationAction, IOperation operation, ISymbol containingSymbol, SemanticModel semanticModel, Action<Diagnostic> addDiagnostic, Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic, TextSpan? filterSpan, bool isGeneratedCode, CancellationToken cancellationToken)
|
|
{
|
|
OperationAnalysisContext item = new OperationAnalysisContext(operation, containingSymbol, semanticModel.Compilation, AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, GetControlFlowGraph, filterSpan, isGeneratedCode, cancellationToken);
|
|
ExecuteAndCatchIfThrows(operationAction.Analyzer, delegate((Action<OperationAnalysisContext> action, OperationAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, (operationAction.Action, item), new AnalysisContextInfo(Compilation, operation), cancellationToken);
|
|
}
|
|
|
|
public void ExecuteCodeBlockActions<TLanguageKindEnum>(IEnumerable<CodeBlockStartAnalyzerAction<TLanguageKindEnum>> codeBlockStartActions, IEnumerable<CodeBlockAnalyzerAction> codeBlockActions, IEnumerable<CodeBlockAnalyzerAction> codeBlockEndActions, DiagnosticAnalyzer analyzer, SyntaxNode declaredNode, ISymbol declaredSymbol, ImmutableArray<SyntaxNode> executableCodeBlocks, SemanticModel semanticModel, Func<SyntaxNode, TLanguageKindEnum> getKind, TextSpan? filterSpan, bool isGeneratedCode, CancellationToken cancellationToken) where TLanguageKindEnum : struct
|
|
{
|
|
ExecuteBlockActionsCore<CodeBlockStartAnalyzerAction<TLanguageKindEnum>, CodeBlockAnalyzerAction, SyntaxNodeAnalyzerAction<TLanguageKindEnum>, SyntaxNode, TLanguageKindEnum>(codeBlockStartActions, codeBlockActions, codeBlockEndActions, analyzer, declaredNode, declaredSymbol, executableCodeBlocks, (ImmutableArray<SyntaxNode> codeBlocks) => codeBlocks.SelectMany(delegate(SyntaxNode cb)
|
|
{
|
|
Func<SyntaxNode, bool> syntaxNodesToAnalyzeFilter = semanticModel.GetSyntaxNodesToAnalyzeFilter(cb, declaredSymbol);
|
|
return (syntaxNodesToAnalyzeFilter != null) ? cb.DescendantNodesAndSelf(syntaxNodesToAnalyzeFilter).Where(syntaxNodesToAnalyzeFilter) : cb.DescendantNodesAndSelf();
|
|
}), semanticModel, getKind, filterSpan, isGeneratedCode, cancellationToken);
|
|
}
|
|
|
|
public void ExecuteOperationBlockActions(IEnumerable<OperationBlockStartAnalyzerAction> operationBlockStartActions, IEnumerable<OperationBlockAnalyzerAction> operationBlockActions, IEnumerable<OperationBlockAnalyzerAction> operationBlockEndActions, DiagnosticAnalyzer analyzer, SyntaxNode declaredNode, ISymbol declaredSymbol, ImmutableArray<IOperation> operationBlocks, ImmutableArray<IOperation> operations, SemanticModel semanticModel, TextSpan? filterSpan, bool isGeneratedCode, CancellationToken cancellationToken)
|
|
{
|
|
ExecuteBlockActionsCore<OperationBlockStartAnalyzerAction, OperationBlockAnalyzerAction, OperationAnalyzerAction, IOperation, int>(operationBlockStartActions, operationBlockActions, operationBlockEndActions, analyzer, declaredNode, declaredSymbol, operationBlocks, (ImmutableArray<IOperation> blocks) => operations, semanticModel, null, filterSpan, isGeneratedCode, cancellationToken);
|
|
}
|
|
|
|
private void ExecuteBlockActionsCore<TBlockStartAction, TBlockAction, TNodeAction, TNode, TLanguageKindEnum>(IEnumerable<TBlockStartAction> startActions, IEnumerable<TBlockAction> actions, IEnumerable<TBlockAction> endActions, DiagnosticAnalyzer analyzer, SyntaxNode declaredNode, ISymbol declaredSymbol, ImmutableArray<TNode> executableBlocks, Func<ImmutableArray<TNode>, IEnumerable<TNode>> getNodesToAnalyze, SemanticModel semanticModel, Func<SyntaxNode, TLanguageKindEnum>? getKind, TextSpan? filterSpan, bool isGeneratedCode, CancellationToken cancellationToken) where TBlockStartAction : AnalyzerAction where TBlockAction : AnalyzerAction where TNodeAction : AnalyzerAction where TLanguageKindEnum : struct
|
|
{
|
|
if ((isGeneratedCode && _shouldSkipAnalysisOnGeneratedCode(analyzer)) || IsAnalyzerSuppressedForTree(analyzer, declaredNode.SyntaxTree, cancellationToken))
|
|
{
|
|
return;
|
|
}
|
|
PooledHashSet<TBlockAction> instance = PooledHashSet<TBlockAction>.GetInstance();
|
|
PooledHashSet<TBlockAction> instance2 = PooledHashSet<TBlockAction>.GetInstance();
|
|
ArrayBuilder<TNodeAction> instance3 = ArrayBuilder<TNodeAction>.GetInstance();
|
|
ArrayBuilder<SyntaxNodeAnalyzerAction<TLanguageKindEnum>> arrayBuilder = instance3 as ArrayBuilder<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>;
|
|
ArrayBuilder<OperationAnalyzerAction> arrayBuilder2 = instance3 as ArrayBuilder<OperationAnalyzerAction>;
|
|
ImmutableArray<IOperation> operationBlocks = ((executableBlocks[0] is IOperation) ? ((ImmutableArray<IOperation>)(object)executableBlocks) : ImmutableArray<IOperation>.Empty);
|
|
instance2.AddAll(actions);
|
|
instance.AddAll(endActions);
|
|
AnalyzerDiagnosticReporter addSemanticDiagnostic = GetAddSemanticDiagnostic(semanticModel.SyntaxTree, declaredNode.FullSpan, analyzer, cancellationToken);
|
|
foreach (TBlockStartAction startAction in startActions)
|
|
{
|
|
if (startAction is CodeBlockStartAnalyzerAction<TLanguageKindEnum> codeBlockStartAnalyzerAction)
|
|
{
|
|
PooledHashSet<CodeBlockAnalyzerAction> item = instance as PooledHashSet<CodeBlockAnalyzerAction>;
|
|
HostCodeBlockStartAnalysisScope<TLanguageKindEnum> hostCodeBlockStartAnalysisScope = new HostCodeBlockStartAnalysisScope<TLanguageKindEnum>();
|
|
AnalyzerCodeBlockStartAnalysisContext<TLanguageKindEnum> item2 = new AnalyzerCodeBlockStartAnalysisContext<TLanguageKindEnum>(startAction.Analyzer, hostCodeBlockStartAnalysisScope, declaredNode, declaredSymbol, semanticModel, AnalyzerOptions, filterSpan, isGeneratedCode, cancellationToken);
|
|
ExecuteAndCatchIfThrows(startAction.Analyzer, delegate((Action<CodeBlockStartAnalysisContext<TLanguageKindEnum>> action, AnalyzerCodeBlockStartAnalysisContext<TLanguageKindEnum> context, HostCodeBlockStartAnalysisScope<TLanguageKindEnum> scope, PooledHashSet<CodeBlockAnalyzerAction> blockEndActions, ArrayBuilder<SyntaxNodeAnalyzerAction<TLanguageKindEnum>> syntaxNodeActions) data)
|
|
{
|
|
data.action(data.context);
|
|
data.blockEndActions?.AddAll(data.scope.CodeBlockEndActions);
|
|
data.syntaxNodeActions?.AddRange(data.scope.SyntaxNodeActions);
|
|
}, (codeBlockStartAnalyzerAction.Action, item2, hostCodeBlockStartAnalysisScope, item, arrayBuilder), new AnalysisContextInfo(Compilation, declaredSymbol, declaredNode), cancellationToken);
|
|
}
|
|
else if (startAction is OperationBlockStartAnalyzerAction operationBlockStartAnalyzerAction)
|
|
{
|
|
PooledHashSet<OperationBlockAnalyzerAction> item3 = instance as PooledHashSet<OperationBlockAnalyzerAction>;
|
|
HostOperationBlockStartAnalysisScope hostOperationBlockStartAnalysisScope = new HostOperationBlockStartAnalysisScope();
|
|
AnalyzerOperationBlockStartAnalysisContext item4 = new AnalyzerOperationBlockStartAnalysisContext(startAction.Analyzer, hostOperationBlockStartAnalysisScope, operationBlocks, declaredSymbol, semanticModel.Compilation, AnalyzerOptions, GetControlFlowGraph, declaredNode.SyntaxTree, filterSpan, isGeneratedCode, cancellationToken);
|
|
ExecuteAndCatchIfThrows(startAction.Analyzer, delegate((Action<OperationBlockStartAnalysisContext> action, AnalyzerOperationBlockStartAnalysisContext context, HostOperationBlockStartAnalysisScope scope, PooledHashSet<OperationBlockAnalyzerAction> blockEndActions, ArrayBuilder<OperationAnalyzerAction> operationActions) data)
|
|
{
|
|
data.action(data.context);
|
|
data.blockEndActions?.AddAll(data.scope.OperationBlockEndActions);
|
|
data.operationActions?.AddRange(data.scope.OperationActions);
|
|
}, (operationBlockStartAnalyzerAction.Action, item4, hostOperationBlockStartAnalysisScope, item3, arrayBuilder2), new AnalysisContextInfo(Compilation, declaredSymbol), cancellationToken);
|
|
}
|
|
}
|
|
Func<Diagnostic, CancellationToken, bool> boundFunction;
|
|
using (PooledDelegates.GetPooledFunction((Diagnostic d, CancellationToken ct, (AnalyzerExecutor self, DiagnosticAnalyzer analyzer) arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), (this, analyzer), out boundFunction))
|
|
{
|
|
if (instance3.Any())
|
|
{
|
|
if (arrayBuilder != null)
|
|
{
|
|
ImmutableSegmentedDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>> nodeActionsByKind = GetNodeActionsByKind(arrayBuilder);
|
|
IEnumerable<SyntaxNode> nodesToAnalyze = (IEnumerable<SyntaxNode>)getNodesToAnalyze(executableBlocks);
|
|
ExecuteSyntaxNodeActions(nodesToAnalyze, nodeActionsByKind, analyzer, declaredSymbol, semanticModel, getKind, addSemanticDiagnostic, boundFunction, filterSpan, isGeneratedCode, startActions.Any(), cancellationToken);
|
|
}
|
|
else if (arrayBuilder2 != null)
|
|
{
|
|
ImmutableSegmentedDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>> operationActionsByKind = GetOperationActionsByKind(arrayBuilder2);
|
|
IEnumerable<IOperation> operationsToAnalyze = (IEnumerable<IOperation>)getNodesToAnalyze(executableBlocks);
|
|
ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, declaredSymbol, semanticModel, addSemanticDiagnostic, boundFunction, filterSpan, isGeneratedCode, startActions.Any(), cancellationToken);
|
|
}
|
|
}
|
|
instance3.Free();
|
|
ExecuteBlockActions(instance2, declaredNode, declaredSymbol, analyzer, semanticModel, operationBlocks, addSemanticDiagnostic.AddDiagnosticAction, boundFunction, filterSpan, isGeneratedCode, cancellationToken);
|
|
ExecuteBlockActions(instance, declaredNode, declaredSymbol, analyzer, semanticModel, operationBlocks, addSemanticDiagnostic.AddDiagnosticAction, boundFunction, filterSpan, isGeneratedCode, cancellationToken);
|
|
addSemanticDiagnostic.Free();
|
|
}
|
|
}
|
|
|
|
private void ExecuteBlockActions<TBlockAction>(PooledHashSet<TBlockAction> blockActions, SyntaxNode declaredNode, ISymbol declaredSymbol, DiagnosticAnalyzer analyzer, SemanticModel semanticModel, ImmutableArray<IOperation> operationBlocks, Action<Diagnostic> addDiagnostic, Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic, TextSpan? filterSpan, bool isGeneratedCode, CancellationToken cancellationToken) where TBlockAction : AnalyzerAction
|
|
{
|
|
foreach (TBlockAction blockAction in blockActions)
|
|
{
|
|
if (blockAction is CodeBlockAnalyzerAction codeBlockAnalyzerAction)
|
|
{
|
|
CodeBlockAnalysisContext item = new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken);
|
|
ExecuteAndCatchIfThrows(codeBlockAnalyzerAction.Analyzer, delegate((Action<CodeBlockAnalysisContext> action, CodeBlockAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, (codeBlockAnalyzerAction.Action, item), new AnalysisContextInfo(Compilation, declaredSymbol, declaredNode), cancellationToken);
|
|
}
|
|
else if (blockAction is OperationBlockAnalyzerAction operationBlockAnalyzerAction)
|
|
{
|
|
OperationBlockAnalysisContext item2 = new OperationBlockAnalysisContext(operationBlocks, declaredSymbol, semanticModel.Compilation, AnalyzerOptions, addDiagnostic, isSupportedDiagnostic, GetControlFlowGraph, declaredNode.SyntaxTree, filterSpan, isGeneratedCode, cancellationToken);
|
|
ExecuteAndCatchIfThrows(operationBlockAnalyzerAction.Analyzer, delegate((Action<OperationBlockAnalysisContext> action, OperationBlockAnalysisContext context) data)
|
|
{
|
|
data.action(data.context);
|
|
}, (operationBlockAnalyzerAction.Action, item2), new AnalysisContextInfo(Compilation, declaredSymbol), cancellationToken);
|
|
}
|
|
}
|
|
blockActions.Free();
|
|
}
|
|
|
|
internal static ImmutableSegmentedDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>> GetNodeActionsByKind<TLanguageKindEnum>(IEnumerable<SyntaxNodeAnalyzerAction<TLanguageKindEnum>> nodeActions) where TLanguageKindEnum : struct
|
|
{
|
|
PooledDictionary<TLanguageKindEnum, ArrayBuilder<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>> instance = PooledDictionary<TLanguageKindEnum, ArrayBuilder<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>>.GetInstance();
|
|
foreach (SyntaxNodeAnalyzerAction<TLanguageKindEnum> nodeAction in nodeActions)
|
|
{
|
|
ImmutableArray<TLanguageKindEnum>.Enumerator enumerator2 = nodeAction.Kinds.GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
TLanguageKindEnum current2 = enumerator2.Current;
|
|
if (!instance.TryGetValue(current2, out var value))
|
|
{
|
|
instance.Add(current2, value = ArrayBuilder<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>.GetInstance());
|
|
}
|
|
value.Add(nodeAction);
|
|
}
|
|
}
|
|
ImmutableSegmentedDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>> result = ImmutableSegmentedDictionary.CreateRange(instance.Select((KeyValuePair<TLanguageKindEnum, ArrayBuilder<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>> kvp) => KeyValuePairUtil.Create(kvp.Key, kvp.Value.ToImmutableAndFree())));
|
|
instance.Free();
|
|
return result;
|
|
}
|
|
|
|
public void ExecuteSyntaxNodeActions<TLanguageKindEnum>(IEnumerable<SyntaxNode> nodesToAnalyze, ImmutableSegmentedDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>> nodeActionsByKind, DiagnosticAnalyzer analyzer, SemanticModel model, Func<SyntaxNode, TLanguageKindEnum> getKind, TextSpan spanForContainingTopmostNodeForAnalysis, ISymbol declaredSymbol, TextSpan? filterSpan, bool isGeneratedCode, bool hasCodeBlockStartOrSymbolStartActions, CancellationToken cancellationToken) where TLanguageKindEnum : struct
|
|
{
|
|
if ((isGeneratedCode && _shouldSkipAnalysisOnGeneratedCode(analyzer)) || IsAnalyzerSuppressedForTree(analyzer, model.SyntaxTree, cancellationToken))
|
|
{
|
|
return;
|
|
}
|
|
AnalyzerDiagnosticReporter addSemanticDiagnostic = GetAddSemanticDiagnostic(model.SyntaxTree, spanForContainingTopmostNodeForAnalysis, analyzer, cancellationToken);
|
|
Func<Diagnostic, CancellationToken, bool> boundFunction;
|
|
using (PooledDelegates.GetPooledFunction((Diagnostic d, CancellationToken ct, (AnalyzerExecutor self, DiagnosticAnalyzer analyzer) arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), (this, analyzer), out boundFunction))
|
|
{
|
|
ExecuteSyntaxNodeActions(nodesToAnalyze, nodeActionsByKind, analyzer, declaredSymbol, model, getKind, addSemanticDiagnostic, boundFunction, filterSpan, isGeneratedCode, hasCodeBlockStartOrSymbolStartActions, cancellationToken);
|
|
addSemanticDiagnostic.Free();
|
|
}
|
|
}
|
|
|
|
private void ExecuteSyntaxNodeActions<TLanguageKindEnum>(IEnumerable<SyntaxNode> nodesToAnalyze, ImmutableSegmentedDictionary<TLanguageKindEnum, ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>> nodeActionsByKind, DiagnosticAnalyzer analyzer, ISymbol containingSymbol, SemanticModel model, Func<SyntaxNode, TLanguageKindEnum> getKind, AnalyzerDiagnosticReporter diagReporter, Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic, TextSpan? filterSpan, bool isGeneratedCode, bool hasCodeBlockStartOrSymbolStartActions, CancellationToken cancellationToken) where TLanguageKindEnum : struct
|
|
{
|
|
foreach (SyntaxNode item in nodesToAnalyze)
|
|
{
|
|
if (nodeActionsByKind.TryGetValue(getKind(item), out ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>> value) && ShouldExecuteNode(item, analyzer, cancellationToken))
|
|
{
|
|
if (!hasCodeBlockStartOrSymbolStartActions)
|
|
{
|
|
diagReporter.FilterSpanForLocalDiagnostics = item.FullSpan;
|
|
}
|
|
ImmutableArray<SyntaxNodeAnalyzerAction<TLanguageKindEnum>>.Enumerator enumerator2 = value.GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
SyntaxNodeAnalyzerAction<TLanguageKindEnum> current2 = enumerator2.Current;
|
|
ExecuteSyntaxNodeAction(current2, item, containingSymbol, model, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static ImmutableSegmentedDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>> GetOperationActionsByKind(IEnumerable<OperationAnalyzerAction> operationActions)
|
|
{
|
|
PooledDictionary<OperationKind, ArrayBuilder<OperationAnalyzerAction>> instance = PooledDictionary<OperationKind, ArrayBuilder<OperationAnalyzerAction>>.GetInstance();
|
|
foreach (OperationAnalyzerAction operationAction in operationActions)
|
|
{
|
|
ImmutableArray<OperationKind>.Enumerator enumerator2 = operationAction.Kinds.GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
OperationKind current2 = enumerator2.Current;
|
|
if (!instance.TryGetValue(current2, out var value))
|
|
{
|
|
instance.Add(current2, value = ArrayBuilder<OperationAnalyzerAction>.GetInstance());
|
|
}
|
|
value.Add(operationAction);
|
|
}
|
|
}
|
|
ImmutableSegmentedDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>> result = ImmutableSegmentedDictionary.CreateRange(instance.Select((KeyValuePair<OperationKind, ArrayBuilder<OperationAnalyzerAction>> kvp) => KeyValuePairUtil.Create(kvp.Key, kvp.Value.ToImmutableAndFree())));
|
|
instance.Free();
|
|
return result;
|
|
}
|
|
|
|
public void ExecuteOperationActions(IEnumerable<IOperation> operationsToAnalyze, ImmutableSegmentedDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>> operationActionsByKind, DiagnosticAnalyzer analyzer, SemanticModel model, TextSpan spanForContainingOperationBlock, ISymbol declaredSymbol, TextSpan? filterSpan, bool isGeneratedCode, bool hasOperationBlockStartOrSymbolStartActions, CancellationToken cancellationToken)
|
|
{
|
|
if ((isGeneratedCode && _shouldSkipAnalysisOnGeneratedCode(analyzer)) || IsAnalyzerSuppressedForTree(analyzer, model.SyntaxTree, cancellationToken))
|
|
{
|
|
return;
|
|
}
|
|
AnalyzerDiagnosticReporter addSemanticDiagnostic = GetAddSemanticDiagnostic(model.SyntaxTree, spanForContainingOperationBlock, analyzer, cancellationToken);
|
|
Func<Diagnostic, CancellationToken, bool> boundFunction;
|
|
using (PooledDelegates.GetPooledFunction((Diagnostic d, CancellationToken ct, (AnalyzerExecutor self, DiagnosticAnalyzer analyzer) arg) => arg.self.IsSupportedDiagnostic(arg.analyzer, d, ct), (this, analyzer), out boundFunction))
|
|
{
|
|
ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, declaredSymbol, model, addSemanticDiagnostic, boundFunction, filterSpan, isGeneratedCode, hasOperationBlockStartOrSymbolStartActions, cancellationToken);
|
|
addSemanticDiagnostic.Free();
|
|
}
|
|
}
|
|
|
|
private void ExecuteOperationActions(IEnumerable<IOperation> operationsToAnalyze, ImmutableSegmentedDictionary<OperationKind, ImmutableArray<OperationAnalyzerAction>> operationActionsByKind, DiagnosticAnalyzer analyzer, ISymbol containingSymbol, SemanticModel model, AnalyzerDiagnosticReporter diagReporter, Func<Diagnostic, CancellationToken, bool> isSupportedDiagnostic, TextSpan? filterSpan, bool isGeneratedCode, bool hasOperationBlockStartOrSymbolStartActions, CancellationToken cancellationToken)
|
|
{
|
|
foreach (IOperation item in operationsToAnalyze)
|
|
{
|
|
if (operationActionsByKind.TryGetValue(item.Kind, out ImmutableArray<OperationAnalyzerAction> value) && ShouldExecuteOperation(item, analyzer, cancellationToken))
|
|
{
|
|
if (!hasOperationBlockStartOrSymbolStartActions)
|
|
{
|
|
diagReporter.FilterSpanForLocalDiagnostics = item.Syntax.FullSpan;
|
|
}
|
|
ImmutableArray<OperationAnalyzerAction>.Enumerator enumerator2 = value.GetEnumerator();
|
|
while (enumerator2.MoveNext())
|
|
{
|
|
OperationAnalyzerAction current2 = enumerator2.Current;
|
|
ExecuteOperationAction(current2, item, containingSymbol, model, diagReporter.AddDiagnosticAction, isSupportedDiagnostic, filterSpan, isGeneratedCode, cancellationToken);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static bool CanHaveExecutableCodeBlock(ISymbol symbol)
|
|
{
|
|
switch (symbol.Kind)
|
|
{
|
|
case SymbolKind.Event:
|
|
case SymbolKind.Method:
|
|
case SymbolKind.NamedType:
|
|
case SymbolKind.Namespace:
|
|
case SymbolKind.Property:
|
|
return true;
|
|
case SymbolKind.Field:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
internal void ExecuteAndCatchIfThrows<TArg>(DiagnosticAnalyzer analyzer, Action<TArg> analyze, TArg argument, AnalysisContextInfo? contextInfo, CancellationToken cancellationToken)
|
|
{
|
|
SharedStopwatch sharedStopwatch = default(SharedStopwatch);
|
|
if (_analyzerExecutionTimeMap != null)
|
|
{
|
|
sharedStopwatch = SharedStopwatch.StartNew();
|
|
}
|
|
object obj = _getAnalyzerGate(analyzer);
|
|
if (obj != null)
|
|
{
|
|
lock (obj)
|
|
{
|
|
ExecuteAndCatchIfThrows_NoLock(analyzer, analyze, argument, contextInfo, cancellationToken);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ExecuteAndCatchIfThrows_NoLock(analyzer, analyze, argument, contextInfo, cancellationToken);
|
|
}
|
|
if (_analyzerExecutionTimeMap != null)
|
|
{
|
|
long ticks = sharedStopwatch.Elapsed.Ticks;
|
|
Interlocked.Add(ref _analyzerExecutionTimeMap.GetOrAdd(analyzer, (DiagnosticAnalyzer _) => new StrongBox<long>(0L)).Value, ticks);
|
|
}
|
|
}
|
|
|
|
private void ExecuteAndCatchIfThrows_NoLock<TArg>(DiagnosticAnalyzer analyzer, Action<TArg> analyze, TArg argument, AnalysisContextInfo? info, CancellationToken cancellationToken)
|
|
{
|
|
try
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
analyze(argument);
|
|
}
|
|
catch (Exception exception) when (HandleAnalyzerException(exception, analyzer, info, OnAnalyzerException, _analyzerExceptionFilter, cancellationToken))
|
|
{
|
|
}
|
|
}
|
|
|
|
internal static bool HandleAnalyzerException(Exception exception, DiagnosticAnalyzer analyzer, AnalysisContextInfo? info, Action<Exception, DiagnosticAnalyzer, Diagnostic, CancellationToken> onAnalyzerException, Func<Exception, bool>? analyzerExceptionFilter, CancellationToken cancellationToken)
|
|
{
|
|
if (!ExceptionFilter(exception, analyzerExceptionFilter, cancellationToken))
|
|
{
|
|
return false;
|
|
}
|
|
Diagnostic arg = CreateAnalyzerExceptionDiagnostic(analyzer, exception, info);
|
|
try
|
|
{
|
|
onAnalyzerException(exception, analyzer, arg, cancellationToken);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
}
|
|
return true;
|
|
static bool ExceptionFilter(Exception ex2, Func<Exception, bool>? func, CancellationToken cancellationToken2)
|
|
{
|
|
OperationCanceledException obj = ex2 as OperationCanceledException;
|
|
if (obj != null && obj.CancellationToken == cancellationToken2)
|
|
{
|
|
return false;
|
|
}
|
|
return func?.Invoke(ex2) ?? true;
|
|
}
|
|
}
|
|
|
|
internal static Diagnostic CreateAnalyzerExceptionDiagnostic(DiagnosticAnalyzer analyzer, Exception e, AnalysisContextInfo? info = null)
|
|
{
|
|
string text = analyzer.ToString();
|
|
string compilerAnalyzerFailure = CodeAnalysisResources.CompilerAnalyzerFailure;
|
|
string compilerAnalyzerThrows = CodeAnalysisResources.CompilerAnalyzerThrows;
|
|
string text2 = string.Join(Environment.NewLine, new string[2]
|
|
{
|
|
CreateDiagnosticDescription(info, e),
|
|
CreateDisablingMessage(analyzer, text)
|
|
}).Trim();
|
|
string[] array = new string[4]
|
|
{
|
|
text,
|
|
e.GetType().ToString(),
|
|
e.Message,
|
|
text2
|
|
};
|
|
DiagnosticDescriptor analyzerExceptionDiagnosticDescriptor = GetAnalyzerExceptionDiagnosticDescriptor("AD0001", compilerAnalyzerFailure, compilerAnalyzerThrows);
|
|
Location none = Location.None;
|
|
object[] messageArgs = array;
|
|
return Diagnostic.Create(analyzerExceptionDiagnosticDescriptor, none, messageArgs);
|
|
}
|
|
|
|
private static string CreateDiagnosticDescription(AnalysisContextInfo? info, Exception e)
|
|
{
|
|
if (!info.HasValue)
|
|
{
|
|
return e.CreateDiagnosticDescription();
|
|
}
|
|
return string.Join(Environment.NewLine, new string[2]
|
|
{
|
|
string.Format(CodeAnalysisResources.ExceptionContext, info?.GetContext()),
|
|
e.CreateDiagnosticDescription()
|
|
});
|
|
}
|
|
|
|
private static string CreateDisablingMessage(DiagnosticAnalyzer analyzer, string analyzerName)
|
|
{
|
|
ImmutableSortedSet<string> immutableSortedSet = ImmutableSortedSet<string>.Empty.WithComparer(StringComparer.OrdinalIgnoreCase);
|
|
try
|
|
{
|
|
ImmutableArray<DiagnosticDescriptor>.Enumerator enumerator = analyzer.SupportedDiagnostics.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
DiagnosticDescriptor current = enumerator.Current;
|
|
if (current != null)
|
|
{
|
|
immutableSortedSet = immutableSortedSet.Add(current.Id);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return string.Format(CodeAnalysisResources.CompilerAnalyzerThrows, new object[4]
|
|
{
|
|
analyzerName,
|
|
ex.GetType().ToString(),
|
|
ex.Message,
|
|
ex.CreateDiagnosticDescription()
|
|
});
|
|
}
|
|
if (immutableSortedSet.IsEmpty)
|
|
{
|
|
return "";
|
|
}
|
|
return string.Format(CodeAnalysisResources.DisableAnalyzerDiagnosticsMessage, string.Join(", ", immutableSortedSet));
|
|
}
|
|
|
|
internal static Diagnostic CreateDriverExceptionDiagnostic(Exception e)
|
|
{
|
|
string analyzerDriverFailure = CodeAnalysisResources.AnalyzerDriverFailure;
|
|
string analyzerDriverThrows = CodeAnalysisResources.AnalyzerDriverThrows;
|
|
string[] array = new string[3]
|
|
{
|
|
e.GetType().ToString(),
|
|
e.Message,
|
|
e.CreateDiagnosticDescription()
|
|
};
|
|
DiagnosticDescriptor analyzerExceptionDiagnosticDescriptor = GetAnalyzerExceptionDiagnosticDescriptor("AD0002", analyzerDriverFailure, analyzerDriverThrows);
|
|
Location none = Location.None;
|
|
object[] messageArgs = array;
|
|
return Diagnostic.Create(analyzerExceptionDiagnosticDescriptor, none, messageArgs);
|
|
}
|
|
|
|
internal static DiagnosticDescriptor GetAnalyzerExceptionDiagnosticDescriptor(string? id = null, string? title = null, string? messageFormat = null)
|
|
{
|
|
if (id == null)
|
|
{
|
|
id = "AD0001";
|
|
}
|
|
if (title == null)
|
|
{
|
|
title = CodeAnalysisResources.CompilerAnalyzerFailure;
|
|
}
|
|
if (messageFormat == null)
|
|
{
|
|
messageFormat = CodeAnalysisResources.CompilerAnalyzerThrows;
|
|
}
|
|
return new DiagnosticDescriptor(id, title, messageFormat, "Compiler", DiagnosticSeverity.Warning, true, null, null, "AnalyzerException");
|
|
}
|
|
|
|
internal static bool IsAnalyzerExceptionDiagnostic(Diagnostic diagnostic)
|
|
{
|
|
if (diagnostic.Id == "AD0001" || diagnostic.Id == "AD0002")
|
|
{
|
|
ImmutableArray<string>.Enumerator enumerator = diagnostic.Descriptor.ImmutableCustomTags.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
if (enumerator.Current == "AnalyzerException")
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal static bool AreEquivalentAnalyzerExceptionDiagnostics(Diagnostic exceptionDiagnostic, Diagnostic other)
|
|
{
|
|
if (!IsAnalyzerExceptionDiagnostic(other))
|
|
{
|
|
return false;
|
|
}
|
|
if (exceptionDiagnostic.Id == other.Id && exceptionDiagnostic.Severity == other.Severity)
|
|
{
|
|
return exceptionDiagnostic.GetMessage() == other.GetMessage();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private bool IsSupportedDiagnostic(DiagnosticAnalyzer analyzer, Diagnostic diagnostic, CancellationToken cancellationToken)
|
|
{
|
|
if (diagnostic is DiagnosticWithInfo)
|
|
{
|
|
return true;
|
|
}
|
|
return _analyzerManager.IsSupportedDiagnostic(analyzer, diagnostic, _isCompilerAnalyzer, this, cancellationToken);
|
|
}
|
|
|
|
private Action<Diagnostic> GetAddDiagnostic(ISymbol contextSymbol, ImmutableArray<SyntaxReference> cachedDeclaringReferences, DiagnosticAnalyzer analyzer, Func<ISymbol, SyntaxReference, Compilation, CancellationToken, SyntaxNode> getTopMostNodeForAnalysis, CancellationToken cancellationToken)
|
|
{
|
|
return GetAddDiagnostic(contextSymbol, cachedDeclaringReferences, Compilation, analyzer, _addNonCategorizedDiagnostic, _addCategorizedLocalDiagnostic, _addCategorizedNonLocalDiagnostic, getTopMostNodeForAnalysis, _shouldSuppressGeneratedCodeDiagnostic, cancellationToken);
|
|
}
|
|
|
|
private static Action<Diagnostic> GetAddDiagnostic(ISymbol contextSymbol, ImmutableArray<SyntaxReference> cachedDeclaringReferences, Compilation compilation, DiagnosticAnalyzer analyzer, Action<Diagnostic, CancellationToken>? addNonCategorizedDiagnostic, Action<Diagnostic, DiagnosticAnalyzer, bool, CancellationToken>? addCategorizedLocalDiagnostic, Action<Diagnostic, DiagnosticAnalyzer, CancellationToken>? addCategorizedNonLocalDiagnostic, Func<ISymbol, SyntaxReference, Compilation, CancellationToken, SyntaxNode> getTopMostNodeForAnalysis, Func<Diagnostic, DiagnosticAnalyzer, Compilation, CancellationToken, bool> shouldSuppressGeneratedCodeDiagnostic, CancellationToken cancellationToken)
|
|
{
|
|
return delegate(Diagnostic diagnostic)
|
|
{
|
|
if (!shouldSuppressGeneratedCodeDiagnostic(diagnostic, analyzer, compilation, cancellationToken))
|
|
{
|
|
if (addCategorizedLocalDiagnostic == null)
|
|
{
|
|
addNonCategorizedDiagnostic(diagnostic, cancellationToken);
|
|
}
|
|
else
|
|
{
|
|
if (diagnostic.Location.IsInSource)
|
|
{
|
|
ImmutableArray<SyntaxReference>.Enumerator enumerator = cachedDeclaringReferences.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
SyntaxReference current = enumerator.Current;
|
|
if (current.SyntaxTree == diagnostic.Location.SourceTree)
|
|
{
|
|
SyntaxNode syntaxNode = getTopMostNodeForAnalysis(contextSymbol, current, compilation, cancellationToken);
|
|
if (diagnostic.Location.SourceSpan.IntersectsWith(syntaxNode.FullSpan))
|
|
{
|
|
addCategorizedLocalDiagnostic(diagnostic, analyzer, arg3: false, cancellationToken);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
addCategorizedNonLocalDiagnostic(diagnostic, analyzer, cancellationToken);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private Action<Diagnostic> GetAddCompilationDiagnostic(DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
|
|
{
|
|
return delegate(Diagnostic diagnostic)
|
|
{
|
|
if (!_shouldSuppressGeneratedCodeDiagnostic(diagnostic, analyzer, Compilation, cancellationToken))
|
|
{
|
|
if (_addCategorizedNonLocalDiagnostic == null)
|
|
{
|
|
_addNonCategorizedDiagnostic(diagnostic, cancellationToken);
|
|
}
|
|
else
|
|
{
|
|
_addCategorizedNonLocalDiagnostic(diagnostic, analyzer, cancellationToken);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private AnalyzerDiagnosticReporter GetAddSemanticDiagnostic(SyntaxTree tree, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
|
|
{
|
|
return AnalyzerDiagnosticReporter.GetInstance(new SourceOrAdditionalFile(tree), null, Compilation, analyzer, isSyntaxDiagnostic: false, _addNonCategorizedDiagnostic, _addCategorizedLocalDiagnostic, _addCategorizedNonLocalDiagnostic, _shouldSuppressGeneratedCodeDiagnostic, cancellationToken);
|
|
}
|
|
|
|
private AnalyzerDiagnosticReporter GetAddSemanticDiagnostic(SyntaxTree tree, TextSpan? span, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
|
|
{
|
|
return AnalyzerDiagnosticReporter.GetInstance(new SourceOrAdditionalFile(tree), span, Compilation, analyzer, isSyntaxDiagnostic: false, _addNonCategorizedDiagnostic, _addCategorizedLocalDiagnostic, _addCategorizedNonLocalDiagnostic, _shouldSuppressGeneratedCodeDiagnostic, cancellationToken);
|
|
}
|
|
|
|
private AnalyzerDiagnosticReporter GetAddSyntaxDiagnostic(SourceOrAdditionalFile file, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
|
|
{
|
|
return AnalyzerDiagnosticReporter.GetInstance(file, null, Compilation, analyzer, isSyntaxDiagnostic: true, _addNonCategorizedDiagnostic, _addCategorizedLocalDiagnostic, _addCategorizedNonLocalDiagnostic, _shouldSuppressGeneratedCodeDiagnostic, cancellationToken);
|
|
}
|
|
|
|
private bool ShouldExecuteNode(SyntaxNode node, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
|
|
{
|
|
if (_shouldSkipAnalysisOnGeneratedCode(analyzer) && _isGeneratedCodeLocation(node.SyntaxTree, node.Span, cancellationToken))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private bool ShouldExecuteOperation(IOperation operation, DiagnosticAnalyzer analyzer, CancellationToken cancellationToken)
|
|
{
|
|
if (operation.Syntax != null && _shouldSkipAnalysisOnGeneratedCode(analyzer) && _isGeneratedCodeLocation(operation.Syntax.SyntaxTree, operation.Syntax.Span, cancellationToken))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
internal TimeSpan ResetAnalyzerExecutionTime(DiagnosticAnalyzer analyzer)
|
|
{
|
|
if (!_analyzerExecutionTimeMap.TryRemove(analyzer, out StrongBox<long> value))
|
|
{
|
|
return TimeSpan.Zero;
|
|
}
|
|
return TimeSpan.FromTicks(value.Value);
|
|
}
|
|
|
|
private ControlFlowGraph GetControlFlowGraphImpl(IOperation operation)
|
|
{
|
|
if (_lazyControlFlowGraphMap == null)
|
|
{
|
|
Interlocked.CompareExchange(ref _lazyControlFlowGraphMap, new ConcurrentDictionary<IOperation, ControlFlowGraph>(), null);
|
|
}
|
|
return _lazyControlFlowGraphMap.GetOrAdd(operation, (IOperation op) => ControlFlowGraphBuilder.Create(op, null, null, null, default(ControlFlowGraphBuilder.Context)));
|
|
}
|
|
|
|
private bool IsAnalyzerSuppressedForSymbol(DiagnosticAnalyzer analyzer, ISymbol symbol, CancellationToken cancellationToken)
|
|
{
|
|
ImmutableArray<Location>.Enumerator enumerator = symbol.Locations.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
Location current = enumerator.Current;
|
|
if (current.SourceTree != null && !IsAnalyzerSuppressedForTree(analyzer, current.SourceTree, cancellationToken))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void OnOperationBlockActionsExecuted(ImmutableArray<IOperation> operationBlocks)
|
|
{
|
|
ConcurrentDictionary<IOperation, ControlFlowGraph>? lazyControlFlowGraphMap = _lazyControlFlowGraphMap;
|
|
if (lazyControlFlowGraphMap != null && lazyControlFlowGraphMap.Count > 0)
|
|
{
|
|
ImmutableArray<IOperation>.Enumerator enumerator = operationBlocks.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
IOperation rootOperation = enumerator.Current.GetRootOperation();
|
|
_lazyControlFlowGraphMap.TryRemove(rootOperation, out ControlFlowGraph _);
|
|
}
|
|
}
|
|
}
|
|
}
|