/// <summary> /// Invoke this method at completion of event processing for the given analysis scope. /// It updates the analysis state of this event for each analyzer and if the event has been fully processed for all analyzers, then removes it from our event cache. /// </summary> public void OnCompilationEventProcessed(CompilationEvent compilationEvent, AnalysisScope analysisScope) { // Analyze if the symbol and all its declaring syntax references are analyzed. var symbolDeclaredEvent = compilationEvent as SymbolDeclaredCompilationEvent; if (symbolDeclaredEvent != null) { OnSymbolDeclaredEventProcessed(symbolDeclaredEvent, analysisScope.Analyzers); } // Check if event is fully analyzed for all analyzers. foreach (var analyzerState in _analyzerStates) { if (!analyzerState.IsEventAnalyzed(compilationEvent)) { return; } } // Remove the event from event map. lock (_gate) { UpdateEventsMap_NoLock(compilationEvent, add: false); } }
/// <summary> /// Executes the symbol actions on the given symbol. /// </summary> /// <param name="symbolActions">Symbol actions to be executed.</param> /// <param name="analyzer">Analyzer whose actions are to be executed.</param> /// <param name="symbol">Symbol to be analyzed.</param> /// <param name="overriddenAddDiagnostic">Overridden add diagnostic delegate.</param> /// <param name="overriddenAddLocalDiagnostic">Overridden add local diagnostic delegate.</param> /// <param name="overriddenAddNonLocalDiagnostic">Overridden add non-local diagnostic delegate.</param> /// <param name="getTopMostNodeForAnalysis">Delegate to get topmost declaration node for a symbol declaration reference.</param> /// <param name="analysisScope">Scope for analyzer execution.</param> /// <param name="analysisStateOpt">An optional object to track analysis state.</param> public void ExecuteSymbolActions( ImmutableArray <SymbolAnalyzerAction> symbolActions, DiagnosticAnalyzer analyzer, ISymbol symbol, Action <Diagnostic> overriddenAddDiagnostic, Action <Diagnostic, DiagnosticAnalyzer, bool> overriddenAddLocalDiagnostic, Action <Diagnostic, DiagnosticAnalyzer> overriddenAddNonLocalDiagnostic, Func <ISymbol, SyntaxReference, Compilation, SyntaxNode> getTopMostNodeForAnalysis, AnalysisScope analysisScope, AnalysisState analysisStateOpt) { AnalyzerStateData analyzerStateOpt = null; try { if (TryStartAnalyzingSymbol(symbol, analyzer, analysisScope, analysisStateOpt, out analyzerStateOpt)) { ExecuteSymbolActionsCore(symbolActions, analyzer, symbol, overriddenAddDiagnostic, overriddenAddLocalDiagnostic, overriddenAddNonLocalDiagnostic, getTopMostNodeForAnalysis, analyzerStateOpt); analysisStateOpt?.MarkSymbolComplete(symbol, analyzer); } } finally { analyzerStateOpt?.ResetToReadyState(); } }
private ImmutableArray <Diagnostic> GetDiagnostics_NoLock(AnalysisScope analysisScope, bool getLocalDiagnostics, bool getNonLocalDiagnostics) { Debug.Assert(getLocalDiagnostics || getNonLocalDiagnostics); var builder = ImmutableArray.CreateBuilder <Diagnostic>(); if (getLocalDiagnostics) { if (!analysisScope.IsTreeAnalysis) { AddAllLocalDiagnostics_NoLock(_localSyntaxDiagnosticsOpt, analysisScope, builder); AddAllLocalDiagnostics_NoLock(_localSemanticDiagnosticsOpt, analysisScope, builder); } else if (analysisScope.IsSyntaxOnlyTreeAnalysis) { AddLocalDiagnosticsForPartialAnalysis_NoLock(_localSyntaxDiagnosticsOpt, analysisScope, builder); } else { AddLocalDiagnosticsForPartialAnalysis_NoLock(_localSemanticDiagnosticsOpt, analysisScope, builder); } } if (getNonLocalDiagnostics && _nonLocalDiagnosticsOpt != null) { AddDiagnostics_NoLock(_nonLocalDiagnosticsOpt, analysisScope, builder); } return(builder.ToImmutableArray()); }
public void ExecuteSyntaxNodeActions <TLanguageKindEnum>( IEnumerable <SyntaxNode> nodesToAnalyze, IDictionary <TLanguageKindEnum, ImmutableArray <SyntaxNodeAnalyzerAction <TLanguageKindEnum> > > nodeActionsByKind, DiagnosticAnalyzer analyzer, SemanticModel model, Func <SyntaxNode, TLanguageKindEnum> getKind, TextSpan filterSpan, SyntaxReference declaration, AnalysisScope analysisScope, AnalysisState analysisStateOpt) where TLanguageKindEnum : struct { DeclarationAnalyzerStateData analyzerStateOpt = null; try { if (TryStartAnalyzingSyntaxRefence(declaration, analyzer, analysisScope, analysisStateOpt, out analyzerStateOpt)) { ExecuteSyntaxNodeActionsCore(nodesToAnalyze, nodeActionsByKind, analyzer, model, getKind, filterSpan, analyzerStateOpt); } } finally { analyzerStateOpt?.ResetToReadyState(); } }
private async Task <ImmutableArray <Diagnostic> > GetAnalyzerDiagnosticsCoreAsync(ImmutableArray <DiagnosticAnalyzer> analyzers, bool includeCompilerDiagnostics, bool includeSourceEvents, bool includeNonSourceEvents, bool forceCompleteCompilation, CancellationToken cancellationToken) { Debug.Assert(!includeCompilerDiagnostics || forceCompleteCompilation); await WaitForActiveAnalysisTasksAsync(cancellationToken).ConfigureAwait(false); var diagnostics = ImmutableArray <Diagnostic> .Empty; var analysisScope = new AnalysisScope(_compilation, analyzers, _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true); Action generateCompilationEvents = () => { if (forceCompleteCompilation) { // Invoke GetDiagnostics to populate the compilation's CompilationEvent queue. // Discard the returned diagnostics. var compDiagnostics = _compilation.GetDiagnostics(cancellationToken); if (includeCompilerDiagnostics) { diagnostics = compDiagnostics; } } }; Func <AsyncQueue <CompilationEvent> > getEventQueue = () => GetPendingEvents(analyzers, includeSourceEvents, includeNonSourceEvents); // Compute the analyzer diagnostics for the given analysis scope. await ComputeAnalyzerDiagnosticsAsync(analysisScope, generateCompilationEvents, getEventQueue, newTaskToken : 0, cancellationToken : cancellationToken).ConfigureAwait(false); // Return computed analyzer diagnostics for the given analysis scope. var analyzerDiagnostics = _analysisResult.GetDiagnostics(analysisScope, getLocalDiagnostics: includeSourceEvents, getNonLocalDiagnostics: includeNonSourceEvents); return(diagnostics.AddRange(analyzerDiagnostics)); }
public void StoreAnalysisResult(AnalysisScope analysisScope, AnalyzerDriver driver) { foreach (var analyzer in analysisScope.Analyzers) { // Dequeue reported analyzer diagnostics from the driver and store them in our maps. var syntaxDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: true); var semanticDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: false); var compilationDiagnostics = driver.DequeueNonLocalDiagnostics(analyzer); lock (_gate) { if (syntaxDiagnostics.Length > 0 || semanticDiagnostics.Length > 0 || compilationDiagnostics.Length > 0) { UpdateLocalDiagnostics_NoLock(analyzer, syntaxDiagnostics, ref _localSyntaxDiagnosticsOpt); UpdateLocalDiagnostics_NoLock(analyzer, semanticDiagnostics, ref _localSemanticDiagnosticsOpt); UpdateNonLocalDiagnostics_NoLock(analyzer, compilationDiagnostics); } if (_analyzerExecutionTimeOpt != null) { _analyzerExecutionTimeOpt[analyzer] += driver.ResetAnalyzerExecutionTime(analyzer); } } } }
public void StoreAnalysisResult(AnalysisScope analysisScope, AnalyzerDriver driver) { foreach (var analyzer in analysisScope.Analyzers) { // Dequeue reported analyzer diagnostics from the driver and store them in our maps. var syntaxDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: true); var semanticDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: false); var compilationDiagnostics = driver.DequeueNonLocalDiagnostics(analyzer); lock (_gate) { if (syntaxDiagnostics.Length > 0 || semanticDiagnostics.Length > 0 || compilationDiagnostics.Length > 0) { UpdateLocalDiagnostics_NoLock(analyzer, syntaxDiagnostics, ref _localSyntaxDiagnosticsOpt); UpdateLocalDiagnostics_NoLock(analyzer, semanticDiagnostics, ref _localSemanticDiagnosticsOpt); UpdateNonLocalDiagnostics_NoLock(analyzer, compilationDiagnostics); } if (_analyzerExecutionTimeOpt != null) { _analyzerExecutionTimeOpt[analyzer] += driver.ResetAnalyzerExecutionTime(analyzer); } } } }
internal ImmutableArray <Diagnostic> GetDiagnostics(AnalysisScope analysisScope, bool getLocalDiagnostics, bool getNonLocalDiagnostics) { lock (_gate) { return(GetDiagnostics_NoLock(analysisScope, getLocalDiagnostics, getNonLocalDiagnostics)); } }
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, SyntaxReference declaration, AnalysisScope analysisScope, AnalysisState analysisStateOpt) where TLanguageKindEnum : struct { DeclarationAnalyzerStateData analyzerStateOpt = null; try { if (TryStartAnalyzingSyntaxRefence(declaration, analyzer, analysisScope, analysisStateOpt, out analyzerStateOpt)) { ExecuteCodeBlockActionsCore(codeBlockStartActions, codeBlockActions, codeBlockEndActions, analyzer, declaredNode, declaredSymbol, executableCodeBlocks, semanticModel, getKind, analyzerStateOpt?.CodeBlockAnalysisState); } } finally { analyzerStateOpt?.ResetToReadyState(); } }
private static void AddLocalDiagnosticsForPartialAnalysis_NoLock( Dictionary <SyntaxTree, Dictionary <DiagnosticAnalyzer, ImmutableArray <Diagnostic> .Builder> > localDiagnostics, AnalysisScope analysisScope, ImmutableArray <Diagnostic> .Builder builder) { if (localDiagnostics != null && localDiagnostics.TryGetValue(analysisScope.FilterTreeOpt, out Dictionary <DiagnosticAnalyzer, ImmutableArray <Diagnostic> .Builder> diagnosticsForTree)) { AddDiagnostics_NoLock(diagnosticsForTree, analysisScope, builder); } }
public void OnCompilationEventGenerated(CompilationEvent compilationEvent, AnalyzerActionCounts actionCounts) { lock (_gate) { var symbolEvent = compilationEvent as SymbolDeclaredCompilationEvent; if (symbolEvent != null) { var needsAnalysis = false; var symbol = symbolEvent.Symbol; if (!AnalysisScope.ShouldSkipSymbolAnalysis(symbolEvent) && actionCounts.SymbolActionsCount > 0) { needsAnalysis = true; _pendingSymbols[symbol] = null; } if (!AnalysisScope.ShouldSkipDeclarationAnalysis(symbol) && (actionCounts.SyntaxNodeActionsCount > 0 || actionCounts.CodeBlockActionsCount > 0 || actionCounts.CodeBlockStartActionsCount > 0)) { foreach (var syntaxRef in symbolEvent.DeclaringSyntaxReferences) { needsAnalysis = true; _pendingDeclarations[syntaxRef.GetSyntax()] = null; } } if (!needsAnalysis) { return; } } else if (compilationEvent is CompilationStartedEvent) { if (actionCounts.SyntaxTreeActionsCount > 0) { var trees = compilationEvent.Compilation.SyntaxTrees; var map = new Dictionary <SyntaxTree, AnalyzerStateData>(trees.Count()); foreach (var tree in trees) { map[tree] = null; } _lazyPendingSyntaxAnalysisTrees = map; } if (actionCounts.CompilationActionsCount == 0) { return; } } _pendingEvents[compilationEvent] = null; } }
public void OnCompilationEventGenerated(CompilationEvent compilationEvent, AnalyzerActionCounts actionCounts) { lock (_gate) { var symbolEvent = compilationEvent as SymbolDeclaredCompilationEvent; if (symbolEvent != null) { var needsAnalysis = false; var symbol = symbolEvent.Symbol; var skipSymbolAnalysis = AnalysisScope.ShouldSkipSymbolAnalysis(symbolEvent); if (!skipSymbolAnalysis && actionCounts.SymbolActionsCount > 0) { needsAnalysis = true; _pendingSymbols[symbol] = null; } var skipDeclarationAnalysis = AnalysisScope.ShouldSkipDeclarationAnalysis(symbol); if (!skipDeclarationAnalysis && actionCounts.HasAnyExecutableCodeActions) { needsAnalysis = true; _pendingDeclarations[symbol] = null; } if (actionCounts.SymbolStartActionsCount > 0 && (!skipSymbolAnalysis || !skipDeclarationAnalysis)) { needsAnalysis = true; _lazyPendingSymbolEndAnalyses = _lazyPendingSymbolEndAnalyses ?? new Dictionary <ISymbol, AnalyzerStateData>(); _lazyPendingSymbolEndAnalyses[symbol] = null; } if (!needsAnalysis) { return; } } else if (compilationEvent is CompilationStartedEvent compilationStartedEvent) { var fileCount = actionCounts.SyntaxTreeActionsCount > 0 ? compilationEvent.Compilation.SyntaxTrees.Count() : 0; fileCount += actionCounts.AdditionalFileActionsCount > 0 ? compilationStartedEvent.AdditionalFiles.Length : 0; if (fileCount > 0) { _lazyFilesWithAnalysisData = new Dictionary <SourceOrAdditionalFile, AnalyzerStateData>(); _pendingSyntaxAnalysisFilesCount = fileCount; } if (actionCounts.CompilationActionsCount == 0) { return; } } _pendingEvents[compilationEvent] = null; } }
private static void AddAllLocalDiagnostics_NoLock( Dictionary <SyntaxTree, Dictionary <DiagnosticAnalyzer, ImmutableArray <Diagnostic> .Builder> >?localDiagnostics, AnalysisScope analysisScope, ImmutableArray <Diagnostic> .Builder builder) { if (localDiagnostics != null) { foreach (var localDiagsByTree in localDiagnostics.Values) { AddDiagnostics_NoLock(localDiagsByTree, analysisScope, builder); } } }
private async Task <ImmutableArray <Diagnostic> > GetAnalyzerSyntaxDiagnosticsCoreAsync(SyntaxTree tree, ImmutableArray <DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken) { var taskToken = Interlocked.Increment(ref _currentToken); var analysisScope = new AnalysisScope(analyzers, tree, filterSpan: null, syntaxAnalysis: true, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true); Action generateCompilationEvents = null; Func <AsyncQueue <CompilationEvent> > getEventQueue = () => s_EmptyEventQueue; // Compute the analyzer diagnostics for the given analysis scope. await ComputeAnalyzerDiagnosticsAsync(analysisScope, generateCompilationEvents, getEventQueue, taskToken, cancellationToken).ConfigureAwait(false); // Return computed analyzer diagnostics for the given analysis scope. return(_analysisResult.GetDiagnostics(analysisScope, getLocalDiagnostics: true, getNonLocalDiagnostics: false)); }
public void OnCompilationEventGenerated(CompilationEvent compilationEvent, AnalyzerActionCounts actionCounts) { lock (_gate) { var symbolEvent = compilationEvent as SymbolDeclaredCompilationEvent; if (symbolEvent != null) { var needsAnalysis = false; var symbol = symbolEvent.Symbol; if (!AnalysisScope.ShouldSkipSymbolAnalysis(symbolEvent) && actionCounts.SymbolActionsCount > 0) { needsAnalysis = true; _pendingSymbols[symbol] = null; } if (!AnalysisScope.ShouldSkipDeclarationAnalysis(symbol) && actionCounts.HasAnyExecutableCodeActions) { needsAnalysis = true; _pendingDeclarations[symbol] = _currentlyAnalyzingDeclarationsMapPool.Allocate(); } if (!needsAnalysis) { return; } } else if (compilationEvent is CompilationStartedEvent) { if (actionCounts.SyntaxTreeActionsCount > 0) { var trees = compilationEvent.Compilation.SyntaxTrees; var map = new Dictionary <SyntaxTree, AnalyzerStateData>(trees.Count()); foreach (var tree in trees) { map[tree] = null; } _lazyPendingSyntaxAnalysisTrees = map; } if (actionCounts.CompilationActionsCount == 0) { return; } } _pendingEvents[compilationEvent] = null; } }
internal void ApplySuppressionsAndStoreAnalysisResult(AnalysisScope analysisScope, AnalyzerDriver driver, Compilation compilation, Func <DiagnosticAnalyzer, AnalyzerActionCounts> getAnalyzerActionCounts, bool fullAnalysisResultForAnalyzersInScope) { Debug.Assert(!fullAnalysisResultForAnalyzersInScope || analysisScope.FilterFileOpt == null, "Full analysis result cannot come from partial (tree) analysis."); foreach (var analyzer in analysisScope.Analyzers) { // Dequeue reported analyzer diagnostics from the driver and store them in our maps. var syntaxDiagnostics = driver.DequeueLocalDiagnosticsAndApplySuppressions(analyzer, syntax: true, compilation: compilation); var semanticDiagnostics = driver.DequeueLocalDiagnosticsAndApplySuppressions(analyzer, syntax: false, compilation: compilation); var compilationDiagnostics = driver.DequeueNonLocalDiagnosticsAndApplySuppressions(analyzer, compilation); lock (_gate) { if (_completedAnalyzers.Contains(analyzer)) { // Already stored full analysis result for this analyzer. continue; } if (syntaxDiagnostics.Length > 0 || semanticDiagnostics.Length > 0 || compilationDiagnostics.Length > 0 || fullAnalysisResultForAnalyzersInScope) { UpdateLocalDiagnostics_NoLock(analyzer, syntaxDiagnostics, fullAnalysisResultForAnalyzersInScope, getSourceTree, ref _localSyntaxDiagnosticsOpt); UpdateLocalDiagnostics_NoLock(analyzer, syntaxDiagnostics, fullAnalysisResultForAnalyzersInScope, getAdditionalTextKey, ref _localAdditionalFileDiagnosticsOpt); UpdateNonLocalDiagnostics_NoLock(analyzer, compilationDiagnostics, fullAnalysisResultForAnalyzersInScope); // NOTE: We need to dedupe compiler analyzer semantic diagnostics as we might run the compiler analyzer multiple times for different spans in the tree. UpdateLocalDiagnostics_NoLock(analyzer, semanticDiagnostics, fullAnalysisResultForAnalyzersInScope, getSourceTree, ref _localSemanticDiagnosticsOpt, dedupeDiagnostics: analyzer is CompilerDiagnosticAnalyzer); } if (_analyzerExecutionTimeOpt != null) { var timeSpan = driver.ResetAnalyzerExecutionTime(analyzer); _analyzerExecutionTimeOpt[analyzer] = fullAnalysisResultForAnalyzersInScope ? timeSpan : _analyzerExecutionTimeOpt[analyzer] + timeSpan; } if (!_analyzerActionCounts.ContainsKey(analyzer)) { _analyzerActionCounts.Add(analyzer, getAnalyzerActionCounts(analyzer)); } if (fullAnalysisResultForAnalyzersInScope) { _completedAnalyzers.Add(analyzer); } } }
public bool ShouldExecuteOperationActions(AnalysisScope analysisScope) { if (!this.OperationActionsByAnalyzerAndKind.IsEmpty) { foreach (var analyzer in analysisScope.Analyzers) { if (this.OperationActionsByAnalyzerAndKind.ContainsKey(analyzer)) { return(true); } } } return(false); }
private static void AddDiagnostics_NoLock( Dictionary <DiagnosticAnalyzer, ImmutableArray <Diagnostic> .Builder> diagnostics, AnalysisScope analysisScope, ImmutableArray <Diagnostic> .Builder builder) { Debug.Assert(diagnostics != null); foreach (DiagnosticAnalyzer analyzer in analysisScope.Analyzers) { if (diagnostics.TryGetValue(analyzer, out ImmutableArray <Diagnostic> .Builder diagnosticsByAnalyzer)) { builder.AddRange(diagnosticsByAnalyzer); } } }
/// <summary> /// Returns true if we have any pending syntax analysis for given analysis scope. /// </summary> public bool HasPendingSyntaxAnalysis(AnalysisScope analysisScope) { if (analysisScope.IsTreeAnalysis && !analysisScope.IsSyntaxOnlyTreeAnalysis) { return(false); } foreach (var analyzer in analysisScope.Analyzers) { if (_analyzerStateMap[analyzer].HasPendingSyntaxAnalysis(analysisScope.FilterTreeOpt)) { return(true); } } return(false); }
internal void StoreAnalysisResult(AnalysisScope analysisScope, AnalyzerDriver driver, Compilation compilation, Func<DiagnosticAnalyzer, AnalyzerActionCounts> getAnalyzerActionCounts, bool fullAnalysisResultForAnalyzersInScope) { Debug.Assert(!fullAnalysisResultForAnalyzersInScope || analysisScope.FilterTreeOpt == null, "Full analysis result cannot come from partial (tree) analysis."); foreach (var analyzer in analysisScope.Analyzers) { // Dequeue reported analyzer diagnostics from the driver and store them in our maps. var syntaxDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: true, compilation: compilation); var semanticDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: false, compilation: compilation); var compilationDiagnostics = driver.DequeueNonLocalDiagnostics(analyzer, compilation); lock (_gate) { if (_completedAnalyzers.Contains(analyzer)) { // Already stored full analysis result for this analyzer. continue; } if (syntaxDiagnostics.Length > 0 || semanticDiagnostics.Length > 0 || compilationDiagnostics.Length > 0 || fullAnalysisResultForAnalyzersInScope) { UpdateLocalDiagnostics_NoLock(analyzer, syntaxDiagnostics, fullAnalysisResultForAnalyzersInScope, ref _localSyntaxDiagnosticsOpt); UpdateLocalDiagnostics_NoLock(analyzer, semanticDiagnostics, fullAnalysisResultForAnalyzersInScope, ref _localSemanticDiagnosticsOpt); UpdateNonLocalDiagnostics_NoLock(analyzer, compilationDiagnostics, fullAnalysisResultForAnalyzersInScope); } if (_analyzerExecutionTimeOpt != null) { var timeSpan = driver.ResetAnalyzerExecutionTime(analyzer); _analyzerExecutionTimeOpt[analyzer] = fullAnalysisResultForAnalyzersInScope ? timeSpan : _analyzerExecutionTimeOpt[analyzer] + timeSpan; } if (!_analyzerActionCounts.ContainsKey(analyzer)) { _analyzerActionCounts.Add(analyzer, getAnalyzerActionCounts(analyzer)); } if (fullAnalysisResultForAnalyzersInScope) { _completedAnalyzers.Add(analyzer); } } } }
internal void StoreAnalysisResult(AnalysisScope analysisScope, AnalyzerDriver driver, Compilation compilation, Func <DiagnosticAnalyzer, AnalyzerActionCounts> getAnalyzerActionCounts, bool fullAnalysisResultForAnalyzersInScope) { Debug.Assert(!fullAnalysisResultForAnalyzersInScope || analysisScope.FilterTreeOpt == null, "Full analysis result cannot come from partial (tree) analysis."); foreach (var analyzer in analysisScope.Analyzers) { // Dequeue reported analyzer diagnostics from the driver and store them in our maps. var syntaxDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: true, compilation: compilation); var semanticDiagnostics = driver.DequeueLocalDiagnostics(analyzer, syntax: false, compilation: compilation); var compilationDiagnostics = driver.DequeueNonLocalDiagnostics(analyzer, compilation); lock (_gate) { if (_completedAnalyzers.Contains(analyzer)) { // Already stored full analysis result for this analyzer. continue; } if (syntaxDiagnostics.Length > 0 || semanticDiagnostics.Length > 0 || compilationDiagnostics.Length > 0 || fullAnalysisResultForAnalyzersInScope) { UpdateLocalDiagnostics_NoLock(analyzer, syntaxDiagnostics, fullAnalysisResultForAnalyzersInScope, ref _localSyntaxDiagnosticsOpt); UpdateLocalDiagnostics_NoLock(analyzer, semanticDiagnostics, fullAnalysisResultForAnalyzersInScope, ref _localSemanticDiagnosticsOpt); UpdateNonLocalDiagnostics_NoLock(analyzer, compilationDiagnostics, fullAnalysisResultForAnalyzersInScope); } if (_analyzerExecutionTimeOpt != null) { var timeSpan = driver.ResetAnalyzerExecutionTime(analyzer); _analyzerExecutionTimeOpt[analyzer] = fullAnalysisResultForAnalyzersInScope ? timeSpan : _analyzerExecutionTimeOpt[analyzer] + timeSpan; } if (!_analyzerActionCounts.ContainsKey(analyzer)) { _analyzerActionCounts.Add(analyzer, getAnalyzerActionCounts(analyzer)); } if (fullAnalysisResultForAnalyzersInScope) { _completedAnalyzers.Add(analyzer); } } } }
/// <summary> /// Returns true if we have any pending syntax analysis for given analysis scope. /// </summary> public async Task <bool> HasPendingSyntaxAnalysisAsync(AnalysisScope analysisScope, CancellationToken cancellationToken) { if (analysisScope.IsTreeAnalysis && !analysisScope.IsSyntaxOnlyTreeAnalysis) { return(false); } foreach (var analyzer in analysisScope.Analyzers) { var analyzerState = GetAnalyzerState(analyzer); if (await analyzerState.HasPendingSyntaxAnalysisAsync(analysisScope.FilterTreeOpt, cancellationToken).ConfigureAwait(false)) { return(true); } } return(false); }
/// <summary> /// Returns true if we have any pending syntax analysis for given analysis scope. /// </summary> public bool HasPendingSyntaxAnalysis(AnalysisScope analysisScope) { if (analysisScope.IsTreeAnalysis && !analysisScope.IsSyntaxOnlyTreeAnalysis) { return(false); } foreach (DiagnosticAnalyzer analyzer in analysisScope.Analyzers) { PerAnalyzerState analyzerState = GetAnalyzerState(analyzer); if (analyzerState.HasPendingSyntaxAnalysis(analysisScope.FilterTreeOpt)) { return(true); } } return(false); }
/// <summary> /// Returns true if we have any pending syntax analysis for given analysis scope. /// </summary> public bool HasPendingSyntaxAnalysis(AnalysisScope analysisScope) { if (analysisScope.IsSingleFileAnalysis && !analysisScope.IsSyntacticSingleFileAnalysis) { return(false); } foreach (var analyzer in analysisScope.Analyzers) { var analyzerState = GetAnalyzerState(analyzer); if (analyzerState.HasPendingSyntaxAnalysis(analysisScope.FilterFileOpt)) { return(true); } } return(false); }
private static void MarkAnalysisCompleteForUnprocessedAnalyzers <T>( AnalysisScope analysisScope, HashSet <DiagnosticAnalyzer> processedAnalyzers, Action <T, DiagnosticAnalyzer> markComplete, T arg) { Debug.Assert(processedAnalyzers.All(analysisScope.Contains)); if (analysisScope.Analyzers.Length == processedAnalyzers.Count) { return; } foreach (var analyzer in analysisScope.Analyzers) { if (!processedAnalyzers.Contains(analyzer)) { markComplete(arg, analyzer); } } }
/// <summary> /// Returns true if we have any pending symbol analysis for given analysis scope. /// </summary> public bool HasPendingSymbolAnalysis(AnalysisScope analysisScope) { Debug.Assert(analysisScope.FilterTreeOpt != null); var symbolDeclaredEvents = GetPendingSymbolDeclaredEvents(analysisScope.FilterTreeOpt); foreach (var symbolDeclaredEvent in symbolDeclaredEvents) { if (analysisScope.ShouldAnalyze(symbolDeclaredEvent.Symbol)) { foreach (var analyzer in analysisScope.Analyzers) { if (_analyzerStateMap[analyzer].HasPendingSymbolAnalysis(symbolDeclaredEvent.Symbol)) { return(true); } } } } return(false); }
private async Task <ImmutableArray <Diagnostic> > GetAnalyzerSemanticDiagnosticsCoreAsync(SemanticModel model, TextSpan?filterSpan, ImmutableArray <DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken) { var taskToken = Interlocked.Increment(ref _currentToken); var analysisScope = new AnalysisScope(analyzers, model.SyntaxTree, filterSpan, syntaxAnalysis: false, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true); // Below invocation will force GetDiagnostics on the model's tree to generate compilation events. Action generateCompilationEvents = () => _analysisState.GetOrCreateCachedSemanticModel(model.SyntaxTree, _compilation, cancellationToken); Func <AsyncQueue <CompilationEvent> > getEventQueue = () => GetPendingEvents(analyzers, model.SyntaxTree); // Compute the analyzer diagnostics for the given analysis scope. // We need to loop till symbol analysis is complete for any partial symbols being processed for other tree diagnostic requests. do { await ComputeAnalyzerDiagnosticsAsync(analysisScope, generateCompilationEvents, getEventQueue, taskToken, cancellationToken).ConfigureAwait(false); } while (_analysisState.HasPendingSymbolAnalysis(analysisScope)); // Return computed analyzer diagnostics for the given analysis scope. return(_analysisResult.GetDiagnostics(analysisScope, getLocalDiagnostics: true, getNonLocalDiagnostics: false)); }
/// <summary> /// Executes the syntax tree actions on the given syntax tree. /// </summary> /// <param name="syntaxTreeActions">Syntax tree actions to be executed.</param> /// <param name="analyzer">Analyzer whose actions are to be executed.</param> /// <param name="tree">Syntax tree to analyze.</param> /// <param name="analysisScope">Scope for analyzer execution.</param> /// <param name="analysisStateOpt">An optional object to track analysis state.</param> public void ExecuteSyntaxTreeActions( ImmutableArray <SyntaxTreeAnalyzerAction> syntaxTreeActions, DiagnosticAnalyzer analyzer, SyntaxTree tree, AnalysisScope analysisScope, AnalysisState analysisStateOpt) { AnalyzerStateData analyzerStateOpt = null; try { if (TryStartSyntaxAnalysis(tree, analyzer, analysisScope, analysisStateOpt, out analyzerStateOpt)) { ExecuteSyntaxTreeActionsCore(syntaxTreeActions, analyzer, tree, analyzerStateOpt); analysisStateOpt?.MarkSyntaxAnalysisComplete(tree, analyzer); } } finally { analyzerStateOpt?.ResetToReadyState(); } }
/// <summary> /// Returns true if we have any pending symbol analysis for given analysis scope. /// </summary> public bool HasPendingSymbolAnalysis(AnalysisScope analysisScope) { Debug.Assert(analysisScope.FilterTreeOpt != null); ImmutableArray <SymbolDeclaredCompilationEvent> symbolDeclaredEvents = GetPendingSymbolDeclaredEvents(analysisScope.FilterTreeOpt); foreach (SymbolDeclaredCompilationEvent symbolDeclaredEvent in symbolDeclaredEvents) { if (analysisScope.ShouldAnalyze(symbolDeclaredEvent.Symbol)) { foreach (DiagnosticAnalyzer analyzer in analysisScope.Analyzers) { PerAnalyzerState analyzerState = GetAnalyzerState(analyzer); if (analyzerState.HasPendingSymbolAnalysis(symbolDeclaredEvent.Symbol)) { return(true); } } } } return(false); }
/// <summary> /// Returns true if we have any pending symbol analysis for given analysis scope. /// </summary> public bool HasPendingSymbolAnalysis(AnalysisScope analysisScope, CancellationToken cancellationToken) { Debug.Assert(analysisScope.FilterTreeOpt != null); var symbolDeclaredEvents = GetPendingSymbolDeclaredEvents(analysisScope.FilterTreeOpt, cancellationToken); foreach (var symbolDeclaredEvent in symbolDeclaredEvents) { if (analysisScope.ShouldAnalyze(symbolDeclaredEvent.Symbol)) { foreach (var analyzer in analysisScope.Analyzers) { var analyzerState = GetAnalyzerState(analyzer); if (analyzerState.HasPendingSymbolAnalysis(symbolDeclaredEvent.Symbol)) { return(true); } } } } return(false); }
/// <summary> /// Returns true if we have any pending symbol analysis for given analysis scope. /// </summary> public async Task <bool> HasPendingSymbolAnalysisAsync(AnalysisScope analysisScope, CancellationToken cancellationToken) { Debug.Assert(analysisScope.FilterTreeOpt != null); var symbolDeclaredEvents = await GetPendingSymbolDeclaredEventsAsync(analysisScope.FilterTreeOpt, cancellationToken).ConfigureAwait(false); foreach (var symbolDeclaredEvent in symbolDeclaredEvents) { if (analysisScope.ShouldAnalyze(symbolDeclaredEvent.Symbol)) { foreach (var analyzer in analysisScope.Analyzers) { var analyzerState = GetAnalyzerState(analyzer); if (await analyzerState.HasPendingSymbolAnalysisAsync(symbolDeclaredEvent.Symbol, cancellationToken).ConfigureAwait(false)) { return(true); } } } } return(false); }
/// <summary> /// Executes the semantic model actions on the given semantic model. /// </summary> /// <param name="semanticModelActions">Semantic model actions to be executed.</param> /// <param name="analyzer">Analyzer whose actions are to be executed.</param> /// <param name="semanticModel">Semantic model to analyze.</param> /// <param name="compilationUnitCompletedEvent">Compilation event for semantic model analysis.</param> /// <param name="analysisScope">Scope for analyzer execution.</param> /// <param name="analysisStateOpt">An optional object to track analysis state.</param> public void ExecuteSemanticModelActions( ImmutableArray <SemanticModelAnalyzerAction> semanticModelActions, DiagnosticAnalyzer analyzer, SemanticModel semanticModel, CompilationEvent compilationUnitCompletedEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt) { AnalyzerStateData analyzerStateOpt = null; try { if (TryStartProcessingEvent(compilationUnitCompletedEvent, analyzer, analysisScope, analysisStateOpt, out analyzerStateOpt)) { ExecuteSemanticModelActionsCore(semanticModelActions, analyzer, semanticModel, analyzerStateOpt); analysisStateOpt?.MarkEventComplete(compilationUnitCompletedEvent, analyzer); } } finally { analyzerStateOpt?.ResetToReadyState(); } }
/// <summary> /// Attaches event queue to the driver and start processing all events pertaining to the given analysis scope. /// </summary> /// <param name="eventQueue">Compilation events to analyze.</param> /// <param name="analysisScope">Scope of analysis.</param> /// <param name="cancellationToken">Cancellation token to abort analysis.</param> /// <remarks>Driver must be initialized before invoking this method, i.e. <see cref="Initialize(AnalyzerExecutor, DiagnosticQueue, CancellationToken)"/> method must have been invoked and <see cref="WhenInitializedTask"/> must be non-null.</remarks> internal void AttachQueueAndStartProcessingEvents(AsyncQueue<CompilationEvent> eventQueue, AnalysisScope analysisScope, CancellationToken cancellationToken) { try { if (_initializeSucceeded) { this.CompilationEventQueue = eventQueue; _queueRegistration = cancellationToken.Register(() => { this.CompilationEventQueue.TryComplete(); this.DiagnosticQueue.TryComplete(); }); _primaryTask = ExecutePrimaryAnalysisTaskAsync(analysisScope, analysisStateOpt: null, usingPrePopulatedEventQueue: false, cancellationToken: cancellationToken) .ContinueWith(c => DiagnosticQueue.TryComplete(), cancellationToken, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } } finally { if (_primaryTask == null) { // Set primaryTask to be a cancelled task. var tcs = new TaskCompletionSource<int>(); tcs.SetCanceled(); _primaryTask = tcs.Task; // Try to set the DiagnosticQueue to be complete. this.DiagnosticQueue.TryComplete(); } } }
/// <summary> /// Attaches a pre-populated event queue to the driver and processes all events in the queue. /// </summary> /// <param name="eventQueue">Compilation events to analyze.</param> /// <param name="analysisScope">Scope of analysis.</param> /// <param name="analysisStateOpt">An optional object to track partial analysis state.</param> /// <param name="cancellationToken">Cancellation token to abort analysis.</param> /// <remarks>Driver must be initialized before invoking this method, i.e. <see cref="Initialize(AnalyzerExecutor, DiagnosticQueue, CancellationToken)"/> method must have been invoked and <see cref="WhenInitializedTask"/> must be non-null.</remarks> internal async Task AttachQueueAndProcessAllEventsAsync(AsyncQueue<CompilationEvent> eventQueue, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { try { if (_initializeSucceeded) { this.CompilationEventQueue = eventQueue; _queueRegistration = default(CancellationTokenRegistration); await ExecutePrimaryAnalysisTaskAsync(analysisScope, analysisStateOpt, usingPrePopulatedEventQueue: true, cancellationToken: cancellationToken).ConfigureAwait(false); _primaryTask = Task.FromResult(true); } } finally { if (_primaryTask == null) { // Set primaryTask to be a cancelled task. var tcs = new TaskCompletionSource<int>(); tcs.SetCanceled(); _primaryTask = tcs.Task; } } }
private async Task GenerateCompilationEventsAndPopulateEventsCacheAsync(AnalysisScope analysisScope, AnalyzerDriver driver, CancellationToken cancellationToken) { #if SIMULATED_EVENT_QUEUE await _analysisState.GenerateSimulatedCompilationEventsAsync(analysisScope, _compilation, _compilationData.GetOrCreateCachedSemanticModel, driver, cancellationToken).ConfigureAwait(false); #else GenerateCompilationEvents(analysisScope, cancellationToken); await PopulateEventsCacheAsync(cancellationToken).ConfigureAwait(false); #endif }
private void GenerateCompilationEvents(AnalysisScope analysisScope, CancellationToken cancellationToken) { // Invoke GetDiagnostics to populate CompilationEvent queue for the given analysis scope. // Discard the returned diagnostics. if (analysisScope.FilterTreeOpt == null) { var unused = _compilation.GetDiagnostics(cancellationToken); } else if (!analysisScope.IsSyntaxOnlyTreeAnalysis) { var mappedModel = _compilationData.GetOrCreateCachedSemanticModel(analysisScope.FilterTreeOpt, _compilation, cancellationToken); var unused = mappedModel.GetDiagnostics(cancellationToken: cancellationToken); } }
public ImmutableArray<Diagnostic> GetDiagnostics(AnalysisScope analysisScope, bool getLocalDiagnostics, bool getNonLocalDiagnostics) { lock (_gate) { return GetDiagnostics_NoLock(analysisScope, getLocalDiagnostics, getNonLocalDiagnostics); } }
private void ExecuteSymbolActions(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { var symbol = symbolEvent.Symbol; if (!analysisScope.ShouldAnalyze(symbol)) { return; } foreach (var analyzer in analysisScope.Analyzers) { // Invoke symbol analyzers only for source symbols. ImmutableArray<ImmutableArray<SymbolAnalyzerAction>> actionsByKind; if (_symbolActionsByKind.TryGetValue(analyzer, out actionsByKind) && (int)symbol.Kind < actionsByKind.Length) { analyzerExecutor.ExecuteSymbolActions(actionsByKind[(int)symbol.Kind], analyzer, symbol, GetTopmostNodeForAnalysis, analysisScope, analysisStateOpt); } else { analysisStateOpt?.MarkSymbolComplete(symbol, analyzer); } } }
private ImmutableArray<Diagnostic> GetDiagnostics_NoLock(AnalysisScope analysisScope, bool getLocalDiagnostics, bool getNonLocalDiagnostics) { Debug.Assert(getLocalDiagnostics || getNonLocalDiagnostics); var builder = ImmutableArray.CreateBuilder<Diagnostic>(); if (getLocalDiagnostics) { if (!analysisScope.IsTreeAnalysis) { AddAllLocalDiagnostics_NoLock(_localSyntaxDiagnosticsOpt, analysisScope, builder); AddAllLocalDiagnostics_NoLock(_localSemanticDiagnosticsOpt, analysisScope, builder); } else if (analysisScope.IsSyntaxOnlyTreeAnalysis) { AddLocalDiagnosticsForPartialAnalysis_NoLock(_localSyntaxDiagnosticsOpt, analysisScope, builder); } else { AddLocalDiagnosticsForPartialAnalysis_NoLock(_localSemanticDiagnosticsOpt, analysisScope, builder); } } if (getNonLocalDiagnostics && _nonLocalDiagnosticsOpt != null) { AddDiagnostics_NoLock(_nonLocalDiagnosticsOpt, analysisScope, builder); } return builder.ToImmutableArray(); }
private void ProcessCompilationUnitCompleted(CompilationUnitCompletedEvent completedEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { // When the compiler is finished with a compilation unit, we can run user diagnostics which // might want to ask the compiler for all the diagnostics in the source file, for example // to get information about unnecessary usings. var semanticModel = analysisStateOpt != null ? GetOrCreateCachedSemanticModel(completedEvent.CompilationUnit, completedEvent.Compilation, cancellationToken) : completedEvent.SemanticModel; if (!analysisScope.ShouldAnalyze(semanticModel.SyntaxTree)) { return; } try { foreach (var analyzer in analysisScope.Analyzers) { ImmutableArray<SemanticModelAnalyzerAction> semanticModelActions; if (_semanticModelActionsMap.TryGetValue(analyzer, out semanticModelActions)) { // Execute actions for a given analyzer sequentially. analyzerExecutor.ExecuteSemanticModelActions(semanticModelActions, analyzer, semanticModel, completedEvent, analysisScope, analysisStateOpt); } else { analysisStateOpt?.MarkEventComplete(completedEvent, analyzer); } } } finally { completedEvent.FlushCache(); } }
private void ExecuteSymbolActions(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { var symbol = symbolEvent.Symbol; if (!analysisScope.ShouldAnalyze(symbol)) { return; } Action<Diagnostic> addDiagnosticForSymbol = GetDiagnosticSinkWithSuppression(DiagnosticQueue.Enqueue, symbolEvent.Compilation, symbol); Action<Diagnostic, DiagnosticAnalyzer, bool> addLocalDiagnosticForSymbol = analysisScope.CategorizeDiagnostics ? GetDiagnosticSinkWithSuppression(DiagnosticQueue.EnqueueLocal, symbolEvent.Compilation, symbol) : null; Action<Diagnostic, DiagnosticAnalyzer> addNonLocalDiagnosticForSymbol = analysisScope.CategorizeDiagnostics ? GetDiagnosticSinkWithSuppression(DiagnosticQueue.EnqueueNonLocal, symbolEvent.Compilation, symbol) : null; foreach (var analyzer in analysisScope.Analyzers) { // Invoke symbol analyzers only for source symbols. ImmutableArray<ImmutableArray<SymbolAnalyzerAction>> actionsByKind; if (_symbolActionsByKind.TryGetValue(analyzer, out actionsByKind) && (int)symbol.Kind < actionsByKind.Length) { analyzerExecutor.ExecuteSymbolActions(actionsByKind[(int)symbol.Kind], analyzer, symbol, addDiagnosticForSymbol, addLocalDiagnosticForSymbol, addNonLocalDiagnosticForSymbol, GetTopmostNodeForAnalysis, analysisScope, analysisStateOpt); } else { analysisStateOpt?.MarkSymbolComplete(symbol, analyzer); } } }
private void ProcessEventCore(CompilationEvent e, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var symbolEvent = e as SymbolDeclaredCompilationEvent; if (symbolEvent != null) { ProcessSymbolDeclared(symbolEvent, analysisScope, analysisStateOpt, cancellationToken); return; } var completedEvent = e as CompilationUnitCompletedEvent; if (completedEvent != null) { ProcessCompilationUnitCompleted(completedEvent, analysisScope, analysisStateOpt, cancellationToken); return; } var endEvent = e as CompilationCompletedEvent; if (endEvent != null) { ProcessCompilationCompleted(endEvent, analysisScope, analysisStateOpt, cancellationToken); return; } var startedEvent = e as CompilationStartedEvent; if (startedEvent != null) { ProcessCompilationStarted(startedEvent, analysisScope, analysisStateOpt, cancellationToken); return; } throw new InvalidOperationException("Unexpected compilation event of type " + e.GetType().Name); }
private async Task<CompilationCompletedEvent> ProcessCompilationEventsCoreAsync(AnalysisScope analysisScope, AnalysisState analysisStateOpt, bool prePopulatedEventQueue, CancellationToken cancellationToken) { try { CompilationCompletedEvent completedEvent = null; while (true) { cancellationToken.ThrowIfCancellationRequested(); if (CompilationEventQueue.Count == 0 && (prePopulatedEventQueue || CompilationEventQueue.IsCompleted)) { break; } CompilationEvent e; try { if (!prePopulatedEventQueue) { e = await CompilationEventQueue.DequeueAsync(cancellationToken).ConfigureAwait(false); } else if (!CompilationEventQueue.TryDequeue(out e)) { return completedEvent; } } catch (TaskCanceledException) when (!prePopulatedEventQueue) { // When the queue is completed with a pending DequeueAsync return then a // TaskCanceledException will be thrown. This just signals the queue is // complete and we should finish processing it. Debug.Assert(CompilationEventQueue.IsCompleted, "DequeueAsync should never throw unless the AsyncQueue<T> is completed."); break; } // Don't process the compilation completed event as other worker threads might still be processing other compilation events. // The caller will wait for all workers to complete and finally process this event. var compilationCompletedEvent = e as CompilationCompletedEvent; if (compilationCompletedEvent != null) { completedEvent = compilationCompletedEvent; continue; } ProcessEvent(e, analysisScope, analysisStateOpt, cancellationToken); } return completedEvent; } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
// internal for testing purposes internal static AnalyzerDriver CreateAndAttachToCompilation( Compilation compilation, ImmutableArray<DiagnosticAnalyzer> analyzers, AnalyzerOptions options, AnalyzerManager analyzerManager, Action<Exception, DiagnosticAnalyzer, Diagnostic> onAnalyzerException, bool reportAnalyzer, out Compilation newCompilation, CancellationToken cancellationToken) { AnalyzerDriver analyzerDriver = compilation.AnalyzerForLanguage(analyzers, analyzerManager); newCompilation = compilation.WithEventQueue(new AsyncQueue<CompilationEvent>()); var categorizeDiagnostics = false; var analysisOptions = new CompilationWithAnalyzersOptions(options, onAnalyzerException, concurrentAnalysis: true, logAnalyzerExecutionTime: reportAnalyzer); analyzerDriver.Initialize(newCompilation, analysisOptions, categorizeDiagnostics, cancellationToken); var analysisScope = new AnalysisScope(newCompilation, analyzers, concurrentAnalysis: newCompilation.Options.ConcurrentBuild, categorizeDiagnostics: categorizeDiagnostics); analyzerDriver.AttachQueueAndStartProcessingEvents(newCompilation.EventQueue, analysisScope, cancellationToken: cancellationToken); return analyzerDriver; }
private static void AddDiagnostics_NoLock( Dictionary<DiagnosticAnalyzer, List<Diagnostic>> diagnostics, AnalysisScope analysisScope, ImmutableArray<Diagnostic>.Builder builder) { Debug.Assert(diagnostics != null); foreach (var analyzer in analysisScope.Analyzers) { List<Diagnostic> diagnosticsByAnalyzer; if (diagnostics.TryGetValue(analyzer, out diagnosticsByAnalyzer)) { builder.AddRange(diagnosticsByAnalyzer); } } }
private async Task ExecutePrimaryAnalysisTaskAsync(AnalysisScope analysisScope, AnalysisState analysisStateOpt, bool usingPrePopulatedEventQueue, CancellationToken cancellationToken) { Debug.Assert(analysisScope != null); Debug.Assert(WhenInitializedTask != null); await WhenInitializedTask.ConfigureAwait(false); if (WhenInitializedTask.IsFaulted) { OnDriverException(WhenInitializedTask, this.analyzerExecutor, analysisScope.Analyzers); } else if (!WhenInitializedTask.IsCanceled) { this.analyzerExecutor = this.analyzerExecutor.WithCancellationToken(cancellationToken); await ProcessCompilationEventsAsync(analysisScope, analysisStateOpt, usingPrePopulatedEventQueue, cancellationToken).ConfigureAwait(false); } }
private void ExecuteSyntaxTreeActions(AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { if (analysisScope.IsTreeAnalysis && !analysisScope.IsSyntaxOnlyTreeAnalysis) { // For partial analysis, only execute syntax tree actions if performing syntax analysis. return; } foreach (var tree in analysisScope.SyntaxTrees) { foreach (var analyzer in analysisScope.Analyzers) { cancellationToken.ThrowIfCancellationRequested(); ImmutableArray<SyntaxTreeAnalyzerAction> syntaxTreeActions; if (_syntaxTreeActionsMap.TryGetValue(analyzer, out syntaxTreeActions)) { // Execute actions for a given analyzer sequentially. analyzerExecutor.ExecuteSyntaxTreeActions(syntaxTreeActions, analyzer, tree, analysisScope, analysisStateOpt); } else { analysisStateOpt?.MarkSyntaxAnalysisComplete(tree, analyzer); } } } }
private void ExecuteCompilationActions( ImmutableDictionary<DiagnosticAnalyzer, ImmutableArray<CompilationAnalyzerAction>> compilationActionsMap, CompilationEvent compilationEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { Debug.Assert(compilationEvent is CompilationStartedEvent || compilationEvent is CompilationCompletedEvent); try { foreach (var analyzer in analysisScope.Analyzers) { ImmutableArray<CompilationAnalyzerAction> compilationActions; if (compilationActionsMap.TryGetValue(analyzer, out compilationActions)) { analyzerExecutor.ExecuteCompilationActions(compilationActions, analyzer, compilationEvent, analysisScope, analysisStateOpt); } else { analysisStateOpt?.MarkEventComplete(compilationEvent, analyzer); } } } finally { compilationEvent.FlushCache(); } }
private async Task ProcessCompilationEventsAsync(AnalysisScope analysisScope, AnalysisState analysisStateOpt, bool prePopulatedEventQueue, CancellationToken cancellationToken) { try { CompilationCompletedEvent completedEvent = null; if (analysisScope.ConcurrentAnalysis) { // Kick off worker tasks to process all compilation events (except the compilation end event) in parallel. // Compilation end event must be processed after all other events. var workerCount = prePopulatedEventQueue ? Math.Min(CompilationEventQueue.Count, _workerCount) : _workerCount; var workerTasks = new Task<CompilationCompletedEvent>[workerCount]; for (int i = 0; i < workerCount; i++) { workerTasks[i] = ProcessCompilationEventsCoreAsync(analysisScope, analysisStateOpt, prePopulatedEventQueue, cancellationToken); } cancellationToken.ThrowIfCancellationRequested(); // Kick off tasks to execute syntax tree actions. var syntaxTreeActionsTask = Task.Run(() => ExecuteSyntaxTreeActions(analysisScope, analysisStateOpt, cancellationToken)); // Wait for all worker threads to complete processing events. await Task.WhenAll(workerTasks.Concat(syntaxTreeActionsTask)).ConfigureAwait(false); for (int i = 0; i < workerCount; i++) { if (workerTasks[i].Status == TaskStatus.RanToCompletion && workerTasks[i].Result != null) { completedEvent = workerTasks[i].Result; break; } } } else { completedEvent = await ProcessCompilationEventsCoreAsync(analysisScope, analysisStateOpt, prePopulatedEventQueue, cancellationToken).ConfigureAwait(false); ExecuteSyntaxTreeActions(analysisScope, analysisStateOpt, cancellationToken); } // Finally process the compilation completed event, if any. if (completedEvent != null) { ProcessEvent(completedEvent, analysisScope, analysisStateOpt, cancellationToken); } } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private async Task<ImmutableArray<Diagnostic>> GetAnalyzerDiagnosticsCoreAsync(ImmutableArray<DiagnosticAnalyzer> analyzers, bool includeCompilerDiagnostics, bool includeSourceEvents, bool includeNonSourceEvents, bool forceCompleteCompilation, CancellationToken cancellationToken) { Debug.Assert(!includeCompilerDiagnostics || forceCompleteCompilation); await WaitForActiveAnalysisTasksAsync(cancellationToken).ConfigureAwait(false); var diagnostics = ImmutableArray<Diagnostic>.Empty; var analysisScope = new AnalysisScope(_compilation, analyzers, _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true); Action generateCompilationEvents = () => { if (forceCompleteCompilation) { // Invoke GetDiagnostics to populate the compilation's CompilationEvent queue. // Discard the returned diagnostics. var compDiagnostics = _compilation.GetDiagnostics(cancellationToken); if (includeCompilerDiagnostics) { diagnostics = compDiagnostics; } } }; Func<AsyncQueue<CompilationEvent>> getEventQueue = () => GetPendingEvents(analyzers, includeSourceEvents, includeNonSourceEvents); // Compute the analyzer diagnostics for the given analysis scope. await ComputeAnalyzerDiagnosticsAsync(analysisScope, generateCompilationEvents, getEventQueue, newTaskToken: 0, cancellationToken: cancellationToken).ConfigureAwait(false); // Return computed analyzer diagnostics for the given analysis scope. var analyzerDiagnostics = _analysisResult.GetDiagnostics(analysisScope, getLocalDiagnostics: includeSourceEvents, getNonLocalDiagnostics: includeNonSourceEvents); return diagnostics.AddRange(analyzerDiagnostics); }
private void ProcessEvent(CompilationEvent e, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { ProcessEventCore(e, analysisScope, analysisStateOpt, cancellationToken); analysisStateOpt?.OnCompilationEventProcessed(e, analysisScope); }
private async Task<ImmutableArray<Diagnostic>> GetAnalyzerSyntaxDiagnosticsCoreAsync(SyntaxTree tree, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken) { try { var taskToken = Interlocked.Increment(ref _currentToken); var analysisScope = new AnalysisScope(analyzers, tree, filterSpan: null, syntaxAnalysis: true, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true); Action generateCompilationEvents = null; Func<AsyncQueue<CompilationEvent>> getEventQueue = () => s_EmptyEventQueue; // Compute the analyzer diagnostics for the given analysis scope. await ComputeAnalyzerDiagnosticsAsync(analysisScope, generateCompilationEvents, getEventQueue, taskToken, cancellationToken).ConfigureAwait(false); // Return computed analyzer diagnostics for the given analysis scope. return _analysisResult.GetDiagnostics(analysisScope, getLocalDiagnostics: true, getNonLocalDiagnostics: false); } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private void ProcessSymbolDeclared(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { try { // Execute all analyzer actions. var symbol = symbolEvent.Symbol; var references = symbolEvent.DeclaringSyntaxReferences; if (!AnalysisScope.ShouldSkipSymbolAnalysis(symbolEvent)) { ExecuteSymbolActions(symbolEvent, analysisScope, analysisStateOpt, cancellationToken); } if (!AnalysisScope.ShouldSkipDeclarationAnalysis(symbol)) { ExecuteDeclaringReferenceActions(symbolEvent, analysisScope, analysisStateOpt, cancellationToken); } } finally { symbolEvent.FlushCache(); } }
private async Task<ImmutableArray<Diagnostic>> GetAnalyzerSemanticDiagnosticsCoreAsync(SemanticModel model, TextSpan? filterSpan, ImmutableArray<DiagnosticAnalyzer> analyzers, CancellationToken cancellationToken) { try { var taskToken = Interlocked.Increment(ref _currentToken); var analysisScope = new AnalysisScope(analyzers, model.SyntaxTree, filterSpan, syntaxAnalysis: false, concurrentAnalysis: _analysisOptions.ConcurrentAnalysis, categorizeDiagnostics: true); // Below invocation will force GetDiagnostics on the model's tree to generate compilation events. Action generateCompilationEvents = () => AnalyzerDriver.GetOrCreateCachedSemanticModel(model.SyntaxTree, _compilation, cancellationToken); Func<AsyncQueue<CompilationEvent>> getEventQueue = () => GetPendingEvents(analyzers, model.SyntaxTree); // Compute the analyzer diagnostics for the given analysis scope. // We need to loop till symbol analysis is complete for any partial symbols being processed for other tree diagnostic requests. do { await ComputeAnalyzerDiagnosticsAsync(analysisScope, generateCompilationEvents, getEventQueue, taskToken, cancellationToken).ConfigureAwait(false); } while (_analysisOptions.ConcurrentAnalysis && _analysisState.HasPendingSymbolAnalysis(analysisScope)); // Return computed analyzer diagnostics for the given analysis scope. return _analysisResult.GetDiagnostics(analysisScope, getLocalDiagnostics: true, getNonLocalDiagnostics: false); } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
protected abstract void ExecuteDeclaringReferenceActions(SymbolDeclaredCompilationEvent symbolEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken);
private async Task ComputeAnalyzerDiagnosticsAsync(AnalysisScope analysisScope, Action generateCompilationEventsOpt, Func<AsyncQueue<CompilationEvent>> getEventQueue, int newTaskToken, CancellationToken cancellationToken) { try { AnalyzerDriver driver = null; Task computeTask = null; CancellationTokenSource cts; // Generate compilation events, if required. if (generateCompilationEventsOpt != null) { generateCompilationEventsOpt(); } // Populate the events cache from the generated compilation events. await PopulateEventsCacheAsync(cancellationToken).ConfigureAwait(false); try { // Get the analyzer driver to execute analysis. driver = await GetAnalyzerDriverAsync(cancellationToken).ConfigureAwait(false); // Driver must have been initialized. Debug.Assert(driver.WhenInitializedTask != null); Debug.Assert(!driver.WhenInitializedTask.IsCanceled); cancellationToken.ThrowIfCancellationRequested(); // Track if this task was suspended by another tree diagnostics request for the same tree. // If so, we wait for the high priority requests to complete before restarting analysis. bool suspendend; do { suspendend = false; // Create a new cancellation source to allow higher priority requests to suspend our analysis. cts = new CancellationTokenSource(); // Link the cancellation source with client supplied cancellation source, so the public API callee can also cancel analysis. using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, cancellationToken)) { try { // Core task to compute analyzer diagnostics. Func<Tuple<Task, CancellationTokenSource>> getComputeTask = () => Tuple.Create( Task.Run(async () => { try { AsyncQueue<CompilationEvent> eventQueue = null; try { // Get event queue with pending events to analyze. eventQueue = getEventQueue(); // Execute analyzer driver on the given analysis scope with the given event queue. await ComputeAnalyzerDiagnosticsCoreAsync(driver, eventQueue, analysisScope, cancellationToken: linkedCts.Token).ConfigureAwait(false); } finally { FreeEventQueue(eventQueue); } } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }, linkedCts.Token), cts); // Wait for higher priority tree document tasks to complete. computeTask = await SetActiveAnalysisTaskAsync(getComputeTask, analysisScope.FilterTreeOpt, newTaskToken, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); await computeTask.ConfigureAwait(false); } catch (OperationCanceledException ex) { cancellationToken.ThrowIfCancellationRequested(); if (!cts.IsCancellationRequested) { throw ex; } suspendend = true; } finally { ClearExecutingTask(computeTask, analysisScope.FilterTreeOpt); cts.Dispose(); computeTask = null; } } } while (suspendend); } finally { FreeDriver(driver); } } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private void ProcessCompilationCompleted(CompilationCompletedEvent endEvent, AnalysisScope analysisScope, AnalysisState analysisStateOpt, CancellationToken cancellationToken) { ExecuteCompilationActions(_compilationEndActionsMap, endEvent, analysisScope, analysisStateOpt, cancellationToken); }
/// <summary> /// Core method for executing analyzers. /// </summary> private async Task ComputeAnalyzerDiagnosticsCoreAsync(AnalyzerDriver driver, AsyncQueue<CompilationEvent> eventQueue, AnalysisScope analysisScope, CancellationToken cancellationToken) { try { Debug.Assert(!driver.WhenInitializedTask.IsCanceled); if (eventQueue.Count > 0 || _analysisState.HasPendingSyntaxAnalysis(analysisScope)) { try { // Perform analysis to compute new diagnostics. Debug.Assert(!eventQueue.IsCompleted); await driver.AttachQueueAndProcessAllEventsAsync(eventQueue, analysisScope, _analysisState, cancellationToken: cancellationToken).ConfigureAwait(false); } finally { // Update the diagnostic results based on the diagnostics reported on the driver. _analysisResult.StoreAnalysisResult(analysisScope, driver); } } } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private static void AddAllLocalDiagnostics_NoLock( Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, List<Diagnostic>>> localDiagnostics, AnalysisScope analysisScope, ImmutableArray<Diagnostic>.Builder builder) { if (localDiagnostics != null) { foreach (var localDiagsByTree in localDiagnostics.Values) { AddDiagnostics_NoLock(localDiagsByTree, analysisScope, builder); } } }
private static void AddLocalDiagnosticsForPartialAnalysis_NoLock( Dictionary<SyntaxTree, Dictionary<DiagnosticAnalyzer, List<Diagnostic>>> localDiagnostics, AnalysisScope analysisScope, ImmutableArray<Diagnostic>.Builder builder) { Dictionary<DiagnosticAnalyzer, List<Diagnostic>> diagnosticsForTree; if (localDiagnostics != null && localDiagnostics.TryGetValue(analysisScope.FilterTreeOpt, out diagnosticsForTree)) { AddDiagnostics_NoLock(diagnosticsForTree, analysisScope, builder); } }