Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce the number of memory allocations in SA1404 (CodeAnalysisSuppressionMustHaveJustification) #1157

Merged
merged 2 commits into from
Aug 8, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ public async Task TestSuppressionWithStringLiteralAsync()
public void Bar()
{

}
}";

await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestSuppressionWithStringLiteralAndUsingAliasDirectiveAsync()
{
var testCode = @"using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute;
public class Foo
{
[SuppressMessage(null, null, Justification = ""a justification"")]
public void Bar()
{

}
}";

Expand All @@ -43,6 +59,42 @@ public void Bar()
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestSuppressionWithUsingAliasDirectiveAndNoJustificationAsync()
{
var testCode = @"using SuppressMessageAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute;
public class Foo
{
[SuppressMessage(null, null)]
public void Bar()
{

}
}";

DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(4, 6);

await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestSuppressionWithUsingDifferentAliasDirectiveAndNoJustificationAsync()
{
var testCode = @"using MySuppressionAttribute = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute;
public class Foo
{
[MySuppression(null, null)]
public void Bar()
{

}
}";

DiagnosticResult expected = this.CSharpDiagnostic().WithLocation(4, 6);

await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestSuppressionWithEmptyJustificationAsync()
{
Expand Down
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