From 88ff7cf36cd734fb0ee5c2f1c27746095d84cb41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Wed, 23 Sep 2026 13:06:00 -0400 Subject: [PATCH] Remove MA0212 Remove the rule that suggested MemoryMarshal.GetReference instead of indexing at 0, along with its code fix, tests and documentation. --- README.md | 1 - docs/README.md | 1 - docs/Rules/MA0212.md | 24 -- ...MarshalGetReferenceForEmptyBuffersFixer.cs | 83 ----- .../configuration/all-errors.editorconfig | 3 - .../all-suggestions.editorconfig | 3 - .../configuration/all-warnings.editorconfig | 3 - .../configuration/default.editorconfig | 3 - .../configuration/none.editorconfig | 3 - src/Meziantou.Analyzer/RuleIdentifiers.cs | 1 - ...shalGetReferenceForEmptyBuffersAnalyzer.cs | 140 -------- ...etReferenceForEmptyBuffersAnalyzerTests.cs | 333 ------------------ 12 files changed, 598 deletions(-) delete mode 100644 docs/Rules/MA0212.md delete mode 100644 src/Meziantou.Analyzer.CodeFixers/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersFixer.cs delete mode 100644 src/Meziantou.Analyzer/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzer.cs delete mode 100644 tests/Meziantou.Analyzer.Test/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzerTests.cs diff --git a/README.md b/README.md index f6b44f7a7..555d38e4c 100755 --- a/README.md +++ b/README.md @@ -240,7 +240,6 @@ If you are already using other analyzers, you can check [which rules are duplica |[MA0209](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0209.md)|Performance|Use in keyword for in parameter|ℹ️|❌|✔️|❌| |[MA0210](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0210.md)|Performance|Use in keyword to call the in overload|ℹ️|❌|✔️|✔️| |[MA0211](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0211.md)|Style|Use multi-line syntax for XML summary comments|ℹ️|❌|✔️|❌| -|[MA0212](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0212.md)|Usage|Use MemoryMarshal.GetReference instead of indexing at 0|⚠️|❌|✔️|❌| |[MA0213](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0213.md)|Style|Simplify negated boolean expression|ℹ️|❌|✔️|❌| |[MA0214](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0214.md)|Design|Use 'await' instead of returning the task|ℹ️|❌|✔️|❌| |[MA0215](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0215.md)|Performance|Return the task instead of awaiting it|ℹ️|❌|✔️|❌| diff --git a/docs/README.md b/docs/README.md index 94c28159b..280e4f51e 100755 --- a/docs/README.md +++ b/docs/README.md @@ -210,7 +210,6 @@ |[MA0209](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0209.md)|Performance|Use in keyword for in parameter|ℹ️|❌|✔️|❌| |[MA0210](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0210.md)|Performance|Use in keyword to call the in overload|ℹ️|❌|✔️|✔️| |[MA0211](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0211.md)|Style|Use multi-line syntax for XML summary comments|ℹ️|❌|✔️|❌| -|[MA0212](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0212.md)|Usage|Use MemoryMarshal.GetReference instead of indexing at 0|⚠️|❌|✔️|❌| |[MA0213](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0213.md)|Style|Simplify negated boolean expression|ℹ️|❌|✔️|❌| |[MA0214](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0214.md)|Design|Use 'await' instead of returning the task|ℹ️|❌|✔️|❌| |[MA0215](https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0215.md)|Performance|Return the task instead of awaiting it|ℹ️|❌|✔️|❌| diff --git a/docs/Rules/MA0212.md b/docs/Rules/MA0212.md deleted file mode 100644 index 19a05c4c2..000000000 --- a/docs/Rules/MA0212.md +++ /dev/null @@ -1,24 +0,0 @@ -# MA0212 - Use MemoryMarshal.GetReference instead of indexing at 0 - -Sources: [UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzer.cs), [UseMemoryMarshalGetReferenceForEmptyBuffersFixer.cs](https://github.com/meziantou/Meziantou.Analyzer/blob/main/src/Meziantou.Analyzer.CodeFixers/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersFixer.cs) - - -This rule reports by-reference access to the first element (`[0]`) of a `Span`, `ReadOnlySpan`, or array. Indexing into `[0]` throws when the buffer is empty; using `MemoryMarshal.GetReference` or `MemoryMarshal.GetArrayDataReference` returns a reference without bounds-checking, which is the correct pattern when working with potentially-empty buffers. - -````csharp -void Process(Span span) -{ - ref byte first = ref span[0]; // throws IndexOutOfRangeException when span is empty -} - -// Should be -void Process(Span span) -{ - ref byte first = ref System.Runtime.InteropServices.MemoryMarshal.GetReference(span); -} -```` - -## Additional resources - -- [MemoryMarshal.GetReference](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.memorymarshal.getreference) -- [MemoryMarshal.GetArrayDataReference](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.memorymarshal.getarraydatareference) (.NET 6+) diff --git a/src/Meziantou.Analyzer.CodeFixers/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersFixer.cs b/src/Meziantou.Analyzer.CodeFixers/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersFixer.cs deleted file mode 100644 index 856ab3337..000000000 --- a/src/Meziantou.Analyzer.CodeFixers/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersFixer.cs +++ /dev/null @@ -1,83 +0,0 @@ -namespace Meziantou.Analyzer.Rules; - -[ExportCodeFixProvider(LanguageNames.CSharp), Shared] -public sealed class UseMemoryMarshalGetReferenceForEmptyBuffersFixer : CodeFixProvider -{ - public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(RuleIdentifiers.UseMemoryMarshalGetReferenceForEmptyBuffers); - - public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; - - public override async Task RegisterCodeFixesAsync(CodeFixContext context) - { - var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); - var nodeToFix = root?.FindNode(context.Span, getInnermostNodeForTie: true); - if (nodeToFix is not ElementAccessExpressionSyntax elementAccess) - return; - - var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); - if (semanticModel is null) - return; - - var receiverType = semanticModel.GetTypeInfo(elementAccess.Expression, context.CancellationToken).Type; - if (receiverType is null) - return; - - var memoryMarshalType = semanticModel.Compilation.GetTypeByMetadataName("System.Runtime.InteropServices.MemoryMarshal"); - if (memoryMarshalType is null) - return; - - string methodName; - if (receiverType.TypeKind is TypeKind.Array) - { - // GetArrayDataReference is available since .NET 6; verify it exists before offering the fix - if (!HasGetArrayDataReference(memoryMarshalType)) - return; - - methodName = "GetArrayDataReference"; - } - else - { - methodName = "GetReference"; - } - - var title = $"Use MemoryMarshal.{methodName}"; - context.RegisterCodeFix( - CodeAction.Create( - title, - ct => FixAsync(context.Document, elementAccess, memoryMarshalType, methodName, ct), - equivalenceKey: title), - context.Diagnostics); - } - - private static bool HasGetArrayDataReference(INamedTypeSymbol memoryMarshalType) - { - foreach (var member in memoryMarshalType.GetMembers("GetArrayDataReference")) - { - if (member is IMethodSymbol) - return true; - } - - return false; - } - - private static async Task FixAsync( - Document document, - ElementAccessExpressionSyntax elementAccess, - INamedTypeSymbol memoryMarshalType, - string methodName, - CancellationToken cancellationToken) - { - var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); - var generator = editor.Generator; - - var receiverExpression = elementAccess.Expression; - - var replacement = generator.InvocationExpression( - generator.TypeMemberAccessExpression(memoryMarshalType, methodName), - receiverExpression.WithoutTrivia()) - .WithTriviaFrom(elementAccess); - - editor.ReplaceNode(elementAccess, replacement); - return editor.GetChangedDocument(); - } -} diff --git a/src/Meziantou.Analyzer.Pack/configuration/all-errors.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/all-errors.editorconfig index 7b399963b..df9d2b2e9 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/all-errors.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/all-errors.editorconfig @@ -629,9 +629,6 @@ dotnet_diagnostic.MA0210.severity = error # MA0211: Use multi-line syntax for XML summary comments dotnet_diagnostic.MA0211.severity = error -# MA0212: Use MemoryMarshal.GetReference instead of indexing at 0 -dotnet_diagnostic.MA0212.severity = error - # MA0213: Simplify negated boolean expression dotnet_diagnostic.MA0213.severity = error diff --git a/src/Meziantou.Analyzer.Pack/configuration/all-suggestions.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/all-suggestions.editorconfig index a245b27bf..90edef460 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/all-suggestions.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/all-suggestions.editorconfig @@ -629,9 +629,6 @@ dotnet_diagnostic.MA0210.severity = suggestion # MA0211: Use multi-line syntax for XML summary comments dotnet_diagnostic.MA0211.severity = suggestion -# MA0212: Use MemoryMarshal.GetReference instead of indexing at 0 -dotnet_diagnostic.MA0212.severity = suggestion - # MA0213: Simplify negated boolean expression dotnet_diagnostic.MA0213.severity = suggestion diff --git a/src/Meziantou.Analyzer.Pack/configuration/all-warnings.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/all-warnings.editorconfig index 7be703bcb..00b452d30 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/all-warnings.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/all-warnings.editorconfig @@ -629,9 +629,6 @@ dotnet_diagnostic.MA0210.severity = warning # MA0211: Use multi-line syntax for XML summary comments dotnet_diagnostic.MA0211.severity = warning -# MA0212: Use MemoryMarshal.GetReference instead of indexing at 0 -dotnet_diagnostic.MA0212.severity = warning - # MA0213: Simplify negated boolean expression dotnet_diagnostic.MA0213.severity = warning diff --git a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig index 9a2e2a49c..f3b2c87be 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/default.editorconfig @@ -629,9 +629,6 @@ dotnet_diagnostic.MA0210.severity = none # MA0211: Use multi-line syntax for XML summary comments dotnet_diagnostic.MA0211.severity = none -# MA0212: Use MemoryMarshal.GetReference instead of indexing at 0 -dotnet_diagnostic.MA0212.severity = none - # MA0213: Simplify negated boolean expression dotnet_diagnostic.MA0213.severity = none diff --git a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig index 1d575d10d..9e52be04a 100644 --- a/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig +++ b/src/Meziantou.Analyzer.Pack/configuration/none.editorconfig @@ -629,9 +629,6 @@ dotnet_diagnostic.MA0210.severity = none # MA0211: Use multi-line syntax for XML summary comments dotnet_diagnostic.MA0211.severity = none -# MA0212: Use MemoryMarshal.GetReference instead of indexing at 0 -dotnet_diagnostic.MA0212.severity = none - # MA0213: Simplify negated boolean expression dotnet_diagnostic.MA0213.severity = none diff --git a/src/Meziantou.Analyzer/RuleIdentifiers.cs b/src/Meziantou.Analyzer/RuleIdentifiers.cs index 059bd27f9..488865694 100755 --- a/src/Meziantou.Analyzer/RuleIdentifiers.cs +++ b/src/Meziantou.Analyzer/RuleIdentifiers.cs @@ -211,7 +211,6 @@ internal static class RuleIdentifiers public const string UseInKeywordForInParameter = "MA0209"; public const string UseInKeywordToSelectInOverload = "MA0210"; public const string UseMultiLineXmlCommentSyntax = "MA0211"; - public const string UseMemoryMarshalGetReferenceForEmptyBuffers = "MA0212"; public const string SimplifyNegatedBooleanExpression = "MA0213"; public const string UseAwaitInsteadOfReturningTask = "MA0214"; public const string ReturnTaskInsteadOfAwaitingIt = "MA0215"; diff --git a/src/Meziantou.Analyzer/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzer.cs b/src/Meziantou.Analyzer/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzer.cs deleted file mode 100644 index 39e477d14..000000000 --- a/src/Meziantou.Analyzer/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzer.cs +++ /dev/null @@ -1,140 +0,0 @@ -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Meziantou.Analyzer.Rules; - -[DiagnosticAnalyzer(LanguageNames.CSharp)] -public sealed class UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzer : DiagnosticAnalyzer -{ - private static readonly DiagnosticDescriptor Rule = new( - RuleIdentifiers.UseMemoryMarshalGetReferenceForEmptyBuffers, - title: "Use MemoryMarshal.GetReference instead of indexing at 0", - messageFormat: "Use MemoryMarshal.GetReference instead of indexing at 0, which throws on empty buffers", - RuleCategories.Usage, - DiagnosticSeverity.Warning, - isEnabledByDefault: false, - description: "Indexing a Span, ReadOnlySpan, or array at index 0 to obtain a by-reference value throws IndexOutOfRangeException on empty buffers. Use MemoryMarshal.GetReference (for spans) or MemoryMarshal.GetArrayDataReference (for arrays) instead, which safely returns a reference to the start even for empty collections.", - helpLinkUri: RuleIdentifiers.GetHelpUri(RuleIdentifiers.UseMemoryMarshalGetReferenceForEmptyBuffers)); - - public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); - - public override void Initialize(AnalysisContext context) - { - context.EnableConcurrentExecution(); - context.ConfigureAnalysisOfGeneratedCode(GeneratedCodeAnalysisFlags.None); - - context.RegisterCompilationStartAction(compilationContext => - { - var analyzerContext = new AnalyzerContext(compilationContext.Compilation); - if (!analyzerContext.IsValid) - return; - - // Handles: ref T local = ref span[0] - compilationContext.RegisterOperationAction(analyzerContext.AnalyzeVariableDeclarator, OperationKind.VariableDeclarator); - - // Handles: return ref span[0] - compilationContext.RegisterOperationAction(analyzerContext.AnalyzeReturn, OperationKind.Return); - - // Handles: Method(ref span[0]), Method(in span[0]) - compilationContext.RegisterOperationAction(analyzerContext.AnalyzeArgument, OperationKind.Argument); - }); - } - - private sealed class AnalyzerContext - { - private readonly INamedTypeSymbol? _spanType; - private readonly INamedTypeSymbol? _readOnlySpanType; - - public AnalyzerContext(Compilation compilation) - { - _spanType = compilation.GetTypeByMetadataName("System.Span`1"); - _readOnlySpanType = compilation.GetTypeByMetadataName("System.ReadOnlySpan`1"); - } - - public bool IsValid => _spanType is not null || _readOnlySpanType is not null; - - public void AnalyzeVariableDeclarator(OperationAnalysisContext context) - { - var declarator = (IVariableDeclaratorOperation)context.Operation; - if (!declarator.Symbol.IsRef) - return; - - var initValue = declarator.Initializer?.Value.UnwrapConversions(); - if (initValue is null) - return; - - ReportIfMatch(context, initValue); - } - - public void AnalyzeReturn(OperationAnalysisContext context) - { - var returnsByRef = context.ContainingSymbol switch - { - IMethodSymbol method => method.ReturnsByRef || method.ReturnsByRefReadonly, - IPropertySymbol property => property.ReturnsByRef || property.ReturnsByRefReadonly, - _ => false, - }; - if (!returnsByRef) - return; - - var returnedValue = ((IReturnOperation)context.Operation).ReturnedValue?.UnwrapConversions(); - if (returnedValue is null) - return; - - ReportIfMatch(context, returnedValue); - } - - public void AnalyzeArgument(OperationAnalysisContext context) - { - var argument = (IArgumentOperation)context.Operation; - - var refKind = argument.Parameter?.RefKind ?? RefKind.None; - if (refKind is RefKind.None or RefKind.Out) - return; - - ReportIfMatch(context, argument.Value.UnwrapConversions()); - } - - private void ReportIfMatch(OperationAnalysisContext context, IOperation value) - { - IOperation? receiverOp; - IOperation? indexOp; - - if (value is IArrayElementReferenceOperation { Indices: [var arrayIdx], ArrayReference: var arrayRef }) - { - receiverOp = arrayRef; - indexOp = arrayIdx; - } - else if (value is IPropertyReferenceOperation { Property.IsIndexer: true, Arguments: [{ Value: var propIdx }], Instance: var instance }) - { - receiverOp = instance; - indexOp = propIdx; - } - else - { - return; - } - - if (!indexOp.IsConstantZero()) - return; - - if (!IsSpanOrArray(receiverOp?.Type)) - return; - - if (value.Syntax is not ElementAccessExpressionSyntax elementAccessSyntax) - return; - - context.ReportDiagnostic(Rule, elementAccessSyntax); - } - - private bool IsSpanOrArray(ITypeSymbol? type) - { - if (type is null) - return false; - - if (type.TypeKind is TypeKind.Array) - return true; - - return type.OriginalDefinition.IsEqualToAny(_spanType, _readOnlySpanType); - } - } -} diff --git a/tests/Meziantou.Analyzer.Test/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzerTests.cs b/tests/Meziantou.Analyzer.Test/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzerTests.cs deleted file mode 100644 index 06eb13080..000000000 --- a/tests/Meziantou.Analyzer.Test/Rules/UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzerTests.cs +++ /dev/null @@ -1,333 +0,0 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using CodeFixTest = Meziantou.Analyzer.Test.Harness.CSharpCodeFixTest< - Meziantou.Analyzer.Rules.UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzer, - Meziantou.Analyzer.Rules.UseMemoryMarshalGetReferenceForEmptyBuffersFixer>; - -namespace Meziantou.Analyzer.Test.Rules; - -public sealed class UseMemoryMarshalGetReferenceForEmptyBuffersAnalyzerTests -{ - private static CodeFixTest CreateTest() => new(); - - [Fact] - public Task RefSpanArgument_ReportsDiagnostic() - { - var test = CreateTest(); - test.TestCode = """ - using System; - class C - { - static void M(ref byte b) { } - void Test(Span span) - { - M(ref {|MA0212:span[0]|}); - } - } - """; - test.FixedCode = """ - using System; - class C - { - static void M(ref byte b) { } - void Test(Span span) - { - M(ref System.Runtime.InteropServices.MemoryMarshal.GetReference(span)); - } - } - """; - - return test.RunAsync(); - } - - [Fact] - public Task InSpanArgument_ReportsDiagnostic() - { - var test = CreateTest(); - test.TestCode = """ - using System; - class C - { - static void M(in byte b) { } - void Test(Span span) - { - M(in {|MA0212:span[0]|}); - } - } - """; - test.FixedCode = """ - using System; - class C - { - static void M(in byte b) { } - void Test(Span span) - { - M(in System.Runtime.InteropServices.MemoryMarshal.GetReference(span)); - } - } - """; - - return test.RunAsync(); - } - - [Fact] - public Task InReadOnlySpanArgument_ReportsDiagnostic() - { - var test = CreateTest(); - test.TestCode = """ - using System; - class C - { - static void M(in byte b) { } - void Test(ReadOnlySpan span) - { - M(in {|MA0212:span[0]|}); - } - } - """; - test.FixedCode = """ - using System; - class C - { - static void M(in byte b) { } - void Test(ReadOnlySpan span) - { - M(in System.Runtime.InteropServices.MemoryMarshal.GetReference(span)); - } - } - """; - - return test.RunAsync(); - } - - [Fact] - public Task RefArrayArgument_ReportsDiagnostic() - { - var test = CreateTest(); - test.TestCode = """ - class C - { - static void M(ref byte b) { } - void Test(byte[] array) - { - M(ref {|MA0212:array[0]|}); - } - } - """; - test.FixedCode = """ - class C - { - static void M(ref byte b) { } - void Test(byte[] array) - { - M(ref System.Runtime.InteropServices.MemoryMarshal.GetArrayDataReference(array)); - } - } - """; - - return test.RunAsync(); - } - - [Fact] - public Task RefSpanReturn_ReportsDiagnostic() - { - var test = CreateTest(); - test.TestCode = """ - using System; - class C - { - ref byte Test(Span span) - { - return ref {|MA0212:span[0]|}; - } - } - """; - test.FixedCode = """ - using System; - class C - { - ref byte Test(Span span) - { - return ref System.Runtime.InteropServices.MemoryMarshal.GetReference(span); - } - } - """; - - return test.RunAsync(); - } - - [Fact] - public Task RefLocalAssignment_ReportsDiagnostic() - { - var test = CreateTest(); - test.TestCode = """ - using System; - class C - { - void Test(Span span) - { - ref byte r = ref {|MA0212:span[0]|}; - } - } - """; - test.FixedCode = """ - using System; - class C - { - void Test(Span span) - { - ref byte r = ref System.Runtime.InteropServices.MemoryMarshal.GetReference(span); - } - } - """; - - return test.RunAsync(); - } - - [Fact] - public Task RefSpanConstantZero_ReportsDiagnostic() - { - var test = CreateTest(); - test.TestCode = """ - using System; - class C - { - static void M(ref byte b) { } - void Test(Span span) - { - const int zero = 0; - M(ref {|MA0212:span[zero]|}); - } - } - """; - test.FixedCode = """ - using System; - class C - { - static void M(ref byte b) { } - void Test(Span span) - { - const int zero = 0; - M(ref System.Runtime.InteropServices.MemoryMarshal.GetReference(span)); - } - } - """; - - return test.RunAsync(); - } - - [Fact] - public Task ValueAccessSpanNotByRef_NoDiagnostic() - { - var test = CreateTest(); - test.TestCode = """ - using System; - class C - { - void Test(Span span) - { - _ = span[0]; - } - } - """; - - return test.RunAsync(); - } - - [Fact] - public Task RefSpanNonZeroIndex_NoDiagnostic() - { - var test = CreateTest(); - test.TestCode = """ - using System; - class C - { - static void M(ref byte b) { } - void Test(Span span) - { - M(ref span[1]); - } - } - """; - - return test.RunAsync(); - } - - [Fact] - public Task RefArrayNonZeroIndex_NoDiagnostic() - { - var test = CreateTest(); - test.TestCode = """ - class C - { - static void M(ref byte b) { } - void Test(byte[] array) - { - M(ref array[1]); - } - } - """; - - return test.RunAsync(); - } - - [Fact] - public Task RefNonConstantIndex_NoDiagnostic() - { - var test = CreateTest(); - test.TestCode = """ - using System; - class C - { - static void M(ref byte b) { } - void Test(Span span, int index) - { - M(ref span[index]); - } - } - """; - - return test.RunAsync(); - } - - [Fact] - public Task RefCustomRefReturningIndexer_NoDiagnostic() - { - var test = CreateTest(); - test.TestCode = """ - class C - { - static void M(ref int v) { } - void Test(MyCollection col) - { - M(ref col[0]); - } - } - struct MyCollection - { - private int[] _items; - public ref int this[int index] => ref _items[index]; - } - """; - - return test.RunAsync(); - } - - [Fact] - public Task ArrayOnNet5_DiagnosticFiresButNoCodeFix() - { - var test = CreateTest(); - test.TestCode = """ - class C - { - static void M(ref byte b) { } - void Test(byte[] array) - { - M(ref {|MA0212:array[0]|}); - } - } - """; - - return test.RunAsync(); - } -}