Skip to content
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
53 changes: 53 additions & 0 deletions src/DataverseAnalyzer/PragmaWarningDisableMA0051Analyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

namespace DataverseAnalyzer;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class PragmaWarningDisableMA0051Analyzer : DiagnosticAnalyzer
{
private static readonly Lazy<DiagnosticDescriptor> LazyRule = new(() => new DiagnosticDescriptor(
"CT0011",
Resources.CT0011_Title,
Resources.CT0011_MessageFormat,
"Usage",
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: Resources.CT0011_Description));

public static DiagnosticDescriptor Rule => LazyRule.Value;

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

public override void Initialize(AnalysisContext context)
{
if (context is null)
throw new ArgumentNullException(nameof(context));

context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();

context.RegisterSyntaxNodeAction(AnalyzePragmaWarning, SyntaxKind.PragmaWarningDirectiveTrivia);
}

private static void AnalyzePragmaWarning(SyntaxNodeAnalysisContext context)
{
var pragma = (PragmaWarningDirectiveTriviaSyntax)context.Node;

if (!pragma.DisableOrRestoreKeyword.IsKind(SyntaxKind.DisableKeyword))
return;

foreach (var errorCode in pragma.ErrorCodes)
{
if (errorCode is IdentifierNameSyntax identifier &&
identifier.Identifier.ValueText == "MA0051")
{
context.ReportDiagnostic(Diagnostic.Create(Rule, pragma.GetLocation()));
return;
}
}
}
}
6 changes: 6 additions & 0 deletions src/DataverseAnalyzer/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions src/DataverseAnalyzer/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,13 @@
<data name="CT0010_Description" xml:space="preserve">
<value>Delete operations cannot have post-images because the record no longer exists after the delete operation. ImageType.Both is also invalid as it includes PostImage.</value>
</data>
<data name="CT0011_Title" xml:space="preserve">
<value>Do not suppress MA0051 (method too long)</value>
</data>
<data name="CT0011_MessageFormat" xml:space="preserve">
<value>Do not suppress MA0051. Split the method into smaller helper methods instead.</value>
</data>
<data name="CT0011_Description" xml:space="preserve">
<value>Suppressing MA0051 hides methods that are too long. Instead, split the method into smaller, focused helper methods to improve readability and maintainability.</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;

namespace DataverseAnalyzer.Tests;

public sealed class PragmaWarningDisableMA0051AnalyzerTests
{
[Fact]
public async Task PragmaDisableMA0051ShouldTrigger()
{
var source = """
class TestClass
{
#pragma warning disable MA0051
public void TestMethod() { }
#pragma warning restore MA0051
}
""";

var diagnostics = await GetDiagnosticsAsync(source);
Assert.Single(diagnostics);
Assert.Equal("CT0011", diagnostics[0].Id);
}

[Fact]
public async Task PragmaDisableMA0051WithOtherWarningsShouldTrigger()
{
var source = """
class TestClass
{
#pragma warning disable MA0051, CS0168
public void TestMethod() { }
#pragma warning restore MA0051, CS0168
}
""";

var diagnostics = await GetDiagnosticsAsync(source);
Assert.Single(diagnostics);
Assert.Equal("CT0011", diagnostics[0].Id);
}

[Fact]
public async Task PragmaRestoreMA0051ShouldNotTrigger()
{
var source = """
class TestClass
{
#pragma warning restore MA0051
public void TestMethod() { }
}
""";

var diagnostics = await GetDiagnosticsAsync(source);
Assert.Empty(diagnostics);
}

[Fact]
public async Task PragmaDisableOtherWarningShouldNotTrigger()
{
var source = """
class TestClass
{
#pragma warning disable CS0168
public void TestMethod() { }
#pragma warning restore CS0168
}
""";

var diagnostics = await GetDiagnosticsAsync(source);
Assert.Empty(diagnostics);
}

[Fact]
public async Task MultiplePragmaDisableMA0051ShouldTriggerMultipleTimes()
{
var source = """
class TestClass
{
#pragma warning disable MA0051
public void TestMethodA() { }
#pragma warning restore MA0051
#pragma warning disable MA0051
public void TestMethodB() { }
#pragma warning restore MA0051
}
""";

var diagnostics = await GetDiagnosticsAsync(source);
Assert.Equal(2, diagnostics.Length);
Assert.All(diagnostics, d => Assert.Equal("CT0011", d.Id));
}

private static async Task<Diagnostic[]> GetDiagnosticsAsync(string source)
{
var syntaxTree = CSharpSyntaxTree.ParseText(source, new CSharpParseOptions(LanguageVersion.Latest));
var references = new List<MetadataReference>
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
};

var compilation = CSharpCompilation.Create(
"TestAssembly",
new[] { syntaxTree },
references,
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

var analyzer = new PragmaWarningDisableMA0051Analyzer();
var compilationWithAnalyzers = compilation.WithAnalyzers(ImmutableArray.Create<DiagnosticAnalyzer>(analyzer));

var diagnostics = await compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync();
return diagnostics.Where(d => d.Id == "CT0011").ToArray();
}
}