File: Diagnostics\EmptyArrayAnalyzer.cs
Web Access
Project: src\src\Compilers\Test\Core\Microsoft.CodeAnalysis.Test.Utilities.csproj (Microsoft.CodeAnalysis.Test.Utilities)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
 
namespace Microsoft.CodeAnalysis.Test.Utilities
{
    /// <summary>Analyzer that looks for empty array allocations and recommends their replacement.</summary>
    public class EmptyArrayAnalyzer : DiagnosticAnalyzer
    {
        private const string SystemCategory = "System";
 
        /// <summary>The name of the array type.</summary>
        internal const string ArrayTypeName = "System.Array"; // using instead of GetSpecialType to make more testable
 
        /// <summary>The name of the Empty method on System.Array.</summary>
        internal const string ArrayEmptyMethodName = "Empty";
 
        private static readonly LocalizableString s_localizableTitle = "Empty Array";
        private static readonly LocalizableString s_localizableMessage = "Empty array creation can be replaced with Array.Empty";
 
        /// <summary>The diagnostic descriptor used when Array.Empty should be used instead of a new array allocation.</summary>
        public static readonly DiagnosticDescriptor UseArrayEmptyDescriptor = new DiagnosticDescriptor(
            "EmptyArrayRule",
            s_localizableTitle,
            s_localizableMessage,
            SystemCategory,
            DiagnosticSeverity.Warning,
            isEnabledByDefault: true);
 
        /// <summary>Gets the set of supported diagnostic descriptors from this analyzer.</summary>
        public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
        {
            get { return ImmutableArray.Create(UseArrayEmptyDescriptor); }
        }
 
        public sealed override void Initialize(AnalysisContext context)
        {
            context.RegisterCompilationStartAction(ctx =>
            {
                RegisterOperationAction(ctx);
            });
        }
 
        /// <summary>Reports a diagnostic warning for an array creation that should be replaced.</summary>
        /// <param name="context">The context.</param>
        /// <param name="arrayCreationExpression">The array creation expression to be replaced.</param>
        internal void Report(OperationAnalysisContext context, SyntaxNode arrayCreationExpression)
        {
            context.ReportDiagnostic(Diagnostic.Create(UseArrayEmptyDescriptor, arrayCreationExpression.GetLocation()));
        }
 
        /// <summary>Called once at compilation start to register actions in the compilation context.</summary>
        /// <param name="context">The analysis context.</param>
        internal void RegisterOperationAction(CompilationStartAnalysisContext context)
        {
            context.RegisterOperationAction(
                (operationContext) =>
                    {
                        IArrayCreationOperation arrayCreation = (IArrayCreationOperation)operationContext.Operation;
 
                        // ToDo: Need to suppress analysis of array creation expressions within attribute applications.
 
                        // Detect array creation expression that have rank 1 and size 0. Such expressions
                        // can be replaced with Array.Empty<T>(), provided that the element type can be a generic type argument.
 
                        var elementType = (arrayCreation as IArrayTypeSymbol)?.ElementType;
                        if (arrayCreation.DimensionSizes.Length == 1
                            //// Pointer types can't be generic type arguments.
                            && elementType?.TypeKind != TypeKind.Pointer)
                        {
                            Optional<object> arrayLength = arrayCreation.DimensionSizes[0].ConstantValue;
                            if (arrayLength.HasValue &&
                                arrayLength.Value is int &&
                                (int)arrayLength.Value == 0)
                            {
                                Report(operationContext, arrayCreation.Syntax);
                            }
                        }
                    },
                OperationKind.ArrayCreation);
        }
    }
}