| File: Microsoft.NetCore.Analyzers\Runtime\BufferBlockCopyLengthAnalyzer.cs | Web Access |
| Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.csproj (Microsoft.CodeAnalysis.NetAnalyzers) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Immutable; using System.Linq; using System.Threading; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.NetCore.Analyzers.Runtime { using static MicrosoftNetCoreAnalyzersResources; /// <summary> /// CA2018: <inheritdoc cref="BufferBlockCopyLengthTitle"/> /// Check for the intended use of .Length on arrays passed into Buffer.BlockCopy /// </summary> [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class BufferBlockCopyLengthAnalyzer : DiagnosticAnalyzer { internal const string RuleId = "CA2018"; internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(RuleId, CreateLocalizableResourceString(nameof(BufferBlockCopyLengthTitle)), CreateLocalizableResourceString(nameof(BufferBlockCopyLengthMessage)), DiagnosticCategory.Reliability, RuleLevel.BuildWarning, CreateLocalizableResourceString(nameof(BufferBlockCopyDescription)), isPortedFxCopRule: false, isDataflowRule: false); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(context => { if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemBuffer, out INamedTypeSymbol? bufferType)) { return; } INamedTypeSymbol arrayType = context.Compilation.GetSpecialType(SpecialType.System_Array); INamedTypeSymbol int32Type = context.Compilation.GetSpecialType(SpecialType.System_Int32); // Buffer.BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count) ParameterInfo[] blockCopyParameterInfo = { ParameterInfo.GetParameterInfo(arrayType, false, 1, false), ParameterInfo.GetParameterInfo(int32Type, false, 1, false), ParameterInfo.GetParameterInfo(arrayType, false, 1, false), ParameterInfo.GetParameterInfo(int32Type, false, 1, false), ParameterInfo.GetParameterInfo(int32Type, false, 1, false)}; IMethodSymbol? blockCopyMethod = bufferType .GetMembers("BlockCopy") .OfType<IMethodSymbol>() .GetFirstOrDefaultMemberWithParameterInfos(blockCopyParameterInfo); if (blockCopyMethod is null) { return; } ISymbol? arrayLengthProperty = arrayType .GetMembers("Length") .FirstOrDefault(p => p is IPropertySymbol); context.RegisterOperationAction(context => { var invocationOperation = (IInvocationOperation)context.Operation; if (!invocationOperation.TargetMethod.Equals(blockCopyMethod, SymbolEqualityComparer.Default)) { return; } ImmutableArray<IArgumentOperation> arguments = IOperationExtensions.GetArgumentsInParameterOrder(invocationOperation.Arguments); IArgumentOperation sourceArgument = arguments[0]; IArgumentOperation destinationArgument = arguments[2]; IArgumentOperation countArgument = arguments[4]; bool CheckArgumentArrayType(IArgumentOperation targetArgument, IPropertyReferenceOperation lengthPropertyArgument) { if (targetArgument.Value is IConversionOperation targetArgumentValue) { if (lengthPropertyArgument.Instance.GetReferencedMemberOrLocalOrParameter() == targetArgumentValue.Operand.GetReferencedMemberOrLocalOrParameter()) { IArrayTypeSymbol countArgumentArrayTypeSymbol = (IArrayTypeSymbol)lengthPropertyArgument.Instance!.Type!; if (countArgumentArrayTypeSymbol.ElementType.SpecialType is not SpecialType.System_Byte and not SpecialType.System_SByte and not SpecialType.System_Boolean) { return true; } } } return false; } bool CheckLengthProperty(IPropertyReferenceOperation countArgument) { if (countArgument.Property.Equals(arrayLengthProperty)) { return CheckArgumentArrayType(sourceArgument, countArgument) || CheckArgumentArrayType(destinationArgument, countArgument); } return false; } if (countArgument.Value is IPropertyReferenceOperation countArgumentValue && CheckLengthProperty(countArgumentValue)) { context.ReportDiagnostic(countArgument.Value.CreateDiagnostic(Rule)); } else { if (countArgument.Value is not ILocalReferenceOperation localReferenceOperation) { return; } SemanticModel semanticModel = countArgument.SemanticModel!; CancellationToken cancellationToken = context.CancellationToken; ILocalSymbol localArgumentDeclaration = localReferenceOperation.Local; SyntaxReference? declaringSyntaxReference = localArgumentDeclaration.DeclaringSyntaxReferences.FirstOrDefault(); if (declaringSyntaxReference is null) { return; } if (semanticModel.GetOperationWalkingUpParentChain(declaringSyntaxReference.GetSyntax(cancellationToken), cancellationToken) is not IVariableDeclaratorOperation variableDeclaratorOperation) { return; } IVariableInitializerOperation? variableInitializer = variableDeclaratorOperation.Initializer; if (variableInitializer is not null && variableInitializer.Value is IPropertyReferenceOperation variableInitializerPropertyReference && CheckLengthProperty(variableInitializerPropertyReference)) { context.ReportDiagnostic(countArgument.Value.CreateDiagnostic(Rule)); } else if (variableDeclaratorOperation.Parent is IVariableDeclarationOperation variableDeclarationOperation && variableDeclarationOperation.Initializer is not null && variableDeclarationOperation.Initializer.Value is IPropertyReferenceOperation variableInitializerPropertyReferenceVB && CheckLengthProperty(variableInitializerPropertyReferenceVB)) { context.ReportDiagnostic(countArgument.Value.CreateDiagnostic(Rule)); } } }, OperationKind.Invocation); }); } } }