File: Microsoft.CodeQuality.Analyzers\ApiDesignGuidelines\UseIntegralOrStringArgumentForIndexers.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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Analyzer.Utilities;
using System.Linq;
using Analyzer.Utilities.Extensions;

namespace Microsoft.CodeQuality.Analyzers.ApiDesignGuidelines
{
    using static MicrosoftCodeQualityAnalyzersResources;

    /// <summary>
    /// CA1043: <inheritdoc cref="UseIntegralOrStringArgumentForIndexersTitle"/>
    /// </summary>
    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    public sealed class UseIntegralOrStringArgumentForIndexersAnalyzer : DiagnosticAnalyzer
    {
        internal const string RuleId = "CA1043";

        internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create(
            RuleId,
            CreateLocalizableResourceString(nameof(UseIntegralOrStringArgumentForIndexersTitle)),
            CreateLocalizableResourceString(nameof(UseIntegralOrStringArgumentForIndexersMessage)),
            DiagnosticCategory.Design,
            RuleLevel.CandidateForRemoval,
            description: CreateLocalizableResourceString(nameof(UseIntegralOrStringArgumentForIndexersDescription)),
            isPortedFxCopRule: true,
            isDataflowRule: false);

        /// <summary>
        /// PERF: ImmutableArray contains performs better than ImmutableHashSet for small arrays of primitive types.
        /// See: https://github.com/dotnet/roslyn-analyzers/pull/3648#discussion_r428714894
        /// </summary>
        private static readonly ImmutableArray<SpecialType> s_allowedSpecialTypes =
            ImmutableArray.Create(
                SpecialType.System_String,
                SpecialType.System_Int16,
                SpecialType.System_Int32,
                SpecialType.System_Int64,
                SpecialType.System_Object,
                SpecialType.System_UInt16,
                SpecialType.System_UInt32,
                SpecialType.System_UInt64
            );

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);

        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

            context.RegisterCompilationStartAction(context =>
            {
                var allowedTypes = ImmutableHashSet.CreateBuilder<INamedTypeSymbol>();

                if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRange, out var rangeType))
                {
                    allowedTypes.Add(rangeType);
                }

                if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemIndex, out var indexType))
                {
                    allowedTypes.Add(indexType);
                }

                context.RegisterSymbolAction(context => AnalyzeSymbol(context, allowedTypes.ToImmutableHashSet()), SymbolKind.Property);
            });
        }

        private static void AnalyzeSymbol(SymbolAnalysisContext context, ImmutableHashSet<INamedTypeSymbol> allowedTypes)
        {
            var symbol = (IPropertySymbol)context.Symbol;
            if (!symbol.IsIndexer || symbol.IsOverride)
            {
                return;
            }

            if (symbol.Parameters.Length != 1)
            {
                return;
            }

            ITypeSymbol paramType = symbol.Parameters[0].Type;

            if (paramType.TypeKind == TypeKind.TypeParameter)
            {
                return;
            }

            if (paramType.TypeKind == TypeKind.Enum)
            {
                paramType = ((INamedTypeSymbol)paramType).EnumUnderlyingType!;
            }

            if (s_allowedSpecialTypes.Contains(paramType.SpecialType) || allowedTypes.Contains(paramType))
            {
                return;
            }

            if (context.Options.MatchesConfiguredVisibility(Rule, symbol, context.Compilation))
            {
                context.ReportDiagnostic(symbol.CreateDiagnostic(Rule));
            }
        }
    }
}