Skip to content

Commit

Permalink
Pool node collections in analyzer driver. (#67776)
Browse files Browse the repository at this point in the history
* In progress

* Simplify

* Simplify

* no dispose
  • Loading branch information
CyrusNajmabadi authored Apr 25, 2023
1 parent 1bc8f49 commit 6ef54a7
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,56 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.PooledObjects;

namespace Microsoft.CodeAnalysis.Diagnostics
{
internal abstract partial class AnalyzerDriver
{
internal sealed class DeclarationAnalysisData
internal readonly struct DeclarationAnalysisData
{
public DeclarationAnalysisData(
SyntaxNode declaringReferenceSyntax,
SyntaxNode topmostNodeForAnalysis,
ImmutableArray<DeclarationInfo> declarationsInNodeBuilder,
ImmutableArray<SyntaxNode> descendantNodesToAnalyze,
bool isPartialAnalysis)
{
DeclaringReferenceSyntax = declaringReferenceSyntax;
TopmostNodeForAnalysis = topmostNodeForAnalysis;
DeclarationsInNode = declarationsInNodeBuilder;
DescendantNodesToAnalyze = descendantNodesToAnalyze;
IsPartialAnalysis = isPartialAnalysis;
}

/// <summary>
/// GetSyntax() for the given SyntaxReference.
/// </summary>
public SyntaxNode DeclaringReferenceSyntax { get; }
public readonly SyntaxNode DeclaringReferenceSyntax;

/// <summary>
/// Topmost declaration node for analysis.
/// </summary>
public SyntaxNode TopmostNodeForAnalysis { get; }
public readonly SyntaxNode TopmostNodeForAnalysis;

/// <summary>
/// All member declarations within the declaration.
/// </summary>
public ImmutableArray<DeclarationInfo> DeclarationsInNode { get; }
public readonly ImmutableArray<DeclarationInfo> DeclarationsInNode;

/// <summary>
/// All descendant nodes for syntax node actions.
/// </summary>
public ImmutableArray<SyntaxNode> DescendantNodesToAnalyze { get; }
public readonly ArrayBuilder<SyntaxNode> DescendantNodesToAnalyze = ArrayBuilder<SyntaxNode>.GetInstance();

/// <summary>
/// Flag indicating if this is a partial analysis.
/// </summary>
public bool IsPartialAnalysis { get; }
public readonly bool IsPartialAnalysis;

public DeclarationAnalysisData(
SyntaxNode declaringReferenceSyntax,
SyntaxNode topmostNodeForAnalysis,
ImmutableArray<DeclarationInfo> declarationsInNodeBuilder,
bool isPartialAnalysis)
{
DeclaringReferenceSyntax = declaringReferenceSyntax;
TopmostNodeForAnalysis = topmostNodeForAnalysis;
DeclarationsInNode = declarationsInNodeBuilder;
IsPartialAnalysis = isPartialAnalysis;
}

public void Free()
{
DescendantNodesToAnalyze.Free();
}
}
}
}
80 changes: 48 additions & 32 deletions src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -646,11 +646,8 @@ internal async Task AttachQueueAndProcessAllEventsAsync(AsyncQueue<CompilationEv
}
finally
{
if (_lazyPrimaryTask == null)
{
// Set primaryTask to be a cancelled task.
_lazyPrimaryTask = Task.FromCanceled(new CancellationToken(canceled: true));
}
// Set primaryTask to be a cancelled task.
_lazyPrimaryTask ??= Task.FromCanceled(new CancellationToken(canceled: true));
}
}

Expand Down Expand Up @@ -2062,7 +2059,7 @@ private async ValueTask<IGroupedAnalyzerActions> GetPerSymbolAnalyzerActionsAsyn

// PERF: For containing symbols, we want to cache the computed actions.
// For member symbols, we do not want to cache as we will not reach this path again.
if (!(symbol is INamespaceOrTypeSymbol namespaceOrType))
if (symbol is not INamespaceOrTypeSymbol namespaceOrType)
{
return await getAllActionsAsync(this, symbol, analyzer, cancellationToken).ConfigureAwait(false);
}
Expand Down Expand Up @@ -2443,7 +2440,7 @@ protected override void ExecuteDeclaringReferenceActions(
}
}

private DeclarationAnalysisData ComputeDeclarationAnalysisData(
private static DeclarationAnalysisData ComputeDeclarationAnalysisData(
ISymbol symbol,
SyntaxReference declaration,
SemanticModel semanticModel,
Expand All @@ -2459,8 +2456,9 @@ private DeclarationAnalysisData ComputeDeclarationAnalysisData(
ImmutableArray<DeclarationInfo> declarationInfos = builder.ToImmutableAndFree();

bool isPartialDeclAnalysis = analysisScope.FilterSpanOpt.HasValue && !analysisScope.ContainsSpan(topmostNodeForAnalysis.FullSpan);
ImmutableArray<SyntaxNode> nodesToAnalyze = GetSyntaxNodesToAnalyze(topmostNodeForAnalysis, symbol, declarationInfos, semanticModel, cancellationToken);
return new DeclarationAnalysisData(declaringReferenceSyntax, topmostNodeForAnalysis, declarationInfos, nodesToAnalyze, isPartialDeclAnalysis);
var data = new DeclarationAnalysisData(declaringReferenceSyntax, topmostNodeForAnalysis, declarationInfos, isPartialDeclAnalysis);
AddSyntaxNodesToAnalyze(topmostNodeForAnalysis, symbol, declarationInfos, semanticModel, data.DescendantNodesToAnalyze, cancellationToken);
return data;
}

private static void ComputeDeclarationsInNode(SemanticModel semanticModel, ISymbol declaredSymbol, SyntaxNode declaringReferenceSyntax, SyntaxNode topmostNodeForAnalysis, ArrayBuilder<DeclarationInfo> builder, CancellationToken cancellationToken)
Expand Down Expand Up @@ -2496,16 +2494,16 @@ private void ExecuteDeclaringReferenceActions(
GetOrCreateSemanticModel(decl.SyntaxTree, symbolEvent.Compilation);

var declarationAnalysisData = ComputeDeclarationAnalysisData(symbol, decl, semanticModel, analysisScope, cancellationToken);
if (!analysisScope.ShouldAnalyze(declarationAnalysisData.TopmostNodeForAnalysis))
if (analysisScope.ShouldAnalyze(declarationAnalysisData.TopmostNodeForAnalysis))
{
return;
}
// Execute stateless syntax node actions.
executeNodeActions();

// Execute stateless syntax node actions.
executeNodeActions();
// Execute actions in executable code: code block actions, operation actions and operation block actions.
executeExecutableCodeActions();
}

// Execute actions in executable code: code block actions, operation actions and operation block actions.
executeExecutableCodeActions();
declarationAnalysisData.Free();

return;

Expand All @@ -2514,12 +2512,12 @@ void executeNodeActions()
if (shouldExecuteSyntaxNodeActions)
{
var nodesToAnalyze = declarationAnalysisData.DescendantNodesToAnalyze;
executeNodeActionsByKind(analysisScope, nodesToAnalyze, coreActions, arePerSymbolActions: false);
executeNodeActionsByKind(analysisScope, nodesToAnalyze, additionalPerSymbolActions, arePerSymbolActions: true);
executeNodeActionsByKind(nodesToAnalyze, coreActions, arePerSymbolActions: false);
executeNodeActionsByKind(nodesToAnalyze, additionalPerSymbolActions, arePerSymbolActions: true);
}
}

void executeNodeActionsByKind(AnalysisScope analysisScope, ImmutableArray<SyntaxNode> nodesToAnalyze, GroupedAnalyzerActions groupedActions, bool arePerSymbolActions)
void executeNodeActionsByKind(ArrayBuilder<SyntaxNode> nodesToAnalyze, GroupedAnalyzerActions groupedActions, bool arePerSymbolActions)
{
foreach (var (analyzer, groupedActionsForAnalyzer) in groupedActions.GroupedActionsByAnalyzer)
{
Expand All @@ -2534,11 +2532,31 @@ void executeNodeActionsByKind(AnalysisScope analysisScope, ImmutableArray<Syntax
// and additionally the analyzer has not registered any code block start actions. In case
// the analyzer has registered code block start actions, we need to make callbacks for all nodes
// in the code block to ensure the analyzer can correctly report code block end diagnostics.
var filteredNodesToAnalyze = declarationAnalysisData.IsPartialAnalysis && !groupedActionsForAnalyzer.HasCodeBlockStartActions
? nodesToAnalyze.WhereAsArray(analysisScope.ShouldAnalyze)
: nodesToAnalyze;
if (declarationAnalysisData.IsPartialAnalysis && !groupedActionsForAnalyzer.HasCodeBlockStartActions)
{
var filteredNodesToAnalyze = ArrayBuilder<SyntaxNode>.GetInstance(nodesToAnalyze.Count);
foreach (var node in nodesToAnalyze)
{
if (analysisScope.ShouldAnalyze(node))
filteredNodesToAnalyze.Add(node);
}

AnalyzerExecutor.ExecuteSyntaxNodeActions(filteredNodesToAnalyze, nodeActionsByKind,
executeSyntaxNodeActions(analyzer, groupedActionsForAnalyzer, filteredNodesToAnalyze);
filteredNodesToAnalyze.Free();
}
else
{
executeSyntaxNodeActions(analyzer, groupedActionsForAnalyzer, nodesToAnalyze);
}
}

void executeSyntaxNodeActions(
DiagnosticAnalyzer analyzer,
GroupedAnalyzerActionsForAnalyzer groupedActionsForAnalyzer,
ArrayBuilder<SyntaxNode> filteredNodesToAnalyze)
{
AnalyzerExecutor.ExecuteSyntaxNodeActions(
filteredNodesToAnalyze, groupedActionsForAnalyzer.NodeActionsByAnalyzerAndKind,
analyzer, semanticModel, _getKind, declarationAnalysisData.TopmostNodeForAnalysis.FullSpan,
symbol, isInGeneratedCode, hasCodeBlockStartOrSymbolStartActions: groupedActionsForAnalyzer.HasCodeBlockStartActions || arePerSymbolActions,
cancellationToken);
Expand Down Expand Up @@ -2623,12 +2641,12 @@ void executeOperationsActions(ImmutableArray<IOperation> operationsToAnalyze)
{
if (shouldExecuteOperationActions)
{
executeOperationsActionsByKind(analysisScope, operationsToAnalyze, coreActions, arePerSymbolActions: false);
executeOperationsActionsByKind(analysisScope, operationsToAnalyze, additionalPerSymbolActions, arePerSymbolActions: true);
executeOperationsActionsByKind(operationsToAnalyze, coreActions, arePerSymbolActions: false);
executeOperationsActionsByKind(operationsToAnalyze, additionalPerSymbolActions, arePerSymbolActions: true);
}
}

void executeOperationsActionsByKind(AnalysisScope analysisScope, ImmutableArray<IOperation> operationsToAnalyze, GroupedAnalyzerActions groupedActions, bool arePerSymbolActions)
void executeOperationsActionsByKind(ImmutableArray<IOperation> operationsToAnalyze, GroupedAnalyzerActions groupedActions, bool arePerSymbolActions)
{
foreach (var (analyzer, groupedActionsForAnalyzer) in groupedActions.GroupedActionsByAnalyzer)
{
Expand Down Expand Up @@ -2726,11 +2744,12 @@ static void addExecutableCodeBlockAnalyzerActions(
}
}

private static ImmutableArray<SyntaxNode> GetSyntaxNodesToAnalyze(
private static void AddSyntaxNodesToAnalyze(
SyntaxNode declaredNode,
ISymbol declaredSymbol,
ImmutableArray<DeclarationInfo> declarationsInNode,
SemanticModel semanticModel,
ArrayBuilder<SyntaxNode> nodesToAnalyze,
CancellationToken cancellationToken)
{
// Eliminate descendant member declarations within declarations.
Expand All @@ -2755,7 +2774,7 @@ private static ImmutableArray<SyntaxNode> GetSyntaxNodesToAnalyze(
break;
}

return ImmutableArray<SyntaxNode>.Empty;
return;
}

// Compute the topmost node representing the syntax declaration for the member that needs to be skipped.
Expand All @@ -2775,17 +2794,14 @@ private static ImmutableArray<SyntaxNode> GetSyntaxNodesToAnalyze(

Func<SyntaxNode, bool>? additionalFilter = semanticModel.GetSyntaxNodesToAnalyzeFilter(declaredNode, declaredSymbol);
bool shouldAddNode(SyntaxNode node) => (descendantDeclsToSkip == null || !descendantDeclsToSkip.Contains(node)) && (additionalFilter is null || additionalFilter(node));
var nodeBuilder = ArrayBuilder<SyntaxNode>.GetInstance();
foreach (var node in declaredNode.DescendantNodesAndSelf(descendIntoChildren: shouldAddNode, descendIntoTrivia: true))
{
if (shouldAddNode(node) &&
!semanticModel.ShouldSkipSyntaxNodeAnalysis(node, declaredSymbol))
{
nodeBuilder.Add(node);
nodesToAnalyze.Add(node);
}
}

return nodeBuilder.ToImmutableAndFree();
}

private static bool IsEquivalentSymbol(ISymbol declaredSymbol, ISymbol? otherSymbol)
Expand Down

0 comments on commit 6ef54a7

Please sign in to comment.