Skip to content

Add Item #6 from the book #7

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

Merged
merged 8 commits into from
Jul 10, 2024
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
65 changes: 65 additions & 0 deletions docs/rules/ECS0006.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# ECS0006: Avoid stringly-typed APIs

This rule is described in detail in [Effective C#: 50 Specific Ways to Improve your C#](https://www.oreilly.com/library/view/effective-c-50/9780134579290/)

## Cause

Using string literals to represent member names or parameter names in APIs.

## Rule description

This rule identifies instances where string literals are used to refer to member names or parameter names. Using string literals in such contexts is prone to errors, especially during refactoring, as the string literals do not update automatically. The `nameof` operator should be used instead to ensure type safety and to facilitate easier refactoring.

## How to fix violations

Replace the string literal with the `nameof` operator to reference the member or parameter name.

## When to suppress warnings

Suppress warnings only if you have a valid reason for using string literals that cannot be replaced with the `nameof` operator. For example, if the string literal represents a dynamic value that cannot be determined at compile time.

## Example of a violation

### Description

Using a string literal to reference a member name.

### Code

```csharp
public class MyClass
{
public static void ExceptionMessage(object thisCantBeNull)
{
if (thisCantBeNull == null)
{
throw new ArgumentNullException(
"thisCantBeNull",
"We told you this cant be null");
}
}
}
```

## Example of how to fix

### Description

Replacing the string literal with the `nameof` operator.

### Code

```csharp
public class MyClass
{
public static void ExceptionMessage(object thisCantBeNull)
{
if (thisCantBeNull == null)
{
throw new ArgumentNullException(
nameof(thisCantBeNull),
"We told you this cant be null");
}
}
}
```
6 changes: 6 additions & 0 deletions effectivecsharpanalyzers.lutconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<LUTConfig Version="1.0">
<Repository />
<ParallelBuilds>true</ParallelBuilds>
<ParallelTestRuns>true</ParallelTestRuns>
<TestCaseTimeout>180000</TestCaseTimeout>
</LUTConfig>
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
ECS0002 | Maintainability | Info | PreferReadonlyOverConstAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/10c2d53afd688efe5a59097f76cb4edf33f6a474/docs/ECS0002.md)
ECS0006 | Refactoring | Warning | AvoidStringlyTypedApisAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/main/docs/ECS0006.md)
ECS1000 | Performance | Info | SpanAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/d00a4cc9f61e7d5b392894aad859e46c43a5611c/docs/ECS1000.md)
76 changes: 76 additions & 0 deletions src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
namespace EffectiveCSharp.Analyzers;

Check failure on line 1 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs#L1

Add or update the header of this file.

Check warning on line 1 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs#L1

Provide a 'CLSCompliant' attribute for assembly 'srcassembly.dll'.

Check warning on line 1 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs#L1

Provide a 'ComVisible' attribute for assembly 'srcassembly.dll'.

Check failure on line 1 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs#L1

Provide an 'AssemblyVersion' attribute for assembly 'srcassembly.dll'.

/// <summary>
/// A <see cref="DiagnosticAnalyzer"/> for Effective C# Item #6 - Avoid stringly typed APIs.
/// </summary>
/// <seealso cref="Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer" />
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AvoidStringlyTypedApisAnalyzer : DiagnosticAnalyzer
{
private const string DiagnosticId = DiagnosticIds.AvoidStringlyTypedApis;
private const string Title = "Avoid stringly-typed APIs";
private const string MessageFormat = "Use 'nameof({0})' instead of the string literal \"{0}\"";

private const string Description =
"Replace string literals representing member names with the nameof operator to ensure type safety.";

private const string Category = "Refactoring";

private static readonly DiagnosticDescriptor Rule = new(
DiagnosticId,
Title,
MessageFormat,
Category,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: Description,
helpLinkUri:

Check warning on line 27 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Check warning on line 27 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Check warning on line 27 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Check warning on line 27 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

Check warning on line 27 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

$"https://github.com/rjmurillo/EffectiveCSharp.Analyzers{ThisAssembly.GitCommitId}/docs/{DiagnosticId}.md");

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

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeLiteralExpression, SyntaxKind.StringLiteralExpression);
}

private static void AnalyzeLiteralExpression(SyntaxNodeAnalysisContext context)
{
LiteralExpressionSyntax literalExpression = (LiteralExpressionSyntax)context.Node;
string literalValue = literalExpression.Token.ValueText;

// Walk up the syntax tree to find the containing class or method
TypeDeclarationSyntax? containingClass = literalExpression.FirstAncestorOrSelf<TypeDeclarationSyntax>();
MethodDeclarationSyntax? containingMethod = literalExpression.FirstAncestorOrSelf<MethodDeclarationSyntax>();

SemanticModel semanticModel = context.SemanticModel;

if (containingClass != null
&& semanticModel.GetDeclaredSymbol(containingClass, context.CancellationToken) is { } containingTypeSymbol)
{
IEnumerable<string> memberNames = containingTypeSymbol.GetMembers().Select(member => member.Name);

if (memberNames.Contains(literalValue, StringComparer.Ordinal))
{
Diagnostic diagnostic = literalExpression.GetLocation().CreateDiagnostic(Rule, literalValue);
context.ReportDiagnostic(diagnostic);
}
}

if (containingMethod != null
&& semanticModel.GetDeclaredSymbol(containingMethod, context.CancellationToken) is { } methodSymbol)
{
IEnumerable<string> parameterNames = methodSymbol.Parameters.Select(parameter => parameter.Name);

if (parameterNames.Contains(literalValue, StringComparer.Ordinal))
{
Diagnostic diagnostic = literalExpression.GetLocation().CreateDiagnostic(Rule, literalValue);
context.ReportDiagnostic(diagnostic);
}
}
}
}
111 changes: 111 additions & 0 deletions src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
namespace EffectiveCSharp.Analyzers;

Check failure on line 1 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs#L1

Add or update the header of this file.

Check warning on line 1 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs#L1

Provide a 'CLSCompliant' attribute for assembly 'srcassembly.dll'.

Check warning on line 1 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs#L1

Provide a 'ComVisible' attribute for assembly 'srcassembly.dll'.

Check failure on line 1 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs#L1

Provide an 'AssemblyVersion' attribute for assembly 'srcassembly.dll'.

/// <summary>
/// A <see cref="CodeFixProvider"/> that provides a code fix for the <see cref="AvoidStringlyTypedApisAnalyzer"/>.
/// </summary>
/// <seealso cref="Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider" />
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AvoidStringlyTypedApisCodeFixProvider))]
[Shared]
public class AvoidStringlyTypedApisCodeFixProvider : CodeFixProvider
{
private const string Title = "Use nameof operator";

/// <inheritdoc/>
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(DiagnosticIds.AvoidStringlyTypedApis);

/// <inheritdoc/>
public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;

/// <inheritdoc/>
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
Diagnostic diagnostic = context.Diagnostics.First();
TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;

SyntaxNode? node = root?.FindNode(diagnosticSpan);

LiteralExpressionSyntax? literalExpression = node switch
{
// Check if the node is a LiteralExpressionSyntax directly
LiteralExpressionSyntax literalNode => literalNode,

// Check if the node is an ArgumentSyntax containing a LiteralExpressionSyntax
ArgumentSyntax { Expression: LiteralExpressionSyntax argLiteralNode } => argLiteralNode,

_ => null

Check warning on line 36 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Check warning on line 36 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Check warning on line 36 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

};

if (literalExpression != null)
{
context.RegisterCodeFix(
CodeAction.Create(
title: Title,
createChangedSolution: c => UseNameofOperatorAsync(context.Document, literalExpression, c),
equivalenceKey: Title),
diagnostic);
}
}

private static async Task<Solution> UseNameofOperatorAsync(Document document, LiteralExpressionSyntax? literalExpression, CancellationToken cancellationToken)
{
string literalValue = literalExpression?.Token.ValueText;

Check warning on line 52 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Converting null literal or possible null value to non-nullable type.

Check warning on line 52 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Converting null literal or possible null value to non-nullable type.

Check warning on line 52 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Converting null literal or possible null value to non-nullable type.

Check warning on line 52 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Converting null literal or possible null value to non-nullable type.

Check warning on line 52 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

Converting null literal or possible null value to non-nullable type.

Check warning on line 52 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

Converting null literal or possible null value to non-nullable type.

// Walk up the syntax tree to find the containing class or method
TypeDeclarationSyntax? containingClass = literalExpression?.FirstAncestorOrSelf<TypeDeclarationSyntax>();
MethodDeclarationSyntax? containingMethod = literalExpression?.FirstAncestorOrSelf<MethodDeclarationSyntax>();

string? nameofExpressionText = null;

if (containingClass != null)
{
SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

if (semanticModel.GetDeclaredSymbol(containingClass, cancellationToken) is { } containingTypeSymbol)
{
IEnumerable<string> memberNames = containingTypeSymbol.GetMembers().Select(member => member.Name);

if (memberNames.Contains(literalValue))

Check warning on line 68 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Use an overload that has a IEqualityComparer<string> or IComparer<string> parameter (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0002.md)

Check warning on line 68 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Use an overload that has a IEqualityComparer<string> or IComparer<string> parameter (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0002.md)

Check warning on line 68 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

Use an overload that has a IEqualityComparer<string> or IComparer<string> parameter (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0002.md)
{
nameofExpressionText = $"nameof({literalValue})";
}
}
}

if (nameofExpressionText == null && containingMethod != null)
{
SemanticModel? semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

if (semanticModel.GetDeclaredSymbol(containingMethod, cancellationToken) is { } methodSymbol)
{
IEnumerable<string> parameterNames = methodSymbol.Parameters.Select(parameter => parameter.Name);

if (parameterNames.Contains(literalValue))

Check warning on line 83 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Use an overload that has a IEqualityComparer<string> or IComparer<string> parameter (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0002.md)

Check warning on line 83 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Use an overload that has a IEqualityComparer<string> or IComparer<string> parameter (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0002.md)

Check warning on line 83 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

Use an overload that has a IEqualityComparer<string> or IComparer<string> parameter (https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0002.md)
{
nameofExpressionText = $"nameof({literalValue})";
}
}
}

if (nameofExpressionText == null)
{
return document.Project.Solution;
}

if (literalExpression != null)

Check warning on line 95 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Change this condition so that it does not always evaluate to 'True'. (https://rules.sonarsource.com/csharp/RSPEC-2589)

Check warning on line 95 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Change this condition so that it does not always evaluate to 'True'. (https://rules.sonarsource.com/csharp/RSPEC-2589)

Check warning on line 95 in src/EffectiveCSharp.Analyzers/AvoidStringlyTypedApisCodeFixProvider.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

Change this condition so that it does not always evaluate to 'True'. (https://rules.sonarsource.com/csharp/RSPEC-2589)
{
ExpressionSyntax nameofExpression = SyntaxFactory.ParseExpression(nameofExpressionText)
.WithTriviaFrom(literalExpression);

SyntaxNode? root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
SyntaxNode? newRoot = root?.ReplaceNode(literalExpression, nameofExpression);

if (newRoot != null)
{
return document.WithSyntaxRoot(newRoot).Project.Solution;
}
}

return document.Project.Solution;
}
}
1 change: 1 addition & 0 deletions src/EffectiveCSharp.Analyzers/DiagnosticIds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
internal static class DiagnosticIds
{
internal const string PreferReadonlyOverConst = "ECS0002";
internal const string AvoidStringlyTypedApis = "ECS0006";
internal const string UseSpanInstead = "ECS1000";
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal class {name}
}

[Benchmark]
public async Task Ecs1000WithDiagnostics()
public async Task Ecs0002WithDiagnostics()
{
ImmutableArray<Diagnostic> diagnostics =
(await TestCompilation!
Expand All @@ -50,7 +50,7 @@ public async Task Ecs1000WithDiagnostics()
}

[Benchmark(Baseline = true)]
public async Task Ecs1000Baseline()
public async Task Ecs0002Baseline()
{
ImmutableArray<Diagnostic> diagnostics =
(await BaselineCompilation!
Expand Down
75 changes: 75 additions & 0 deletions tests/EffectiveCSharp.Analyzers.Benchmarks/Ecs0006Benchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
namespace EffectiveCSharp.Analyzers.Benchmarks;

Check failure on line 1 in tests/EffectiveCSharp.Analyzers.Benchmarks/Ecs0006Benchmarks.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/EffectiveCSharp.Analyzers.Benchmarks/Ecs0006Benchmarks.cs#L1

Add or update the header of this file.

Check warning on line 1 in tests/EffectiveCSharp.Analyzers.Benchmarks/Ecs0006Benchmarks.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/EffectiveCSharp.Analyzers.Benchmarks/Ecs0006Benchmarks.cs#L1

Provide a 'CLSCompliant' attribute for assembly 'srcassembly.dll'.

Check warning on line 1 in tests/EffectiveCSharp.Analyzers.Benchmarks/Ecs0006Benchmarks.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/EffectiveCSharp.Analyzers.Benchmarks/Ecs0006Benchmarks.cs#L1

Provide a 'ComVisible' attribute for assembly 'srcassembly.dll'.

Check failure on line 1 in tests/EffectiveCSharp.Analyzers.Benchmarks/Ecs0006Benchmarks.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/EffectiveCSharp.Analyzers.Benchmarks/Ecs0006Benchmarks.cs#L1

Provide an 'AssemblyVersion' attribute for assembly 'srcassembly.dll'.

[InProcess]
[MemoryDiagnoser]
public class Ecs0006Benchmarks
{
private static CompilationWithAnalyzers? BaselineCompilation { get; set; }

private static CompilationWithAnalyzers? TestCompilation { get; set; }

[IterationSetup]
[SuppressMessage("Usage", "VSTHRD002:Avoid problematic synchronous waits", Justification = "Async setup not supported in BenchmarkDotNet.See https://github.com/dotnet/BenchmarkDotNet/issues/2442.")]
public static void SetupCompilation()
{
List<(string Name, string Content)> sources = [];
for (int index = 0; index < Constants.NumberOfCodeFiles; index++)
{
string name = $"TypeName{index}";
sources.Add((name, @$"
using System;

public class {name}
{{
public static void ExceptionMessage(object thisCantBeNull)
{{
if (thisCantBeNull == null)
{{
throw new ArgumentNullException(
""thisCantBeNull"",
""We told you this cant be null"");
}}
}}
}}
"));
}

(BaselineCompilation, TestCompilation) =
BenchmarkCSharpCompilationFactory
.CreateAsync<AvoidStringlyTypedApisAnalyzer>(sources.ToArray())
.GetAwaiter()
.GetResult();
}

[Benchmark]
public async Task Ecs0006WithDiagnostics()
{
ImmutableArray<Diagnostic> diagnostics =
(await TestCompilation!
.GetAnalysisResultAsync(CancellationToken.None)
.ConfigureAwait(false))
.AssertValidAnalysisResult()
.GetAllDiagnostics();

if (diagnostics.Length != Constants.NumberOfCodeFiles)
{
throw new InvalidOperationException($"Expected '{Constants.NumberOfCodeFiles:N0}' analyzer diagnostics but found '{diagnostics.Length}'");
}
}

[Benchmark(Baseline = true)]
public async Task Ecs0006Baseline()
{
ImmutableArray<Diagnostic> diagnostics =
(await BaselineCompilation!
.GetAnalysisResultAsync(CancellationToken.None)
.ConfigureAwait(false))
.AssertValidAnalysisResult()
.GetAllDiagnostics();

if (diagnostics.Length != 0)
{
throw new InvalidOperationException($"Expected no analyzer diagnostics but found '{diagnostics.Length}'");
}
}
}
Loading
Loading