Skip to content

Add Item #9 - minimize boxing and unboxing #15

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 38 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
6213cba
Add docs for ECS0009: minimize boxing and unboxing
rjmurillo Jul 12, 2024
495e9dc
Move AvoidStringlyTypedApis analyzer to info
rjmurillo Jul 13, 2024
c50fe06
Add ECS0009: minimize boxing and unboxing
rjmurillo Jul 13, 2024
d498df6
Improved FP of boxing detection
rjmurillo Jul 13, 2024
e5fe5a7
Improve FP of boxing detection when using record types
rjmurillo Jul 13, 2024
c8fc93a
Refactor IOperation methods to extensions
rjmurillo Jul 13, 2024
47e899d
Simplify ECS0009 implementation
rjmurillo Jul 13, 2024
010d497
Add analyzer for copying of value types to heap
rjmurillo Jul 16, 2024
6a1532c
Rename analyzer to match convention
rjmurillo Jul 16, 2024
439f9dc
Move conjunction operators to beginning of line
rjmurillo Jul 16, 2024
9254f29
Add composite analyzer (tests all analyzers)
rjmurillo Jul 16, 2024
10b7c22
Add additional positive and negative cases for boxing detection
rjmurillo Jul 16, 2024
95f096a
Update test harness to use the first descriptor encountered
rjmurillo Jul 17, 2024
ea99939
Add composite analyzer for the boxing tests
rjmurillo Jul 17, 2024
faa9f36
Add CLSCompliantAttribute to analyzers assembly
rjmurillo Jul 23, 2024
3f0f219
Fix static code analysis warnings
rjmurillo Jul 23, 2024
30926ed
Add ComVisible and CLSCompliant attributes to compiler.props for all …
rjmurillo Jul 23, 2024
e7a3491
Remove extra line break in ECS0009.md doc
rjmurillo Jul 24, 2024
48fcd88
Redunce number of conditional operators by merging and using pattern …
rjmurillo Jul 24, 2024
c1ce486
Add documentation to extension methods
rjmurillo Jul 24, 2024
ab34516
Simplify implementation of ECS0009
rjmurillo Jul 24, 2024
3a3bb1d
Add new test for Dictionary`2
rjmurillo Jul 24, 2024
75ebf14
Switch to RegisterCompilationStartAction
rjmurillo Jul 24, 2024
7cac1c3
Add checks to return when encountering a triggering condition for Dic…
rjmurillo Jul 24, 2024
08d4d7c
Correct name for local function
rjmurillo Jul 24, 2024
2cf39ce
Amend the case to only report on writable structs
rjmurillo Jul 24, 2024
53e1bf0
Invert an if to reduce the amount of code nested
rjmurillo Jul 24, 2024
bc2ea98
Convert switch to if statement to improve readability
rjmurillo Jul 24, 2024
676b0ea
Add assert for cases that aren't list or dictionary so we know what e…
rjmurillo Jul 24, 2024
0e3297d
Suppress analyzer warnings in test code
rjmurillo Jul 24, 2024
12e2839
Clean up analyzer warnings on boxing analyzer
rjmurillo Jul 24, 2024
62f97a8
Convert theory to TheoryData<>
rjmurillo Jul 24, 2024
d895984
Add all supported .NET Framework and .NET versions to test matrix
rjmurillo Jul 24, 2024
77ecee2
Update variable names in test data extensions
rjmurillo Jul 24, 2024
8d03c49
Add IFDEF for runtime version. Spans were only available in 6 and later
rjmurillo Jul 24, 2024
01bc84d
Fix code analysis items
rjmurillo Jul 24, 2024
ea9e660
Update benchmark for ECS0009
rjmurillo Jul 24, 2024
b380ff1
Merge branch 'main' into feature/use-null-conditional-operator
rjmurillo Jul 24, 2024
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
9 changes: 9 additions & 0 deletions build/targets/compiler/Compiler.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,13 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<AssemblyAttribute Include="System.CLSCompliantAttribute">
<_Parameter1>false</_Parameter1>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Runtime.InteropServices.ComVisibleAttribute">
<_Parameter1>false</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
</Project>
73 changes: 73 additions & 0 deletions docs/rules/ECS0009.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# ECS0009: Minimize boxing and unboxing

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

Value types can be converted to `System.Object` or any interface reference. Those conversions may happen implicitly, complicating the task of finding them. The boxing and unboxing operations make copies where you might not expect. That causes bugs.Boxing and unboxing operations can degrade performance and cause subtle bugs through those copies. These operations occur when a value type is converted to a reference type and vice versa. Be on the lookout for any constructs that convert value types to either `System.Object` or interface types: placing values in collections, calling methods defined in `System.Object`, and casts to `System.Object`.

## Rule description

This rule detects scenarios where boxing and unboxing occur implicitly or explicitly. It aims to help developers identify and minimize these operations to improve performance and avoid potential issues.

## How to fix violations

To fix violations, consider using generics, value type collections, or other means to avoid converting value types to reference types.

## When to suppress warnings

Suppress warnings if boxing or unboxing is necessary and there is no performance-critical impact, or if the code is optimized for readability and maintainability rather than performance.

## Example of a violation

### Description

Assigning a value type to a reference type or passing a value type to a method that expects a reference type causes boxing.

### Code

```csharp
int i = 5;
object o = i; // boxing
```

Boxing may also occur in compiler-generated code implicitly.

```csharp
var attendees = new List<Person>();
var p = new Person { Name = "Old Name" };
attendees.Add(p);

// Try to change the name
var p2 = attendees[0];
p2.Name = "New Name"; // Boxing occurs here

// Writes "Old Name":
Console.WriteLine(attendees[0].ToString());
```

## Example of how to fix

### Description

Use collections or methods that avoid boxing and unboxing operations.

### Code

```csharp
int i = 5;
int j = i; // No boxing
```

For the `Person` value type, create an immutable value type.

```csharp
public struct Person
{
public string Name { get; }

public Person(string name) => Name = name;

public override string ToString() => Name;
}
```
3 changes: 2 additions & 1 deletion src/EffectiveCSharp.Analyzers/AnalyzerReleases.Unshipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
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)
ECS0006 | Refactoring | Info | AvoidStringlyTypedApisAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers6213cba8473dac61d6132e205550884eae1c94bf/docs/ECS0006.md)
ECS0009 | Performance | Info | MinimizeBoxingUnboxingAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/6213cba8473dac61d6132e205550884eae1c94bf/docs/ECS0009.md)
ECS1000 | Performance | Info | SpanAnalyzer, [Documentation](https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/d00a4cc9f61e7d5b392894aad859e46c43a5611c/docs/ECS1000.md)
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ public class AvoidStringlyTypedApisAnalyzer : DiagnosticAnalyzer
Title,
MessageFormat,
Category,
DiagnosticSeverity.Warning,
DiagnosticSeverity.Info,
isEnabledByDefault: true,
description: Description,
helpLinkUri:
$"https://github.com/rjmurillo/EffectiveCSharp.Analyzers{ThisAssembly.GitCommitId}/docs/{DiagnosticId}.md");
helpLinkUri: $"https://github.com/rjmurillo/EffectiveCSharp.Analyzers{ThisAssembly.GitCommitId}/docs/{DiagnosticId}.md");

/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
Diagnostic diagnostic = context.Diagnostics.First();
Diagnostic diagnostic = context.Diagnostics[0];
TextSpan diagnosticSpan = diagnostic.Location.SourceSpan;

SyntaxNode? node = root?.FindNode(diagnosticSpan);
Expand All @@ -33,7 +33,7 @@
// Check if the node is an ArgumentSyntax containing a LiteralExpressionSyntax
ArgumentSyntax { Expression: LiteralExpressionSyntax argLiteralNode } => argLiteralNode,

_ => null
_ => null,
};

if (literalExpression != null)
Expand All @@ -49,7 +49,7 @@

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

// Walk up the syntax tree to find the containing class or method
TypeDeclarationSyntax? containingClass = literalExpression?.FirstAncestorOrSelf<TypeDeclarationSyntax>();
Expand All @@ -65,7 +65,7 @@
{
IEnumerable<string> memberNames = containingTypeSymbol.GetMembers().Select(member => member.Name);

if (memberNames.Contains(literalValue))
if (memberNames.Contains(literalValue, StringComparer.Ordinal))
{
nameofExpressionText = $"nameof({literalValue})";
}
Expand All @@ -80,7 +80,7 @@
{
IEnumerable<string> parameterNames = methodSymbol.Parameters.Select(parameter => parameter.Name);

if (parameterNames.Contains(literalValue))
if (parameterNames.Contains(literalValue, StringComparer.Ordinal))
{
nameofExpressionText = $"nameof({literalValue})";
}
Expand All @@ -92,7 +92,7 @@
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 (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)

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);
Expand Down
5 changes: 5 additions & 0 deletions src/EffectiveCSharp.Analyzers/Common/DiagnosticExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@

internal static class DiagnosticExtensions
{
[DebuggerStepThrough]
internal static Diagnostic CreateDiagnostic(
this SyntaxNode node,
DiagnosticDescriptor rule,
params object?[]? messageArgs)
=> node.CreateDiagnostic(rule, properties: null, messageArgs);

[DebuggerStepThrough]
internal static Diagnostic CreateDiagnostic(
this SyntaxNode node,
DiagnosticDescriptor rule,
ImmutableDictionary<string, string?>? properties,
params object?[]? messageArgs)
=> node.CreateDiagnostic(rule, additionalLocations: ImmutableArray<Location>.Empty, properties, messageArgs);

[DebuggerStepThrough]
internal static Diagnostic CreateDiagnostic(
this SyntaxNode node,
DiagnosticDescriptor rule,
Expand All @@ -29,6 +32,7 @@ internal static Diagnostic CreateDiagnostic(
properties: properties,
messageArgs: messageArgs);

[DebuggerStepThrough]
internal static Diagnostic CreateDiagnostic(
this Location location,
DiagnosticDescriptor rule,
Expand All @@ -46,6 +50,7 @@ internal static Diagnostic CreateDiagnostic(
params object?[]? messageArgs)
=> location.CreateDiagnostic(rule, ImmutableArray<Location>.Empty, properties, messageArgs);

[DebuggerStepThrough]
internal static Diagnostic CreateDiagnostic(
this Location location,
DiagnosticDescriptor rule,
Expand Down
28 changes: 28 additions & 0 deletions src/EffectiveCSharp.Analyzers/Common/IOperationExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace EffectiveCSharp.Analyzers.Common;

internal static class IOperationExtensions
{
/// <summary>
/// Determines if a given operation involves boxing through type conversion.
/// </summary>
/// <param name="operation">The operation to check.</param>
/// <returns>True if the operation is a boxing conversion, otherwise false.</returns>
internal static bool IsBoxingOperation(this IOperation? operation)
=> operation is IConversionOperation { Operand.Type.IsValueType: true, Type.IsReferenceType: true };

/// <summary>
/// Determines if a given operation involves unboxing through type conversion.
/// </summary>
/// <param name="operation">The operation to check.</param>
/// <returns>True if the operation is an unboxing conversion, otherwise false.</returns>
internal static bool IsUnboxingOperation(this IOperation? operation)
=> operation is IConversionOperation { Operand.Type.IsReferenceType: true, Type.IsValueType: true };

/// <summary>
/// Determines if a given operation involves boxing or unboxing through type conversion.
/// </summary>
/// <param name="operation">The operation to check.</param>
/// <returns>True if the operation is a boxing or unboxing conversion, otherwise false.</returns>
internal static bool IsBoxingOrUnboxingOperation(this IOperation? operation)
=> operation.IsBoxingOperation() || operation.IsUnboxingOperation();
}
2 changes: 2 additions & 0 deletions src/EffectiveCSharp.Analyzers/DiagnosticIds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ internal static class DiagnosticIds
{
internal const string PreferReadonlyOverConst = "ECS0002";
internal const string AvoidStringlyTypedApis = "ECS0006";
internal const string MinimizeBoxingUnboxing = "ECS0009";
internal const string BeAwareOfValueTypeCopyInReferenceTypes = "ECS0009";
internal const string UseSpanInstead = "ECS1000";
}
1 change: 1 addition & 0 deletions src/EffectiveCSharp.Analyzers/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
global using Microsoft.CodeAnalysis.CSharp;
global using Microsoft.CodeAnalysis.CSharp.Syntax;
global using Microsoft.CodeAnalysis.Diagnostics;
global using Microsoft.CodeAnalysis.Operations;
global using Microsoft.CodeAnalysis.Text;
122 changes: 122 additions & 0 deletions src/EffectiveCSharp.Analyzers/MinimizeBoxingUnboxingAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
namespace EffectiveCSharp.Analyzers;

/// <summary>
/// A <see cref="DiagnosticAnalyzer"/> for Effective C# Item #9 - Minimize boxing and unboxing.
/// </summary>
/// <seealso cref="Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer" />
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class MinimizeBoxingUnboxingAnalyzer : DiagnosticAnalyzer
{
private const string Id = DiagnosticIds.MinimizeBoxingUnboxing;

private static readonly DiagnosticDescriptor Rule = new(
id: Id,
title: "Minimize boxing and unboxing",
messageFormat: "Consider using an alternative implementation to avoid boxing and unboxing",
category: "Performance",
defaultSeverity: DiagnosticSeverity.Info,
isEnabledByDefault: true,
helpLinkUri: $"https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/{ThisAssembly.GitCommitId}/docs/{Id}.md");

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

/// <inheritdoc />
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(compilationStartAnalysisContext =>
{
INamedTypeSymbol? dictionarySymbol = compilationStartAnalysisContext.Compilation.GetTypeByMetadataName("System.Collections.Generic.Dictionary`2");
INamedTypeSymbol? listSymbol = compilationStartAnalysisContext.Compilation.GetTypeByMetadataName("System.Collections.Generic.List`1");

compilationStartAnalysisContext.RegisterOperationAction(AnalyzeOperation, OperationKind.Conversion);
compilationStartAnalysisContext.RegisterSyntaxNodeAction(
syntaxNodeContext => AnalyzeNode(syntaxNodeContext, dictionarySymbol, listSymbol),
SyntaxKind.ElementAccessExpression);
});
}

private static void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol? dictionarySymbol, INamedTypeSymbol? listSymbol)
{
if (context.Node is not ElementAccessExpressionSyntax elementAccess)
{
return;
}

// Get the type of the accessed object
TypeInfo typeInfo = context.SemanticModel.GetTypeInfo(elementAccess.Expression, context.CancellationToken);

if (typeInfo.Type is not INamedTypeSymbol { IsGenericType: true } namedType)
{
return;
}

INamedTypeSymbol baseType = namedType.ConstructedFrom;
if (SymbolEqualityComparer.Default.Equals(baseType, dictionarySymbol))
{
ITypeSymbol keyType = namedType.TypeArguments[0]; // The TKey in Dictionary<TKey, TValue>
if (ReportDiagnosticOnValueType(keyType))
{
return;
}

ITypeSymbol valueType = namedType.TypeArguments[1]; // The TValue in Dictionary<TKey, TValue>
if (ReportDiagnosticOnValueType(valueType))
{
return;
}
}
else if (SymbolEqualityComparer.Default.Equals(baseType, listSymbol))
{
ITypeSymbol elementType = namedType.TypeArguments[0]; // The T in List<T>
if (ReportDiagnosticOnValueType(elementType))
{
return;
}
}
else
{
Debug.Fail($"Unrecognized constructed from named type '{baseType}'.");
}

return;

bool ReportDiagnosticOnValueType(ITypeSymbol? typeSymbol)
{
// Check if the struct is read/write; if so, there can be bad things that happen to warn
if (typeSymbol is not { IsValueType: true, IsReadOnly: false })
{
return false;
}

// Create and report a diagnostic if the element is accessed directly
Diagnostic diagnostic = elementAccess.GetLocation().CreateDiagnostic(Rule, typeSymbol.Name);
context.ReportDiagnostic(diagnostic);

return true;
}
}

private static void AnalyzeOperation(OperationAnalysisContext context)
{
if (context.Operation is IConversionOperation conversionOperation)
{
AnalyzeConversionOperation(conversionOperation, context);
}
else
{
throw new NotSupportedException($"Unsupported operation kind: {context.Operation.Kind}");
}
}

private static void AnalyzeConversionOperation(IConversionOperation conversionOperation, OperationAnalysisContext context)
{
if (conversionOperation.IsBoxingOrUnboxingOperation())
{
Diagnostic diagnostic = conversionOperation.Syntax.GetLocation().CreateDiagnostic(Rule);
context.ReportDiagnostic(diagnostic);
}
}
}
4 changes: 2 additions & 2 deletions src/EffectiveCSharp.Analyzers/SpanAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
messageFormat: "Consider using Span<T> instead of array for better performance",
category: "Performance",
defaultSeverity: DiagnosticSeverity.Info,
helpLinkUri: $"https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/{ThisAssembly.GitCommitId}/docs/{Id}.md",
isEnabledByDefault: true);
isEnabledByDefault: true,
helpLinkUri: $"https://github.com/rjmurillo/EffectiveCSharp.Analyzers/blob/{ThisAssembly.GitCommitId}/docs/{Id}.md");

/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);
Expand Down Expand Up @@ -50,7 +50,7 @@
private static bool IsInsideSpanInitialization(ArrayCreationExpressionSyntax arrayCreation)
{
// Check if the parent is a Span<T> or ReadOnlySpan<T> creation
// example: new Span<int>(new int[10]);

Check warning on line 53 in src/EffectiveCSharp.Analyzers/SpanAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)

Check warning on line 53 in src/EffectiveCSharp.Analyzers/SpanAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)

Check warning on line 53 in src/EffectiveCSharp.Analyzers/SpanAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)

Check warning on line 53 in src/EffectiveCSharp.Analyzers/SpanAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build (windows-2022)

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)

Check warning on line 53 in src/EffectiveCSharp.Analyzers/SpanAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)

Check warning on line 53 in src/EffectiveCSharp.Analyzers/SpanAnalyzer.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04)

Remove this commented out code. (https://rules.sonarsource.com/csharp/RSPEC-125)
if (arrayCreation.Parent?.Parent?.Parent is not ObjectCreationExpressionSyntax objectCreation)
{
return false;
Expand Down
Loading
Loading