File: MetaAnalyzers\DiagnosticDescriptorCreationAnalyzer.cs
Web Access
Project: src\src\RoslynAnalyzers\Microsoft.CodeAnalysis.Analyzers\Core\Microsoft.CodeAnalysis.Analyzers.csproj (Microsoft.CodeAnalysis.Analyzers)
// 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.
 
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Analyzer.Utilities.PooledObjects;
using Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers.Helpers;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Microsoft.CodeAnalysis.ReleaseTracking;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers
{
    using static CodeAnalysisDiagnosticsResources;
    using PooledLocalizabeStringsConcurrentDictionary = PooledConcurrentDictionary<INamedTypeSymbol, PooledConcurrentSet<(IFieldSymbol field, IArgumentOperation argument)>>;
    using PooledResourcesDataValueConcurrentDictionary = PooledConcurrentDictionary<string, ImmutableDictionary<string, (string value, Location location)>>;
    using PooledFieldToResourceNameAndFileNameConcurrentDictionary = PooledConcurrentDictionary<IFieldSymbol, (string nameOfResource, string resourceFileName)>;
    using PooledFieldToCustomTagsConcurrentDictionary = PooledConcurrentDictionary<IFieldSymbol, ImmutableArray<string>>;
 
    /// <summary>
    /// RS1007 <inheritdoc cref="UseLocalizableStringsInDescriptorTitle"/>
    /// RS1015 <inheritdoc cref="ProvideHelpUriInDescriptorTitle"/>
    /// RS1017 <inheritdoc cref="DiagnosticIdMustBeAConstantTitle"/>
    /// RS1019 <inheritdoc cref="UseUniqueDiagnosticIdTitle"/>
    /// RS1028 <inheritdoc cref="ProvideCustomTagsInDescriptorTitle"/>
    /// RS1029 <inheritdoc cref="DoNotUseReservedDiagnosticIdTitle"/>
    /// RS1031 <inheritdoc cref="DefineDiagnosticTitleCorrectlyTitle"/>
    /// RS1032 <inheritdoc cref="DefineDiagnosticMessageCorrectlyTitle"/>
    /// RS1033 <inheritdoc cref="DefineDiagnosticDescriptionCorrectlyTitle"/>
    /// </summary>
    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    public sealed partial class DiagnosticDescriptorCreationAnalyzer : DiagnosticAnalyzer
    {
        private const string HelpLinkUriParameterName = "helpLinkUri";
        private const string CategoryParameterName = "category";
        private const string DiagnosticIdParameterName = "id";
        private const string CustomTagsParameterName = "customTags";
        private const string IsEnabledByDefaultParameterName = "isEnabledByDefault";
        private const string DefaultSeverityParameterName = "defaultSeverity";
        private const string RuleLevelParameterName = "ruleLevel";
        private const string CompilationEndWellKnownDiagnosticTag = "CompilationEnd" /*WellKnownDiagnosticTags.CompilationEnd*/;
 
        internal const string DefineDescriptorArgumentCorrectlyFixValue = nameof(DefineDescriptorArgumentCorrectlyFixValue);
        private const string DefineDescriptorArgumentCorrectlyFixAdditionalDocumentLocationInfo = nameof(DefineDescriptorArgumentCorrectlyFixAdditionalDocumentLocationInfo);
        private const string AdditionalDocumentLocationInfoSeparator = ";;";
 
        private static readonly ImmutableHashSet<string> CADiagnosticIdAllowedAssemblies = ImmutableHashSet.Create(
            StringComparer.Ordinal,
            "Microsoft.CodeAnalysis.VersionCheckAnalyzer",
            "Microsoft.CodeAnalysis.NetAnalyzers",
            "Microsoft.CodeAnalysis.CSharp.NetAnalyzers",
            "Microsoft.CodeAnalysis.VisualBasic.NetAnalyzers",
            "Microsoft.CodeQuality.Analyzers",
            "Microsoft.CodeQuality.CSharp.Analyzers",
            "Microsoft.CodeQuality.VisualBasic.Analyzers",
            "Microsoft.NetCore.Analyzers",
            "Microsoft.NetCore.CSharp.Analyzers",
            "Microsoft.NetCore.VisualBasic.Analyzers",
            "Microsoft.NetFramework.Analyzers",
            "Microsoft.NetFramework.CSharp.Analyzers",
            "Microsoft.NetFramework.VisualBasic.Analyzers",
            "Text.Analyzers",
            "Text.CSharp.Analyzers",
            "Text.VisualBasic.Analyzers");
 
        public static readonly DiagnosticDescriptor UseLocalizableStringsInDescriptorRule = new(
            DiagnosticIds.UseLocalizableStringsInDescriptorRuleId,
            CreateLocalizableResourceString(nameof(UseLocalizableStringsInDescriptorTitle)),
            CreateLocalizableResourceString(nameof(UseLocalizableStringsInDescriptorMessage)),
            DiagnosticCategory.MicrosoftCodeAnalysisLocalization,
            DiagnosticSeverity.Warning,
            isEnabledByDefault: false,
            description: CreateLocalizableResourceString(nameof(UseLocalizableStringsInDescriptorDescription)),
            customTags: WellKnownDiagnosticTagsExtensions.Telemetry);
 
        public static readonly DiagnosticDescriptor ProvideHelpUriInDescriptorRule = new(
            DiagnosticIds.ProvideHelpUriInDescriptorRuleId,
            CreateLocalizableResourceString(nameof(ProvideHelpUriInDescriptorTitle)),
            CreateLocalizableResourceString(nameof(ProvideHelpUriInDescriptorMessage)),
            DiagnosticCategory.MicrosoftCodeAnalysisDocumentation,
            DiagnosticSeverity.Warning,
            isEnabledByDefault: false,
            description: CreateLocalizableResourceString(nameof(ProvideHelpUriInDescriptorDescription)),
            customTags: WellKnownDiagnosticTagsExtensions.Telemetry);
 
        public static readonly DiagnosticDescriptor DiagnosticIdMustBeAConstantRule = new(
            DiagnosticIds.DiagnosticIdMustBeAConstantRuleId,
            CreateLocalizableResourceString(nameof(DiagnosticIdMustBeAConstantTitle)),
            CreateLocalizableResourceString(nameof(DiagnosticIdMustBeAConstantMessage)),
            DiagnosticCategory.MicrosoftCodeAnalysisDesign,
            DiagnosticSeverity.Warning,
            isEnabledByDefault: true,
            description: CreateLocalizableResourceString(nameof(DiagnosticIdMustBeAConstantDescription)),
            customTags: WellKnownDiagnosticTagsExtensions.Telemetry);
 
        public static readonly DiagnosticDescriptor UseUniqueDiagnosticIdRule = new(
            DiagnosticIds.UseUniqueDiagnosticIdRuleId,
            CreateLocalizableResourceString(nameof(UseUniqueDiagnosticIdTitle)),
            CreateLocalizableResourceString(nameof(UseUniqueDiagnosticIdMessage)),
            DiagnosticCategory.MicrosoftCodeAnalysisDesign,
            DiagnosticSeverity.Warning,
            isEnabledByDefault: true,
            description: CreateLocalizableResourceString(nameof(UseUniqueDiagnosticIdDescription)),
            customTags: WellKnownDiagnosticTagsExtensions.CompilationEndAndTelemetry);
 
        public static readonly DiagnosticDescriptor ProvideCustomTagsInDescriptorRule = new(
            DiagnosticIds.ProvideCustomTagsInDescriptorRuleId,
            CreateLocalizableResourceString(nameof(ProvideCustomTagsInDescriptorTitle)),
            CreateLocalizableResourceString(nameof(ProvideCustomTagsInDescriptorMessage)),
            DiagnosticCategory.MicrosoftCodeAnalysisDocumentation,
            DiagnosticSeverity.Warning,
            isEnabledByDefault: false,
            description: CreateLocalizableResourceString(nameof(ProvideCustomTagsInDescriptorDescription)),
            customTags: WellKnownDiagnosticTagsExtensions.Telemetry);
 
        public static readonly DiagnosticDescriptor DoNotUseReservedDiagnosticIdRule = new(
            DiagnosticIds.DoNotUseReservedDiagnosticIdRuleId,
            CreateLocalizableResourceString(nameof(DoNotUseReservedDiagnosticIdTitle)),
            CreateLocalizableResourceString(nameof(DoNotUseReservedDiagnosticIdMessage)),
            DiagnosticCategory.MicrosoftCodeAnalysisDesign,
            DiagnosticSeverity.Warning,
            isEnabledByDefault: true,
            description: CreateLocalizableResourceString(nameof(DoNotUseReservedDiagnosticIdDescription)),
            customTags: WellKnownDiagnosticTagsExtensions.Telemetry);
 
        public static readonly DiagnosticDescriptor DefineDiagnosticTitleCorrectlyRule = new(
            DiagnosticIds.DefineDiagnosticTitleCorrectlyRuleId,
            CreateLocalizableResourceString(nameof(DefineDiagnosticTitleCorrectlyTitle)),
            CreateLocalizableResourceString(nameof(DefineDiagnosticTitleCorrectlyMessage)),
            DiagnosticCategory.MicrosoftCodeAnalysisDesign,
            DiagnosticSeverity.Warning,
            isEnabledByDefault: true,
            customTags: WellKnownDiagnosticTagsExtensions.Telemetry);
 
        public static readonly DiagnosticDescriptor DefineDiagnosticMessageCorrectlyRule = new(
            DiagnosticIds.DefineDiagnosticMessageCorrectlyRuleId,
            CreateLocalizableResourceString(nameof(DefineDiagnosticMessageCorrectlyTitle)),
            CreateLocalizableResourceString(nameof(DefineDiagnosticMessageCorrectlyMessage)),
            DiagnosticCategory.MicrosoftCodeAnalysisDesign,
            DiagnosticSeverity.Warning,
            isEnabledByDefault: true,
            customTags: WellKnownDiagnosticTagsExtensions.Telemetry);
 
        public static readonly DiagnosticDescriptor DefineDiagnosticDescriptionCorrectlyRule = new(
            DiagnosticIds.DefineDiagnosticDescriptionCorrectlyRuleId,
            CreateLocalizableResourceString(nameof(DefineDiagnosticDescriptionCorrectlyTitle)),
            CreateLocalizableResourceString(nameof(DefineDiagnosticDescriptionCorrectlyMessage)),
            DiagnosticCategory.MicrosoftCodeAnalysisDesign,
            DiagnosticSeverity.Warning,
            isEnabledByDefault: true,
            customTags: WellKnownDiagnosticTagsExtensions.Telemetry);
 
        public static readonly DiagnosticDescriptor AddCompilationEndCustomTagRule = new(
            DiagnosticIds.AddCompilationEndCustomTagRuleId,
            CreateLocalizableResourceString(nameof(AddCompilationEndCustomTagTitle)),
            CreateLocalizableResourceString(nameof(AddCompilationEndCustomTagMessage)),
            DiagnosticCategory.MicrosoftCodeAnalysisDesign,
            DiagnosticSeverity.Warning,
            isEnabledByDefault: true,
            description: CreateLocalizableResourceString(nameof(AddCompilationEndCustomTagDescription)),
            customTags: WellKnownDiagnosticTagsExtensions.Telemetry);
 
        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(
            UseLocalizableStringsInDescriptorRule,
            ProvideHelpUriInDescriptorRule,
            DiagnosticIdMustBeAConstantRule,
            DiagnosticIdMustBeInSpecifiedFormatRule,
            UseUniqueDiagnosticIdRule,
            UseCategoriesFromSpecifiedRangeRule,
            AnalyzerCategoryAndIdRangeFileInvalidRule,
            ProvideCustomTagsInDescriptorRule,
            DoNotUseReservedDiagnosticIdRule,
            DeclareDiagnosticIdInAnalyzerReleaseRule,
            UpdateDiagnosticIdInAnalyzerReleaseRule,
            RemoveUnshippedDeletedDiagnosticIdRule,
            RemoveShippedDeletedDiagnosticIdRule,
            UnexpectedAnalyzerDiagnosticForRemovedDiagnosticIdRule,
            RemoveDuplicateEntriesForAnalyzerReleaseRule,
            RemoveDuplicateEntriesBetweenAnalyzerReleasesRule,
            InvalidEntryInAnalyzerReleasesFileRule,
            InvalidHeaderInAnalyzerReleasesFileRule,
            InvalidUndetectedEntryInAnalyzerReleasesFileRule,
            InvalidRemovedOrChangedWithoutPriorNewEntryInAnalyzerReleasesFileRule,
            EnableAnalyzerReleaseTrackingRule,
            DefineDiagnosticTitleCorrectlyRule,
            DefineDiagnosticMessageCorrectlyRule,
            DefineDiagnosticDescriptionCorrectlyRule,
            AddCompilationEndCustomTagRule);
 
        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
 
            context.RegisterCompilationStartAction(compilationContext =>
            {
                if (!compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticDescriptor, out var diagnosticDescriptorType) ||
                    !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisLocalizableString, out var localizableResourceType) ||
                    !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisLocalizableResourceString, out var localizableResourceStringType) ||
                    !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsCompilationEndAnalysisContext, out var compilationEndContextType) ||
                    !compilationContext.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnostic, out var diagnosticType))
                {
                    return;
                }
 
                // Try read the additional file containing the allowed categories, and corresponding ID ranges.
                var checkCategoryAndAllowedIds = TryGetCategoryAndAllowedIdsMap(
                    compilationContext.Options.AdditionalFiles,
                    compilationContext.CancellationToken,
                    out AdditionalText? diagnosticCategoryAndIdRangeText,
                    out ImmutableDictionary<string, ImmutableArray<(string? prefix, int start, int end)>>? categoryAndAllowedIdsMap,
                    out List<Diagnostic>? invalidFileDiagnostics);
 
                // Try read the additional files containing the shipped and unshipped analyzer releases.
                var isAnalyzerReleaseTracking = TryGetReleaseTrackingData(
                    compilationContext.Options.AdditionalFiles,
                    compilationContext.CancellationToken,
                    out var shippedData,
                    out var unshippedData,
                    out List<Diagnostic>? invalidReleaseFileEntryDiagnostics);
 
                PooledLocalizabeStringsConcurrentDictionary? localizableTitles = null;
                PooledLocalizabeStringsConcurrentDictionary? localizableMessages = null;
                PooledLocalizabeStringsConcurrentDictionary? localizableDescriptions = null;
                PooledResourcesDataValueConcurrentDictionary? resourcesDataValueMap = null;
 
                var analyzeResourceStrings = HasResxAdditionalFiles(compilationContext.Options);
                if (analyzeResourceStrings)
                {
                    localizableTitles = PooledLocalizabeStringsConcurrentDictionary.GetInstance();
                    localizableMessages = PooledLocalizabeStringsConcurrentDictionary.GetInstance();
                    localizableDescriptions = PooledLocalizabeStringsConcurrentDictionary.GetInstance();
                    resourcesDataValueMap = PooledResourcesDataValueConcurrentDictionary.GetInstance();
                }
 
                var idToAnalyzerMap = new ConcurrentDictionary<string, ConcurrentDictionary<string, ConcurrentBag<Location>>>();
                var seenRuleIds = PooledConcurrentSet<string>.GetInstance();
                var customTagsMap = PooledFieldToCustomTagsConcurrentDictionary.GetInstance(SymbolEqualityComparer.Default);
                compilationContext.RegisterOperationAction(operationAnalysisContext =>
                {
                    var fieldInitializer = (IFieldInitializerOperation)operationAnalysisContext.Operation;
                    if (!TryGetDescriptorCreateMethodAndArguments(fieldInitializer, diagnosticDescriptorType, out var creationMethod, out var creationArguments))
                    {
                        return;
                    }
 
                    var containingType = operationAnalysisContext.ContainingSymbol.ContainingType;
                    AnalyzeTitle(operationAnalysisContext, creationArguments, fieldInitializer, containingType,
                        localizableTitles, resourcesDataValueMap, localizableResourceType, localizableResourceStringType);
                    AnalyzeMessage(operationAnalysisContext, creationArguments, containingType,
                        localizableMessages, resourcesDataValueMap, localizableResourceType, localizableResourceStringType);
                    AnalyzeDescription(operationAnalysisContext, creationArguments, containingType,
                        localizableDescriptions, resourcesDataValueMap, localizableResourceType, localizableResourceStringType);
                    AnalyzeHelpLinkUri(operationAnalysisContext, creationArguments, out var helpLink);
                    AnalyzeCustomTags(operationAnalysisContext, creationArguments, fieldInitializer, customTagsMap);
                    var (isEnabledByDefault, defaultSeverity) = GetDefaultSeverityAndEnabledByDefault(operationAnalysisContext.Compilation, creationArguments);
 
                    if (!TryAnalyzeCategory(operationAnalysisContext, creationArguments, checkCategoryAndAllowedIds,
                            diagnosticCategoryAndIdRangeText, categoryAndAllowedIdsMap, out var category, out var allowedIdsInfoList))
                    {
                        allowedIdsInfoList = default;
                    }
 
                    var analyzerName = fieldInitializer.InitializedFields.First().ContainingType.Name;
                    AnalyzeRuleId(operationAnalysisContext, creationArguments,
                        isAnalyzerReleaseTracking, shippedData, unshippedData, seenRuleIds, diagnosticCategoryAndIdRangeText,
                        category, analyzerName, helpLink, isEnabledByDefault, defaultSeverity, allowedIdsInfoList, idToAnalyzerMap);
 
                }, OperationKind.FieldInitializer);
 
                if (analyzeResourceStrings)
                {
                    compilationContext.RegisterSymbolStartAction(context =>
                    {
                        var symbolToResourceMap = PooledFieldToResourceNameAndFileNameConcurrentDictionary.GetInstance(SymbolEqualityComparer.Default);
                        context.RegisterOperationAction(context =>
                        {
                            var fieldInitializer = (IFieldInitializerOperation)context.Operation;
                            if (TryGetLocalizableResourceStringCreation(fieldInitializer.Value, localizableResourceStringType,
                                    out var nameOfLocalizableResource, out var resourceFileName))
                            {
                                foreach (var field in fieldInitializer.InitializedFields)
                                {
                                    symbolToResourceMap.TryAdd(field, (nameOfLocalizableResource, resourceFileName));
                                }
                            }
                        }, OperationKind.FieldInitializer);
 
                        context.RegisterSymbolEndAction(context =>
                        {
                            RoslynDebug.Assert(localizableTitles != null);
                            RoslynDebug.Assert(localizableMessages != null);
                            RoslynDebug.Assert(localizableDescriptions != null);
                            RoslynDebug.Assert(resourcesDataValueMap != null);
 
                            var namedType = (INamedTypeSymbol)context.Symbol;
 
                            AnalyzeLocalizableStrings(localizableTitles, AnalyzeTitleCore, symbolToResourceMap, namedType,
                                resourcesDataValueMap, context.Options, context.ReportDiagnostic, context.CancellationToken);
                            AnalyzeLocalizableStrings(localizableMessages, AnalyzeMessageCore, symbolToResourceMap, namedType,
                                resourcesDataValueMap, context.Options, context.ReportDiagnostic, context.CancellationToken);
                            AnalyzeLocalizableStrings(localizableDescriptions, AnalyzeDescriptionCore, symbolToResourceMap, namedType,
                                resourcesDataValueMap, context.Options, context.ReportDiagnostic, context.CancellationToken);
 
                            symbolToResourceMap.Free(context.CancellationToken);
                        });
                    }, SymbolKind.NamedType);
                }
 
                // Flag descriptor fields that are used to report compilation end diagnostics,
                // but do not have the required 'WellKnownDiagnosticTags.CompilationEnd' custom tag.
                // See https://github.com/dotnet/roslyn-analyzers/issues/6282 for details.
                if (compilationEndContextType.GetMembers(DiagnosticWellKnownNames.ReportDiagnosticName).FirstOrDefault() is IMethodSymbol compilationEndReportDiagnosticMethod)
                {
                    var diagnosticCreateMethods = diagnosticType.GetMembers("Create").OfType<IMethodSymbol>()
                        .Where(m => m.IsPublic() && m.Parameters.Length > 0 && SymbolEqualityComparer.Default.Equals(m.Parameters[0].Type, diagnosticDescriptorType))
                        .ToImmutableHashSet(SymbolEqualityComparer.Default);
                    compilationContext.RegisterSymbolStartAction(context =>
                    {
                        var localsToDescriptorsMap = PooledConcurrentDictionary<ILocalSymbol, PooledConcurrentSet<IFieldSymbol>>.GetInstance(SymbolEqualityComparer.Default);
                        var localsUsedForCompilationEndReportDiagnostic = PooledConcurrentSet<ILocalSymbol>.GetInstance(SymbolEqualityComparer.Default);
                        var fieldsUsedForCompilationEndReportDiagnostic = PooledConcurrentSet<IFieldSymbol>.GetInstance(SymbolEqualityComparer.Default);
 
                        context.RegisterOperationAction(context =>
                        {
                            var invocation = (IInvocationOperation)context.Operation;
                            if (invocation.Arguments.IsEmpty)
                                return;
 
                            if (SymbolEqualityComparer.Default.Equals(invocation.TargetMethod, compilationEndReportDiagnosticMethod) &&
                                    invocation.Arguments[0].Value.WalkDownConversion() is ILocalReferenceOperation localReference)
                            {
                                // Code pattern such as:
                                //      var diagnostic = Diagnostic.Create(field, ...);
                                //      context.ReportDiagnostic(diagnostic);
                                localsUsedForCompilationEndReportDiagnostic.Add(localReference.Local);
                            }
                            else if (diagnosticCreateMethods.Contains(invocation.TargetMethod))
                            {
                                if (invocation.Arguments[0].Value.WalkDownConversion() is IFieldReferenceOperation fieldReference)
                                {
                                    // Code pattern such as:
                                    //      'Diagnostic.Create(field, ...)'
                                    if (invocation.GetAncestor<IInvocationOperation>(OperationKind.Invocation,
                                                inv => SymbolEqualityComparer.Default.Equals(inv.TargetMethod, compilationEndReportDiagnosticMethod)) is not null)
                                    {
                                        // Code pattern such as:
                                        //      'context.ReportDiagnostic(Diagnostic.Create(field, ...));'
                                        fieldsUsedForCompilationEndReportDiagnostic.Add(fieldReference.Field);
                                    }
                                    else
                                    {
                                        switch (invocation.Parent)
                                        {
                                            case IVariableInitializerOperation variableInitializer:
                                                // Code pattern such as:
                                                //      'var diagnostic = Diagnostic.Create(field, ...);'
                                                if (variableInitializer.GetAncestor<IVariableDeclarationOperation>(OperationKind.VariableDeclaration) is { } variableDeclaration)
                                                {
                                                    foreach (var local in variableDeclaration.GetDeclaredVariables())
                                                    {
                                                        AddToLocalsToDescriptorsMap(local, fieldReference.Field, localsToDescriptorsMap);
                                                    }
                                                }
 
                                                break;
 
                                            case ISimpleAssignmentOperation simpleAssignment:
                                                // Code pattern such as:
                                                //      'diagnostic = Diagnostic.Create(field, ...);'
                                                if (simpleAssignment.Target is ILocalReferenceOperation localReferenceTarget)
                                                {
                                                    AddToLocalsToDescriptorsMap(localReferenceTarget.Local, fieldReference.Field, localsToDescriptorsMap);
                                                }
 
                                                break;
                                        }
                                    }
                                }
 
                                static void AddToLocalsToDescriptorsMap(ILocalSymbol local, IFieldSymbol field, PooledConcurrentDictionary<ILocalSymbol, PooledConcurrentSet<IFieldSymbol>> localsToDescriptorsMap)
                                {
                                    localsToDescriptorsMap.AddOrUpdate(local,
                                        addValueFactory: _ =>
                                        {
                                            var set = PooledConcurrentSet<IFieldSymbol>.GetInstance(SymbolEqualityComparer.Default);
                                            set.Add(field);
                                            return set;
                                        },
                                        updateValueFactory: (_, fields) =>
                                        {
                                            fields.Add(field);
                                            return fields;
                                        });
                                }
                            }
                        }, OperationKind.Invocation);
 
                        context.RegisterSymbolEndAction(context =>
                        {
                            foreach (var local in localsUsedForCompilationEndReportDiagnostic)
                            {
                                if (localsToDescriptorsMap.TryGetValue(local, out var fields))
                                {
                                    foreach (var field in fields)
                                        AnalyzeField(field);
                                }
                            }
 
                            foreach (var field in fieldsUsedForCompilationEndReportDiagnostic)
                            {
                                AnalyzeField(field);
                            }
 
                            foreach (var value in localsToDescriptorsMap.Values)
                                value.Free(context.CancellationToken);
                            localsToDescriptorsMap.Free(context.CancellationToken);
                            localsUsedForCompilationEndReportDiagnostic.Free(context.CancellationToken);
                            fieldsUsedForCompilationEndReportDiagnostic.Free(context.CancellationToken);
 
                            void AnalyzeField(IFieldSymbol field)
                            {
                                if (customTagsMap.TryGetValue(field, out var customTags) &&
                                    !customTags.IsDefault &&
                                    !customTags.Contains(CompilationEndWellKnownDiagnosticTag) &&
                                    !field.Locations.IsEmpty &&
                                    field.Locations[0].IsInSource)
                                {
                                    context.ReportDiagnostic(Diagnostic.Create(AddCompilationEndCustomTagRule, field.Locations[0], field.Name));
                                }
                            }
                        });
                    }, SymbolKind.NamedType);
                }
 
                compilationContext.RegisterCompilationEndAction(compilationEndContext =>
                {
                    // Report any invalid additional file diagnostics.
                    if (invalidFileDiagnostics != null)
                    {
                        foreach (var diagnostic in invalidFileDiagnostics)
                        {
                            compilationEndContext.ReportDiagnostic(diagnostic);
                        }
                    }
 
                    // Report diagnostics for duplicate diagnostic ID used across analyzers.
                    foreach (var kvp in idToAnalyzerMap)
                    {
                        var ruleId = kvp.Key;
                        var analyzerToDescriptorLocationsMap = kvp.Value;
                        if (analyzerToDescriptorLocationsMap.Count <= 1)
                        {
                            // ID used by a single analyzer.
                            continue;
                        }
 
                        ImmutableSortedSet<string> sortedAnalyzerNames = analyzerToDescriptorLocationsMap.Keys.ToImmutableSortedSet();
                        var skippedAnalyzerName = sortedAnalyzerNames[0];
                        foreach (var analyzerName in sortedAnalyzerNames.Skip(1))
                        {
                            var locations = analyzerToDescriptorLocationsMap[analyzerName];
                            foreach (var location in locations)
                            {
                                // Diagnostic Id '{0}' is already used by analyzer '{1}'. Please use a different diagnostic ID.
                                var diagnostic = Diagnostic.Create(UseUniqueDiagnosticIdRule, location, ruleId, skippedAnalyzerName);
                                compilationEndContext.ReportDiagnostic(diagnostic);
                            }
                        }
                    }
 
                    // Report analyzer release tracking invalid entry and compilation end diagnostics.
                    if (isAnalyzerReleaseTracking || invalidReleaseFileEntryDiagnostics != null)
                    {
                        RoslynDebug.Assert(shippedData != null);
                        RoslynDebug.Assert(unshippedData != null);
 
                        ReportAnalyzerReleaseTrackingDiagnostics(invalidReleaseFileEntryDiagnostics, shippedData, unshippedData, seenRuleIds, compilationEndContext);
                    }
 
                    seenRuleIds.Free(compilationEndContext.CancellationToken);
                    if (analyzeResourceStrings)
                    {
                        RoslynDebug.Assert(localizableTitles != null);
                        RoslynDebug.Assert(localizableMessages != null);
                        RoslynDebug.Assert(localizableDescriptions != null);
                        RoslynDebug.Assert(resourcesDataValueMap != null);
 
                        FreeLocalizableStringsMap(localizableTitles, compilationEndContext.CancellationToken);
                        FreeLocalizableStringsMap(localizableMessages, compilationEndContext.CancellationToken);
                        FreeLocalizableStringsMap(localizableDescriptions, compilationEndContext.CancellationToken);
                        resourcesDataValueMap.Free(compilationEndContext.CancellationToken);
                    }
 
                    customTagsMap.Free(compilationEndContext.CancellationToken);
                });
            });
 
            static void FreeLocalizableStringsMap(PooledLocalizabeStringsConcurrentDictionary localizableStrings, CancellationToken cancellationToken)
            {
                foreach (var builder in localizableStrings.Values)
                {
                    builder.Free(cancellationToken);
                }
 
                localizableStrings.Free(cancellationToken);
            }
        }
 
        private static bool TryGetDescriptorCreateMethodAndArguments(
            IFieldInitializerOperation fieldInitializer,
            INamedTypeSymbol diagnosticDescriptorType,
            [NotNullWhen(returnValue: true)] out IMethodSymbol? creationMethod,
            [NotNullWhen(returnValue: true)] out ImmutableArray<IArgumentOperation> creationArguments)
        {
            (creationMethod, creationArguments) = fieldInitializer.Value.WalkDownConversion() switch
            {
                IObjectCreationOperation objectCreation when IsDescriptorConstructor(objectCreation.Constructor)
                    => (objectCreation.Constructor, objectCreation.Arguments),
                IInvocationOperation invocation when IsCreateHelper(invocation.TargetMethod)
                    => (invocation.TargetMethod, invocation.Arguments),
                _ => default
            };
 
            return creationMethod != null;
 
            bool IsDescriptorConstructor(IMethodSymbol? method)
                => SymbolEqualityComparer.Default.Equals(method?.ContainingType, diagnosticDescriptorType);
 
            // Heuristic to identify helper methods to create DiagnosticDescriptor:
            //  "A method invocation that returns 'DiagnosticDescriptor' and has a first string parameter named 'id'"
            bool IsCreateHelper(IMethodSymbol method)
                => SymbolEqualityComparer.Default.Equals(method.ReturnType, diagnosticDescriptorType) &&
                    !method.Parameters.IsEmpty &&
                    method.Parameters[0].Name == DiagnosticIdParameterName &&
                    method.Parameters[0].Type.SpecialType == SpecialType.System_String;
        }
 
        private static bool TryGetLocalizableResourceStringCreation(
            IOperation operation,
            INamedTypeSymbol localizableResourceStringType,
            [NotNullWhen(returnValue: true)] out string? nameOfLocalizableResource,
            [NotNullWhen(returnValue: true)] out string? resourceFileName)
        {
            return TryGetConstructorCreation(out nameOfLocalizableResource, out resourceFileName) ||
                TryGetHelperMethodCreation(out nameOfLocalizableResource, out resourceFileName);
 
            //  Local functions
 
            //  Attempts to get the resource and file name for the creation of a localizable resource string using the
            //  constructor on LocalizableResourceString
            bool TryGetConstructorCreation([NotNullWhen(true)] out string? nameOfLocalizableResource, [NotNullWhen(true)] out string? resourceFileName)
            {
                if (operation.WalkDownConversion() is IObjectCreationOperation objectCreation &&
                    SymbolEqualityComparer.Default.Equals(objectCreation.Constructor?.ContainingType, localizableResourceStringType) &&
                    objectCreation.Arguments.Length >= 3 &&
                    objectCreation.Arguments.GetArgumentForParameterAtIndex(0) is { } firstParamArgument &&
                    firstParamArgument.Parameter?.Type.SpecialType == SpecialType.System_String &&
                    firstParamArgument.Value.ConstantValue.HasValue &&
                    firstParamArgument.Value.ConstantValue.Value is string nameOfResource &&
                    objectCreation.Arguments.GetArgumentForParameterAtIndex(2) is { } thirdParamArgument &&
                    thirdParamArgument.Value is ITypeOfOperation typeOfOperation &&
                    typeOfOperation.TypeOperand is { } typeOfType)
                {
                    nameOfLocalizableResource = nameOfResource;
                    resourceFileName = typeOfType.Name;
                    return true;
                }
 
                nameOfLocalizableResource = null;
                resourceFileName = null;
                return false;
            }
 
            //  Attempts to get the resource and file name for the creation of a localizable resource string using a
            //  helper method on the resource class. For an operation to be considered a helper method invocation, it must
            //  - Be an invocation of a static method
            //  - Method must have return type 'LocalizableResourceString'
            //  - Method must have single 'string' parameter
            //  - Argument must be a compile-time constant (typically a nameof operation on one of the resource class's properties).
            bool TryGetHelperMethodCreation([NotNullWhen(true)] out string? nameOfLocalizableResource, [NotNullWhen(true)] out string? resourceFileName)
            {
                if (operation.WalkDownConversion() is IInvocationOperation invocation &&
                    invocation.TargetMethod.ReturnType.Equals(localizableResourceStringType) &&
                    invocation.Arguments.Length == 1 &&
                    invocation.Arguments[0].Parameter?.Type.SpecialType == SpecialType.System_String &&
                    invocation.Arguments[0].Value.ConstantValue.HasValue &&
                    invocation.Arguments[0].Value.ConstantValue.Value is string nameOfResource)
                {
                    nameOfLocalizableResource = nameOfResource;
                    resourceFileName = invocation.TargetMethod.ContainingType.Name;
                    return true;
                }
 
                nameOfLocalizableResource = null;
                resourceFileName = null;
                return false;
            }
        }
 
        private static void AnalyzeTitle(
            OperationAnalysisContext operationAnalysisContext,
            ImmutableArray<IArgumentOperation> creationArguments,
            IFieldInitializerOperation creation,
            INamedTypeSymbol containingType,
            PooledLocalizabeStringsConcurrentDictionary? localizableTitles,
            PooledResourcesDataValueConcurrentDictionary? resourceDataValueMap,
            INamedTypeSymbol localizableStringType,
            INamedTypeSymbol localizableResourceStringType)
        {
            IArgumentOperation? titleArgument = creationArguments.FirstOrDefault(a => a.Parameter?.Name.Equals("title", StringComparison.OrdinalIgnoreCase) == true);
            if (titleArgument != null)
            {
                if (titleArgument.Parameter?.Type.SpecialType == SpecialType.System_String)
                {
                    operationAnalysisContext.ReportDiagnostic(creation.Value.CreateDiagnostic(UseLocalizableStringsInDescriptorRule, WellKnownTypeNames.MicrosoftCodeAnalysisLocalizableString));
                }
 
                AnalyzeDescriptorArgument(operationAnalysisContext, titleArgument,
                    AnalyzeTitleCore, containingType, localizableTitles, resourceDataValueMap,
                    localizableStringType, localizableResourceStringType);
            }
        }
 
        private static void AnalyzeTitleCore(string title, IArgumentOperation argumentOperation, Location fixLocation, Action<Diagnostic> reportDiagnostic)
        {
            var hasLeadingOrTrailingWhitespaces = HasLeadingOrTrailingWhitespaces(title);
            if (hasLeadingOrTrailingWhitespaces)
            {
                title = RemoveLeadingAndTrailingWhitespaces(title);
            }
 
            var isMultiSentences = IsMultiSentences(title);
            var endsWithPeriod = EndsWithPeriod(title);
            var containsLineReturn = ContainsLineReturn(title);
 
            if (isMultiSentences || endsWithPeriod || containsLineReturn || hasLeadingOrTrailingWhitespaces)
            {
                // Leading and trailing spaces were already fixed
                var fixedTitle = endsWithPeriod ? RemoveTrailingPeriod(title) : title;
                fixedTitle = isMultiSentences ? FixMultiSentences(fixedTitle) : fixedTitle;
                fixedTitle = containsLineReturn ? FixLineReturns(fixedTitle, allowMultisentences: false) : fixedTitle;
 
                ReportDefineDiagnosticArgumentCorrectlyDiagnostic(DefineDiagnosticTitleCorrectlyRule,
                    argumentOperation, fixedTitle, fixLocation, reportDiagnostic);
            }
        }
 
        private static void ReportDefineDiagnosticArgumentCorrectlyDiagnostic(
            DiagnosticDescriptor descriptor,
            IArgumentOperation argumentOperation,
            string fixValue,
            Location fixLocation,
            Action<Diagnostic> reportDiagnostic)
        {
            // Additional location in an additional document does not seem to be preserved
            // from analyzer to code fix due to a Roslyn bug: https://github.com/dotnet/roslyn/issues/46377
            // We workaround this bug by passing additional document file path and location span as strings.
 
            var additionalLocations = ImmutableArray<Location>.Empty;
            var properties = ImmutableDictionary<string, string?>.Empty.Add(DefineDescriptorArgumentCorrectlyFixValue, fixValue);
            if (fixLocation.IsInSource)
            {
                additionalLocations = additionalLocations.Add(fixLocation);
            }
            else
            {
                var span = fixLocation.SourceSpan;
                var locationInfo = $"{span.Start}{AdditionalDocumentLocationInfoSeparator}{span.Length}{AdditionalDocumentLocationInfoSeparator}{fixLocation.GetLineSpan().Path}";
                properties = properties.Add(DefineDescriptorArgumentCorrectlyFixAdditionalDocumentLocationInfo, locationInfo);
            }
 
            reportDiagnostic(argumentOperation.CreateDiagnostic(descriptor, additionalLocations, properties));
        }
 
        internal static bool TryGetAdditionalDocumentLocationInfo(Diagnostic diagnostic,
            [NotNullWhen(returnValue: true)] out string? filePath,
            [NotNullWhen(returnValue: true)] out TextSpan? fileSpan)
        {
            Debug.Assert(diagnostic.Id is DiagnosticIds.DefineDiagnosticTitleCorrectlyRuleId or
                DiagnosticIds.DefineDiagnosticMessageCorrectlyRuleId or
                DiagnosticIds.DefineDiagnosticDescriptionCorrectlyRuleId);
 
            filePath = null;
            fileSpan = null;
            if (!diagnostic.Properties.TryGetValue(DefineDescriptorArgumentCorrectlyFixAdditionalDocumentLocationInfo, out var locationInfo)
                || locationInfo is null)
            {
                return false;
            }
 
            var parts = locationInfo.Split(new[] { AdditionalDocumentLocationInfoSeparator }, StringSplitOptions.None);
            if (parts.Length != 3 ||
                !int.TryParse(parts[0], out var spanSpart) ||
                !int.TryParse(parts[1], out var spanLength))
            {
                return false;
            }
 
            fileSpan = new TextSpan(spanSpart, spanLength);
            filePath = parts[2];
            return !string.IsNullOrEmpty(filePath);
        }
 
        private static void AnalyzeMessage(
            OperationAnalysisContext operationAnalysisContext,
            ImmutableArray<IArgumentOperation> creationArguments,
            INamedTypeSymbol containingType,
            PooledLocalizabeStringsConcurrentDictionary? localizableMessages,
            PooledResourcesDataValueConcurrentDictionary? resourceDataValueMap,
            INamedTypeSymbol localizableStringType,
            INamedTypeSymbol localizableResourceStringType)
        {
            var messageArgument = creationArguments.FirstOrDefault(a => a.Parameter?.Name.Equals("messageFormat", StringComparison.OrdinalIgnoreCase) == true);
            if (messageArgument != null)
            {
                AnalyzeDescriptorArgument(operationAnalysisContext, messageArgument,
                    AnalyzeMessageCore, containingType, localizableMessages, resourceDataValueMap,
                    localizableStringType, localizableResourceStringType);
            }
        }
 
        private static void AnalyzeMessageCore(string message, IArgumentOperation argumentOperation, Location fixLocation, Action<Diagnostic> reportDiagnostic)
        {
            var hasLeadingOrTrailingWhitespaces = HasLeadingOrTrailingWhitespaces(message);
            if (hasLeadingOrTrailingWhitespaces)
            {
                message = RemoveLeadingAndTrailingWhitespaces(message);
            }
 
            var isMultiSentences = IsMultiSentences(message);
            var endsWithPeriod = EndsWithPeriod(message);
            var containsLineReturn = ContainsLineReturn(message);
 
            if (isMultiSentences ^ endsWithPeriod || containsLineReturn || hasLeadingOrTrailingWhitespaces)
            {
                // Leading and trailing spaces were already fixed
                var fixedMessage = containsLineReturn ? FixLineReturns(message, allowMultisentences: true) : message;
                isMultiSentences = IsMultiSentences(fixedMessage);
                endsWithPeriod = EndsWithPeriod(fixedMessage);
 
                if (isMultiSentences ^ endsWithPeriod)
                {
                    fixedMessage = endsWithPeriod ? RemoveTrailingPeriod(fixedMessage) : fixedMessage + ".";
                }
 
                ReportDefineDiagnosticArgumentCorrectlyDiagnostic(DefineDiagnosticMessageCorrectlyRule,
                    argumentOperation, fixedMessage, fixLocation, reportDiagnostic);
            }
        }
 
        private static void AnalyzeDescription(
            OperationAnalysisContext operationAnalysisContext,
            ImmutableArray<IArgumentOperation> creationArguments,
            INamedTypeSymbol containingType,
            PooledLocalizabeStringsConcurrentDictionary? localizableDescriptions,
            PooledResourcesDataValueConcurrentDictionary? resourceDataValueMap,
            INamedTypeSymbol localizableStringType,
            INamedTypeSymbol localizableResourceStringType)
        {
            IArgumentOperation? descriptionArgument = creationArguments.FirstOrDefault(a => a.Parameter?.Name.Equals("description", StringComparison.OrdinalIgnoreCase) == true);
            if (descriptionArgument != null)
            {
                AnalyzeDescriptorArgument(operationAnalysisContext, descriptionArgument,
                    AnalyzeDescriptionCore, containingType, localizableDescriptions, resourceDataValueMap,
                    localizableStringType, localizableResourceStringType);
            }
        }
 
        private static void AnalyzeDescriptionCore(string description, IArgumentOperation argumentOperation, Location fixLocation, Action<Diagnostic> reportDiagnostic)
        {
            var hasLeadingOrTrailingWhitespaces = HasLeadingOrTrailingWhitespaces(description);
            if (hasLeadingOrTrailingWhitespaces)
            {
                description = RemoveLeadingAndTrailingWhitespaces(description);
            }
 
            var endsWithPunctuation = EndsWithPunctuation(description);
 
            if (!endsWithPunctuation || hasLeadingOrTrailingWhitespaces)
            {
                var fixedDescription = !endsWithPunctuation ? description + "." : description;
 
                ReportDefineDiagnosticArgumentCorrectlyDiagnostic(DefineDiagnosticDescriptionCorrectlyRule,
                    argumentOperation, fixedDescription, fixLocation, reportDiagnostic);
            }
        }
 
        private static void AnalyzeDescriptorArgument(
            OperationAnalysisContext operationAnalysisContext,
            IArgumentOperation argument,
            Action<string, IArgumentOperation, Location, Action<Diagnostic>> analyzeStringValueCore,
            INamedTypeSymbol containingType,
            PooledLocalizabeStringsConcurrentDictionary? localizableStringsMap,
            PooledResourcesDataValueConcurrentDictionary? resourceDataValueMap,
            INamedTypeSymbol localizableStringType,
            INamedTypeSymbol localizableResourceStringType)
        {
            if (TryGetNonEmptyConstantStringValue(argument, out var argumentValue, out var argumentValueLocation))
            {
                analyzeStringValueCore(argumentValue, argument, argumentValueLocation, operationAnalysisContext.ReportDiagnostic);
            }
            else if (localizableStringsMap != null &&
                SymbolEqualityComparer.Default.Equals(argument.Parameter?.Type, localizableStringType))
            {
                RoslynDebug.Assert(resourceDataValueMap != null);
 
                if (TryGetLocalizableResourceStringCreation(argument.Value, localizableResourceStringType,
                        out var nameOfLocalizableResource, out var resourceFileName))
                {
                    AnalyzeLocalizableDescriptorArgument(analyzeStringValueCore, nameOfLocalizableResource, resourceFileName,
                        argument, resourceDataValueMap, operationAnalysisContext.Options,
                        operationAnalysisContext.ReportDiagnostic, operationAnalysisContext.CancellationToken);
                }
                else
                {
                    var value = argument.Value.WalkDownConversion();
                    if (value is IFieldReferenceOperation fieldReference &&
                        fieldReference.Field.Type.DerivesFrom(localizableStringType, baseTypesOnly: true))
                    {
                        var builder = localizableStringsMap.GetOrAdd(containingType, _ => PooledConcurrentSet<(IFieldSymbol, IArgumentOperation)>.GetInstance());
                        builder.Add((fieldReference.Field, argument));
                    }
                }
            }
        }
 
        private static void AnalyzeLocalizableDescriptorArgument(
            Action<string, IArgumentOperation, Location, Action<Diagnostic>> analyzeStringValueCore,
            string nameOfLocalizableResource,
            string resourceFileName,
            IArgumentOperation argument,
            PooledResourcesDataValueConcurrentDictionary resourceDataValueMap,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            CancellationToken cancellationToken)
        {
            var map = GetOrCreateResourceMap(options, resourceFileName, resourceDataValueMap, cancellationToken);
            if (map.TryGetValue(nameOfLocalizableResource, out var resourceStringTuple))
            {
                analyzeStringValueCore(resourceStringTuple.value, argument, resourceStringTuple.location, reportDiagnostic);
            }
        }
 
        private static void AnalyzeLocalizableStrings(
            PooledLocalizabeStringsConcurrentDictionary localizableStringsMap,
            Action<string, IArgumentOperation, Location, Action<Diagnostic>> analyzeLocalizableStringValueCore,
            PooledFieldToResourceNameAndFileNameConcurrentDictionary symbolToResourceMap,
            INamedTypeSymbol namedType,
            PooledResourcesDataValueConcurrentDictionary resourceDataValueMap,
            AnalyzerOptions options,
            Action<Diagnostic> reportDiagnostic,
            CancellationToken cancellationToken)
        {
            if (localizableStringsMap.TryRemove(namedType, out var localizableFieldsWithOriginalArguments))
            {
                foreach (var (field, argument) in localizableFieldsWithOriginalArguments)
                {
                    if (symbolToResourceMap.TryGetValue(field, out var resourceTuple))
                    {
                        AnalyzeLocalizableDescriptorArgument(analyzeLocalizableStringValueCore, resourceTuple.nameOfResource, resourceTuple.resourceFileName,
                            argument, resourceDataValueMap, options, reportDiagnostic, cancellationToken);
                    }
                }
 
                localizableFieldsWithOriginalArguments.Dispose();
            }
        }
 
        private static bool TryGetNonEmptyConstantStringValue(
            IArgumentOperation argumentOperation,
            [NotNullWhen(true)] out string? value,
            [NotNullWhen(true)] out Location? valueLocation)
        {
            value = null;
            valueLocation = null;
 
            IOperation valueOperation;
            var argumentValueOperation = argumentOperation.Value.WalkDownConversion();
            if (argumentValueOperation is ILiteralOperation literalOperation)
            {
                valueOperation = literalOperation;
            }
            else if (argumentValueOperation is IFieldReferenceOperation fieldReferenceOperation &&
                fieldReferenceOperation.Syntax.SyntaxTree == argumentValueOperation.Syntax.SyntaxTree &&
                fieldReferenceOperation.Field.DeclaringSyntaxReferences.Length == 1 &&
                fieldReferenceOperation.Field.DeclaringSyntaxReferences[0].GetSyntax() is { } fieldDeclaration &&
                fieldDeclaration.SyntaxTree == argumentValueOperation.Syntax.SyntaxTree &&
                GetFieldInitializer(fieldDeclaration, argumentValueOperation.SemanticModel!) is { } fieldInitializer &&
                fieldInitializer.Value.WalkDownConversion() is ILiteralOperation fieldInitializerLiteral)
            {
                valueOperation = fieldInitializerLiteral;
            }
            else
            {
                valueOperation = argumentValueOperation;
            }
 
            if (!TryGetNonEmptyConstantStringValueCore(valueOperation, out var literalValue))
            {
                return false;
            }
 
            value = literalValue;
            valueLocation = valueOperation.Syntax.GetLocation();
            return true;
 
            static IFieldInitializerOperation? GetFieldInitializer(SyntaxNode fieldDeclaration, SemanticModel model)
            {
                if (fieldDeclaration.Language == LanguageNames.VisualBasic)
                {
                    // For VB, the field initializer is on the parent node.
                    fieldDeclaration = fieldDeclaration.Parent!;
                }
 
                foreach (var node in fieldDeclaration.DescendantNodes())
                {
                    if (model.GetOperation(node) is IFieldInitializerOperation initializer)
                    {
                        return initializer;
                    }
                }
 
                return null;
            }
        }
 
        private static bool TryGetNonEmptyConstantStringValueCore(IOperation operation, [NotNullWhen(returnValue: true)] out string? literalValue)
        {
            if (operation.ConstantValue.HasValue &&
                operation.ConstantValue.Value is string value &&
                !string.IsNullOrEmpty(value))
            {
                literalValue = value;
                return true;
            }
 
            literalValue = null;
            return false;
        }
 
        // Assumes that a string is a multi-sentences if it contains a period followed by a whitespace ('. ').
        private const string MultiSentenceSeparator = ". ";
 
        private static bool IsMultiSentences(string s)
            => s.Contains(MultiSentenceSeparator);
 
        private static string FixMultiSentences(string s)
        {
            Debug.Assert(IsMultiSentences(s));
            var index = s.IndexOf(MultiSentenceSeparator, StringComparison.OrdinalIgnoreCase);
            return s[..index];
        }
 
        private static bool EndsWithPeriod(string s)
            => s[^1] == '.';
 
        private static string RemoveTrailingPeriod(string s)
        {
            Debug.Assert(EndsWithPeriod(s));
            return s[0..^1];
        }
 
        private static bool ContainsLineReturn(string s)
            => s.Contains("\r") || s.Contains("\n");
 
        private static string FixLineReturns(string s, bool allowMultisentences)
        {
            Debug.Assert(ContainsLineReturn(s));
 
            var parts = s.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
            if (!allowMultisentences)
            {
                return parts[0];
            }
 
            var builder = new StringBuilder();
            for (var i = 0; i < parts.Length; i++)
            {
                var part = parts[i];
                if (!EndsWithPeriod(part))
                {
                    part += ".";
                }
 
                if (part.TrimEnd().Length == part.Length &&
                    i < parts.Length - 1)
                {
                    part += " ";
                }
 
                builder.Append(part);
            }
 
            return builder.ToString();
        }
 
        private static bool EndsWithPunctuation(string s)
        {
            var lastChar = s[^1];
 
            return lastChar.Equals('.') || lastChar.Equals('!') || lastChar.Equals('?');
        }
 
        private static bool HasLeadingOrTrailingWhitespaces(string s)
            => s.Trim().Length != s.Length;
 
        private static string RemoveLeadingAndTrailingWhitespaces(string s)
        {
            Debug.Assert(HasLeadingOrTrailingWhitespaces(s));
            return s.Trim();
        }
 
        private static void AnalyzeHelpLinkUri(
            OperationAnalysisContext operationAnalysisContext,
            ImmutableArray<IArgumentOperation> creationArguments,
            out string? helpLink)
        {
            helpLink = null;
 
            // Find the matching argument for helpLinkUri
            foreach (var argument in creationArguments)
            {
                if (argument.Parameter?.Name.Equals(HelpLinkUriParameterName, StringComparison.OrdinalIgnoreCase) == true)
                {
                    if (argument.Value.ConstantValue.HasValue)
                    {
                        helpLink = argument.Value.ConstantValue.Value as string;
                        if (helpLink == null)
                        {
                            Diagnostic diagnostic = argument.CreateDiagnostic(ProvideHelpUriInDescriptorRule);
                            operationAnalysisContext.ReportDiagnostic(diagnostic);
                        }
                    }
 
                    return;
                }
            }
        }
 
        private static void AnalyzeCustomTags(
            OperationAnalysisContext operationAnalysisContext,
            ImmutableArray<IArgumentOperation> creationArguments,
            IFieldInitializerOperation fieldInitializerOperation,
            PooledFieldToCustomTagsConcurrentDictionary customTagsMap)
        {
            // Default to indicate unknown set of custom tags.
            ImmutableArray<string> customTags = default;
 
            try
            {
                // Find the matching argument for customTags
                var argument = creationArguments.FirstOrDefault(
                    a => a.Parameter?.Name.Equals(CustomTagsParameterName, StringComparison.OrdinalIgnoreCase) == true);
                if (argument is null ||
                    argument.Value is not IArrayCreationOperation arrayCreation ||
                    arrayCreation.DimensionSizes.Length != 1)
                {
                    return;
                }
 
                if (arrayCreation.DimensionSizes[0].ConstantValue.HasValue &&
                    arrayCreation.DimensionSizes[0].ConstantValue.Value is int size &&
                    size == 0)
                {
                    Diagnostic diagnostic = argument.CreateDiagnostic(ProvideCustomTagsInDescriptorRule);
                    operationAnalysisContext.ReportDiagnostic(diagnostic);
 
                    customTags = ImmutableArray<string>.Empty;
                }
                else if (arrayCreation.Initializer is IArrayInitializerOperation arrayInitializer &&
                    arrayInitializer.ElementValues.All(element => element.ConstantValue.HasValue && element.ConstantValue.Value is string))
                {
                    customTags = arrayInitializer.ElementValues.Select(element => (string)element.ConstantValue.Value!).ToImmutableArray();
                }
            }
            finally
            {
                AddCustomTags(customTags, fieldInitializerOperation, customTagsMap);
            }
 
            static void AddCustomTags(
                ImmutableArray<string> customTags,
                IFieldInitializerOperation fieldInitializerOperation,
                PooledFieldToCustomTagsConcurrentDictionary customTagsMap)
            {
                foreach (var field in fieldInitializerOperation.InitializedFields)
                {
                    customTagsMap[field] = customTags;
                }
            }
        }
 
        private static (bool? isEnabledByDefault, DiagnosticSeverity? defaultSeverity) GetDefaultSeverityAndEnabledByDefault(Compilation compilation, ImmutableArray<IArgumentOperation> creationArguments)
        {
            var diagnosticSeverityType = compilation.GetOrCreateTypeByMetadataName(typeof(DiagnosticSeverity).FullName);
            var ruleLevelType = compilation.GetOrCreateTypeByMetadataName(typeof(RuleLevel).FullName);
 
            bool? isEnabledByDefault = null;
            DiagnosticSeverity? defaultSeverity = null;
 
            foreach (var argument in creationArguments)
            {
                switch (argument.Parameter?.Name)
                {
                    case IsEnabledByDefaultParameterName:
                        if (argument.Value.ConstantValue.HasValue &&
                            argument.Value.ConstantValue.Value is bool value)
                        {
                            isEnabledByDefault = value;
                        }
 
                        break;
 
                    case DefaultSeverityParameterName:
                        if (argument.Value is IFieldReferenceOperation fieldReference &&
                            SymbolEqualityComparer.Default.Equals(fieldReference.Field.ContainingType, diagnosticSeverityType) &&
                            Enum.TryParse(fieldReference.Field.Name, out DiagnosticSeverity parsedSeverity))
                        {
                            defaultSeverity = parsedSeverity;
                        }
 
                        break;
 
                    case RuleLevelParameterName:
                        if (ruleLevelType != null &&
                            argument.Value is IFieldReferenceOperation fieldReference2 &&
                            SymbolEqualityComparer.Default.Equals(fieldReference2.Field.ContainingType, ruleLevelType) &&
                            Enum.TryParse(fieldReference2.Field.Name, out RuleLevel parsedRuleLevel))
                        {
                            switch (parsedRuleLevel)
                            {
                                case RuleLevel.BuildWarning:
                                    defaultSeverity = DiagnosticSeverity.Warning;
                                    isEnabledByDefault = true;
                                    break;
 
                                case RuleLevel.IdeSuggestion:
                                    defaultSeverity = DiagnosticSeverity.Info;
                                    isEnabledByDefault = true;
                                    break;
 
                                case RuleLevel.IdeHidden_BulkConfigurable:
                                    defaultSeverity = DiagnosticSeverity.Hidden;
                                    isEnabledByDefault = true;
                                    break;
 
                                case RuleLevel.Disabled:
                                case RuleLevel.CandidateForRemoval:
                                    isEnabledByDefault = false;
                                    break;
                            }
 
                            return (isEnabledByDefault, defaultSeverity);
                        }
 
                        break;
                }
            }
 
            if (isEnabledByDefault == false)
            {
                defaultSeverity = null;
            }
 
            return (isEnabledByDefault, defaultSeverity);
        }
 
        private static void AnalyzeRuleId(
            OperationAnalysisContext operationAnalysisContext,
            ImmutableArray<IArgumentOperation> creationArguments,
            bool isAnalyzerReleaseTracking,
            ReleaseTrackingData? shippedData,
            ReleaseTrackingData? unshippedData,
            PooledConcurrentSet<string> seenRuleIds,
            AdditionalText? diagnosticCategoryAndIdRangeText,
            string? category,
            string analyzerName,
            string? helpLink,
            bool? isEnabledByDefault,
            DiagnosticSeverity? defaultSeverity,
            ImmutableArray<(string? prefix, int start, int end)> allowedIdsInfoListOpt,
            ConcurrentDictionary<string, ConcurrentDictionary<string, ConcurrentBag<Location>>> idToAnalyzerMap)
        {
            var analyzer = ((IFieldSymbol)operationAnalysisContext.ContainingSymbol).ContainingType.OriginalDefinition;
            string? ruleId = null;
            foreach (var argument in creationArguments)
            {
                if (argument.Parameter?.Name.Equals(DiagnosticIdParameterName, StringComparison.OrdinalIgnoreCase) == true)
                {
                    // Check if diagnostic ID is a constant string.
                    if (argument.Value.ConstantValue.HasValue &&
                        argument.Value.Type != null &&
                        argument.Value.Type.SpecialType == SpecialType.System_String &&
                        argument.Value.ConstantValue.Value is string value)
                    {
                        ruleId = value;
                        seenRuleIds.Add(ruleId);
 
                        var location = argument.Value.Syntax.GetLocation();
                        static string GetAnalyzerName(INamedTypeSymbol a) => a.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat);
 
                        // Factory methods to track declaration locations for every analyzer rule ID.
                        ConcurrentBag<Location> AddLocationFactory(string analyzerName)
                            => new() { location };
 
                        ConcurrentBag<Location> UpdateLocationsFactory(string analyzerName, ConcurrentBag<Location> bag)
                        {
                            bag.Add(location);
                            return bag;
                        }
 
                        ConcurrentDictionary<string, ConcurrentBag<Location>> AddNamedTypeFactory(string r)
                        {
                            var dict = new ConcurrentDictionary<string, ConcurrentBag<Location>>();
                            dict.AddOrUpdate(
                                key: GetAnalyzerName(analyzer),
                                addValueFactory: AddLocationFactory,
                                updateValueFactory: UpdateLocationsFactory);
                            return dict;
                        }
 
                        ConcurrentDictionary<string, ConcurrentBag<Location>> UpdateNamedTypeFactory(string r, ConcurrentDictionary<string, ConcurrentBag<Location>> existingValue)
                        {
                            existingValue.AddOrUpdate(
                                key: GetAnalyzerName(analyzer),
                                addValueFactory: AddLocationFactory,
                                updateValueFactory: UpdateLocationsFactory);
                            return existingValue;
                        }
 
                        idToAnalyzerMap.AddOrUpdate(
                            key: ruleId,
                            addValueFactory: AddNamedTypeFactory,
                            updateValueFactory: UpdateNamedTypeFactory);
 
                        if (IsReservedDiagnosticId(ruleId, operationAnalysisContext.Compilation.AssemblyName))
                        {
                            operationAnalysisContext.ReportDiagnostic(argument.Value.Syntax.CreateDiagnostic(DoNotUseReservedDiagnosticIdRule, ruleId));
                        }
 
                        // If we have an additional file specifying required range and/or format for the ID, validate the ID.
                        if (!allowedIdsInfoListOpt.IsDefault)
                        {
                            AnalyzeAllowedIdsInfoList(ruleId, argument, diagnosticCategoryAndIdRangeText, category, allowedIdsInfoListOpt, operationAnalysisContext.ReportDiagnostic);
                        }
 
                        // If we have an additional file specifying required range and/or format for the ID, validate the ID.
                        if (isAnalyzerReleaseTracking)
                        {
                            RoslynDebug.Assert(shippedData != null);
                            RoslynDebug.Assert(unshippedData != null);
 
                            AnalyzeAnalyzerReleases(ruleId, argument, category, analyzerName, helpLink, isEnabledByDefault,
                                defaultSeverity, shippedData, unshippedData, operationAnalysisContext.ReportDiagnostic);
                        }
                        else if (shippedData == null && unshippedData == null)
                        {
                            var diagnostic = argument.CreateDiagnostic(EnableAnalyzerReleaseTrackingRule, ruleId);
                            operationAnalysisContext.ReportDiagnostic(diagnostic);
                        }
                    }
                    else
                    {
                        // Diagnostic Id for rule '{0}' must be a non-null constant.
                        string arg1 = ((IFieldInitializerOperation)operationAnalysisContext.Operation).InitializedFields.Single().Name;
                        var diagnostic = argument.Value.CreateDiagnostic(DiagnosticIdMustBeAConstantRule, arg1);
                        operationAnalysisContext.ReportDiagnostic(diagnostic);
                    }
 
                    return;
                }
            }
        }
 
        private static bool IsReservedDiagnosticId(string ruleId, string? assemblyName)
        {
            if (ruleId.Length < 3)
            {
                return false;
            }
 
            var isCARule = ruleId.StartsWith("CA", StringComparison.Ordinal);
 
            if (!isCARule &&
                !ruleId.StartsWith("CS", StringComparison.Ordinal) &&
                !ruleId.StartsWith("BC", StringComparison.Ordinal))
            {
                return false;
            }
 
            if (!ruleId[2..].All(char.IsDigit))
            {
                return false;
            }
 
            if (!isCARule)
            {
                // This is a reserved compiler diagnostic ID (CS or BC prefix)
                return true;
            }
 
            if (assemblyName is null)
            {
                // This is a reserved code analysis ID (CA prefix) being reported from an unspecified assembly
                return true;
            }
 
            return !CADiagnosticIdAllowedAssemblies.Contains(assemblyName);
        }
    }
}