Skip to content

Commit

Permalink
Reduce the number of memory allocations in SA1404 (CodeAnalysisSuppre…
Browse files Browse the repository at this point in the history
…ssionMustHaveJustification)

* Avoid semantic analysis when simple name analysis will work. This step
  reuses cached information from SA1121 (UseBuiltInTypeAlias).
* Avoid calling `GetTypeByMetadataName` for every attribute in the
  compilation. Instead, this method is called once per compilation and
  cached.
  • Loading branch information
sharwell committed Aug 8, 2015
1 parent 6e92da9 commit 4ae9b5e
Showing 1 changed file with 46 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Helpers;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
Expand Down Expand Up @@ -54,25 +55,61 @@ public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeActionHonorExclusions(this.HandleAttributeNode, SyntaxKind.Attribute);
context.RegisterCompilationStartAction(HandleCompilationStart);
}

private void HandleAttributeNode(SyntaxNodeAnalysisContext context)
private static void HandleCompilationStart(CompilationStartAnalysisContext context)
{
var attribute = context.Node as AttributeSyntax;
if (attribute != null)
AnalyzerInstance instance = new AnalyzerInstance();
context.RegisterSyntaxNodeActionHonorExclusions(instance.HandleAttributeNode, SyntaxKind.Attribute);
}

/// <summary>
/// This class holds analyzer state information for analysis within a particular <see cref="Compilation"/>.
/// </summary>
private sealed class AnalyzerInstance
{
/// <summary>
/// A lazily-initialized reference to <see cref="SuppressMessageAttribute"/> within the context of a
/// particular <see cref="Compilation"/>.
/// </summary>
private INamedTypeSymbol suppressMessageAttribute;

public void HandleAttributeNode(SyntaxNodeAnalysisContext context)
{
var attribute = (AttributeSyntax)context.Node;

// Return fast if the name doesn't match and the file doesn't contain any using alias directives
if (!attribute.SyntaxTree.ContainsUsingAlias())
{
SimpleNameSyntax simpleNameSyntax = attribute.Name as SimpleNameSyntax;
if (simpleNameSyntax == null)
{
QualifiedNameSyntax qualifiedNameSyntax = attribute.Name as QualifiedNameSyntax;
simpleNameSyntax = qualifiedNameSyntax.Right;
}

if (simpleNameSyntax.Identifier.ValueText != nameof(SuppressMessageAttribute)
&& simpleNameSyntax.Identifier.ValueText != "SuppressMessage")
{
return;
}
}

SymbolInfo symbolInfo = context.SemanticModel.GetSymbolInfo(attribute);
ISymbol symbol = symbolInfo.Symbol;
if (symbol != null)
{
var suppressMessageType = context.SemanticModel.Compilation.GetTypeByMetadataName(typeof(SuppressMessageAttribute).FullName);
if (symbol.ContainingType == suppressMessageType)
if (this.suppressMessageAttribute == null)
{
this.suppressMessageAttribute = context.SemanticModel.Compilation.GetTypeByMetadataName(typeof(SuppressMessageAttribute).FullName);
}

if (symbol.ContainingType == this.suppressMessageAttribute)
{
foreach (var argument in attribute.ArgumentList.ChildNodes())
foreach (var attributeArgument in attribute.ArgumentList.Arguments)
{
var attributeArgument = argument as AttributeArgumentSyntax;
if (attributeArgument?.NameEquals?.Name?.Identifier.ValueText == nameof(SuppressMessageAttribute.Justification))
if (attributeArgument.NameEquals?.Name?.Identifier.ValueText == nameof(SuppressMessageAttribute.Justification))
{
// Check if the justification is not empty
var value = context.SemanticModel.GetConstantValue(attributeArgument.Expression);
Expand Down

0 comments on commit 4ae9b5e

Please sign in to comment.