| File: Microsoft.NetCore.Analyzers\Performance\UseSpanClearInsteadOfFill.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; using System.Collections.Immutable; using System.Linq; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.NetCore.Analyzers.Performance { using static MicrosoftNetCoreAnalyzersResources; /// <summary> /// CA1855: <inheritdoc cref="UseSpanClearInsteadOfFillTitle"/> /// </summary> public abstract class UseSpanClearInsteadOfFillAnalyzer : DiagnosticAnalyzer { internal const string DiagnosticId = "CA1855"; internal const string FillMethod = "Fill"; internal const string ClearMethod = "Clear"; internal static readonly DiagnosticDescriptor s_Rule = DiagnosticDescriptorHelper.Create( DiagnosticId, CreateLocalizableResourceString(nameof(UseSpanClearInsteadOfFillTitle)), CreateLocalizableResourceString(nameof(UseSpanClearInsteadOfFillMessage)), DiagnosticCategory.Performance, RuleLevel.IdeSuggestion, description: CreateLocalizableResourceString(nameof(UseSpanClearInsteadOfFillDescription)), isPortedFxCopRule: false, isDataflowRule: false); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(s_Rule); public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(OnCompilationStart); } private void OnCompilationStart(CompilationStartAnalysisContext context) { var compilation = context.Compilation; if (!compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemSpan1, out var spanType)) { return; } var spanFillMethod = spanType.GetMembers(FillMethod) .OfType<IMethodSymbol>() .FirstOrDefault(); if (spanFillMethod?.Parameters.Length != 1) { return; } context.RegisterOperationAction( context => { var invocation = (IInvocationOperation)context.Operation; if (SymbolEqualityComparer.Default.Equals(invocation.TargetMethod.OriginalDefinition, spanFillMethod) && invocation.Arguments.Length == 1 && IsDefaultValue(invocation.Arguments[0])) { context.ReportDiagnostic(invocation.CreateDiagnostic(s_Rule)); } }, OperationKind.Invocation); } private static bool IsDefaultValue(IArgumentOperation argumentOperation) { var value = argumentOperation.Value; var type = argumentOperation.Parameter?.Type; var constantOpt = value.ConstantValue; if (constantOpt.HasValue) { if (constantOpt.Value == null) { // null must be default value for any valid type return true; } if (argumentOperation.Type.IsNullableValueType()) { // 0 isn't default value for T? return false; } // enum/nint are treated as integers // This is missing default value of DateTime literal for VB, // but since VB doesn't properly support Span, just don't consider it switch (constantOpt.Value) { case (byte)0 or (short)0 or 0 or 0L or (sbyte)0 or (ushort)0 or 0U or 0UL or false or '\0': return true; // -0 is not all bits zero. Handle them by bits case float f: // SingleToInt32Bits not available in netstandard2.0 return BitConverter.DoubleToInt64Bits(f) == 0; case double d: return BitConverter.DoubleToInt64Bits(d) == 0; case decimal d: return decimal.GetBits(d).All(b => b == 0); default: return false; } } if (value is IDefaultValueOperation) { return SymbolEqualityComparer.Default.Equals(value.Type, type); } if (value is IConversionOperation { Operand: IDefaultValueOperation defaultValue }) { // handle the conversion of default literal only return SymbolEqualityComparer.Default.Equals(defaultValue.Type, type); } if (value is IObjectCreationOperation objectCreation) { return objectCreation.Type?.IsValueType == true && objectCreation.Constructor?.IsImplicitlyDeclared == true && objectCreation.Arguments.IsEmpty && (objectCreation.Initializer?.Initializers.IsEmpty ?? true) && SymbolEqualityComparer.Default.Equals(value.Type, type); } return false; } } }