Skip to content

Commit

Permalink
Add suppressor support to CSharpVerifier and new base classes
Browse files Browse the repository at this point in the history
  • Loading branch information
bradwilson committed Mar 2, 2024
1 parent ab53655 commit 687c8f1
Show file tree
Hide file tree
Showing 4 changed files with 267 additions and 0 deletions.
177 changes: 177 additions & 0 deletions src/xunit.analyzers.tests/Utility/CSharpVerifier.Suppressors.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Testing;

public partial class CSharpVerifier<TAnalyzer>
{
// ----- Multi-version -----

/// <summary>
/// Verify that an analyzer was used to suppress another analyzers. Runs against
/// xUnit.net v2 and v3, using C# 6.
/// </summary>
/// <param name="source">The code to verify</param>
/// <param name="suppressedAnalyzer">The analyzer that is expected to be suppressed</param>
/// <param name="diagnostics">Any expected diagnostics that still exist after the suppression</param>
public static async Task VerifySuppressor(
string source,
DiagnosticAnalyzer suppressedAnalyzer,
params DiagnosticResult[] diagnostics)
{
await VerifySuppressorV2(LanguageVersion.CSharp6, [source], [suppressedAnalyzer], diagnostics);
await VerifySuppressorV3(LanguageVersion.CSharp6, [source], [suppressedAnalyzer], diagnostics);
}

/// <summary>
/// Verify that an analyzer was used to suppress another analyzers. Runs against
/// xUnit.net v2 and v3, using the provided version of C#.
/// </summary>
/// <param name="source">The code to verify</param>
/// <param name="suppressedAnalyzer">The analyzer that is expected to be suppressed</param>
/// <param name="diagnostics">Any expected diagnostics that still exist after the suppression</param>
public static async Task VerifySuppressor(
LanguageVersion languageVersion,
string source,
DiagnosticAnalyzer suppressedAnalyzer,
params DiagnosticResult[] diagnostics)
{
await VerifySuppressorV2(languageVersion, [source], [suppressedAnalyzer], diagnostics);
await VerifySuppressorV3(languageVersion, [source], [suppressedAnalyzer], diagnostics);
}

/// <summary>
/// Verify that an analyzer was used to suppress one or more other analyzers. Runs against
/// xUnit.net v2 and v3, using the provided version of C#.
/// </summary>
/// <param name="languageVersion">The language version to compile with</param>
/// <param name="sources">The code to verify</param>
/// <param name="suppressedAnalyzers">The analyzer(s) that are expected to be suppressed</param>
/// <param name="diagnostics">Any expected diagnostics that still exist after the suppression</param>
public static async Task VerifySuppressor(
LanguageVersion languageVersion,
string[] sources,
DiagnosticAnalyzer[] suppressedAnalyzers,
params DiagnosticResult[] diagnostics)
{
await VerifySuppressorV2(languageVersion, sources, suppressedAnalyzers, diagnostics);
await VerifySuppressorV3(languageVersion, sources, suppressedAnalyzers, diagnostics);
}

// ----- v2 -----

/// <summary>
/// Verify that an analyzer was used to suppress another analyzers. Runs against
/// xUnit.net v2, using C# 6.
/// </summary>
/// <param name="source">The code to verify</param>
/// <param name="suppressedAnalyzer">The analyzer that is expected to be suppressed</param>
/// <param name="diagnostics">Any expected diagnostics that still exist after the suppression</param>
public static Task VerifySuppressorV2(
string source,
DiagnosticAnalyzer suppressedAnalyzer,
params DiagnosticResult[] diagnostics) =>
VerifySuppressorV2(LanguageVersion.CSharp6, [source], [suppressedAnalyzer], diagnostics);

/// <summary>
/// Verify that an analyzer was used to suppress another analyzers. Runs against
/// xUnit.net v2, using the provided version of C#.
/// </summary>
/// <param name="languageVersion">The language version to compile with</param>
/// <param name="source">The code to verify</param>
/// <param name="suppressedAnalyzer">The analyzer that is expected to be suppressed</param>
/// <param name="diagnostics">Any expected diagnostics that still exist after the suppression</param>
public static Task VerifySuppressorV2(
LanguageVersion languageVersion,
string source,
DiagnosticAnalyzer suppressedAnalyzer,
params DiagnosticResult[] diagnostics) =>
VerifySuppressorV2(languageVersion, [source], [suppressedAnalyzer], diagnostics);

/// <summary>
/// Verify that an analyzer was used to suppress one or more other analyzers. Runs against
/// xUnit.net v2, using the provided version of C#.
/// </summary>
/// <param name="languageVersion">The language version to compile with</param>
/// <param name="sources">The code to verify</param>
/// <param name="suppressedAnalyzers">The analyzer(s) that are expected to be suppressed</param>
/// <param name="diagnostics">Any expected diagnostics that still exist after the suppression</param>
public static Task VerifySuppressorV2(
LanguageVersion languageVersion,
string[] sources,
DiagnosticAnalyzer[] suppressedAnalyzers,
params DiagnosticResult[] diagnostics)
{
var test = new TestV2(languageVersion);

foreach (var suppressedAnalyzer in suppressedAnalyzers)
test.AddDiagnosticAnalyzer(suppressedAnalyzer);

foreach (var source in sources)
test.TestState.Sources.Add(source);

test.TestState.ExpectedDiagnostics.AddRange(diagnostics);
test.TestState.OutputKind = OutputKind.ConsoleApplication;
test.TestState.Sources.Add("internal class Program { public static void Main() { } }");
return test.RunAsync();
}

// ----- v3 -----

/// <summary>
/// Verify that an analyzer was used to suppress another analyzers. Runs against
/// xUnit.net v3, using C# 6.
/// </summary>
/// <param name="source">The code to verify</param>
/// <param name="suppressedAnalyzer">The analyzer that is expected to be suppressed</param>
/// <param name="diagnostics">Any expected diagnostics that still exist after the suppression</param>
public static Task VerifySuppressorV3(
string source,
DiagnosticAnalyzer suppressedAnalyzer,
params DiagnosticResult[] diagnostics) =>
VerifySuppressorV3(LanguageVersion.CSharp6, [source], [suppressedAnalyzer], diagnostics);

/// <summary>
/// Verify that an analyzer was used to suppress another analyzers. Runs against
/// xUnit.net v3, using the provided version of C#.
/// </summary>
/// <param name="languageVersion">The language version to compile with</param>
/// <param name="source">The code to verify</param>
/// <param name="suppressedAnalyzer">The analyzer that is expected to be suppressed</param>
/// <param name="diagnostics">Any expected diagnostics that still exist after the suppression</param>
public static Task VerifySuppressorV3(
LanguageVersion languageVersion,
string source,
DiagnosticAnalyzer suppressedAnalyzer,
params DiagnosticResult[] diagnostics) =>
VerifySuppressorV3(languageVersion, [source], [suppressedAnalyzer], diagnostics);

/// <summary>
/// Verify that an analyzer was used to suppress one or more other analyzers. Runs against
/// xUnit.net v3, using the provided version of C#.
/// </summary>
/// <param name="languageVersion">The language version to compile with</param>
/// <param name="sources">The code to verify</param>
/// <param name="suppressedAnalyzers">The analyzer(s) that are expected to be suppressed</param>
/// <param name="diagnostics">Any expected diagnostics that still exist after the suppression</param>
public static Task VerifySuppressorV3(
LanguageVersion languageVersion,
string[] sources,
DiagnosticAnalyzer[] suppressedAnalyzers,
params DiagnosticResult[] diagnostics)
{
var test = new TestV3(languageVersion);

foreach (var suppressedAnalyzer in suppressedAnalyzers)
test.AddDiagnosticAnalyzer(suppressedAnalyzer);

foreach (var source in sources)
test.TestState.Sources.Add(source);

test.TestState.ExpectedDiagnostics.AddRange(diagnostics);
test.TestState.OutputKind = OutputKind.ConsoleApplication;
test.TestState.Sources.Add("internal class Program { public static void Main() { } }");
return test.RunAsync();
}
}
58 changes: 58 additions & 0 deletions src/xunit.analyzers/Utility/XunitDiagnosticSuppressor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;

namespace Xunit.Analyzers;

/// <summary>
/// Base class for diagnostic suppressors which support xUnit.net v2 and v3.
/// </summary>
public abstract class XunitDiagnosticSuppressor : DiagnosticSuppressor
{
protected XunitDiagnosticSuppressor(SuppressionDescriptor descriptor) =>
SupportedSuppressions = new[] { descriptor }.ToImmutableArray();

protected SuppressionDescriptor Descriptor => SupportedSuppressions[0];

/// <inheritdoc/>
public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions { get; }

/// <summary>
/// Override this factory method to influence the creation of <see cref="XunitContext"/>.
/// Typically used by derived classes wanting to provide version overrides for specific
/// references.
/// </summary>
/// <param name="compilation">The Roslyn compilation context</param>
protected virtual XunitContext CreateXunitContext(Compilation compilation) =>
new(compilation);

/// <inheritdoc/>
public sealed override void ReportSuppressions(SuppressionAnalysisContext context)
{
var xunitContext = CreateXunitContext(context.Compilation);

if (ShouldAnalyze(xunitContext))
foreach (var diagnostic in context.ReportedDiagnostics)
if (ShouldSuppress(diagnostic, context, xunitContext))
context.ReportSuppression(Suppression.Create(Descriptor, diagnostic));
}

/// <summary>
/// Override this method to influence when we should consider diagnostic analysis. By
/// default analyzes all assemblies that have a reference to xUnit.net v2 or v3.
/// </summary>
/// <param name="xunitContext">The xUnit.net context</param>
/// <returns>Return <c>true</c> to analyze source; return <c>false</c> to skip analysis</returns>
protected virtual bool ShouldAnalyze(XunitContext xunitContext) =>
Guard.ArgumentNotNull(xunitContext).HasV2References || xunitContext.HasV3References;

/// <summary>
/// Analyzes the given diagnostic to determine if it should be suppressed.
/// </summary>
/// <param name="context">The Roslyn supression analysis context</param>
/// <param name="xunitContext">The xUnit.net context</param>
protected abstract bool ShouldSuppress(
Diagnostic diagnostic,
SuppressionAnalysisContext context,
XunitContext xunitContext);
}
16 changes: 16 additions & 0 deletions src/xunit.analyzers/Utility/XunitV2DiagnosticSuppressor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.CodeAnalysis;

namespace Xunit.Analyzers;

/// <summary>
/// Base class for diagnostic suppressors which support xUnit.net v2 only.
/// </summary>
public abstract class XunitV2DiagnosticSuppressor : XunitDiagnosticSuppressor
{
protected XunitV2DiagnosticSuppressor(SuppressionDescriptor descriptor) :
base(descriptor)
{ }

protected override bool ShouldAnalyze(XunitContext xunitContext) =>
Guard.ArgumentNotNull(xunitContext).HasV2References;
}
16 changes: 16 additions & 0 deletions src/xunit.analyzers/Utility/XunitV3DiagnosticSuppressor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.CodeAnalysis;

namespace Xunit.Analyzers;

/// <summary>
/// Base class for diagnostic suppressors which support xUnit.net v3 only.
/// </summary>
public abstract class XunitV3DiagnosticSuppressor : XunitDiagnosticSuppressor
{
protected XunitV3DiagnosticSuppressor(SuppressionDescriptor descriptor) :
base(descriptor)
{ }

protected override bool ShouldAnalyze(XunitContext xunitContext) =>
Guard.ArgumentNotNull(xunitContext).HasV3References;
}

0 comments on commit 687c8f1

Please sign in to comment.