File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\NamingStyles\Serialization\NamingStylePreferences.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.NamingStyles;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
 
/// <summary>
/// Contains all information related to Naming Style Preferences.
/// 1. Symbol Specifications
/// 2. Name Style
/// 3. Naming Rule (points to Symbol Specification IDs)
/// </summary>
[DataContract]
internal sealed class NamingStylePreferences : IEquatable<NamingStylePreferences>
{
    private const int s_serializationVersion = 5;
 
    private static readonly string _defaultNamingPreferencesString = $@"
<NamingPreferencesInfo SerializationVersion=""{s_serializationVersion}"">
  <SymbolSpecifications>
    <SymbolSpecification ID=""5c545a62-b14d-460a-88d8-e936c0a39316"" Name=""{CompilerExtensionsResources.Class}"">
      <ApplicableSymbolKindList>
        <TypeKind>Class</TypeKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""23d856b4-5089-4405-83ce-749aada99153"" Name=""{CompilerExtensionsResources.Interface}"">
      <ApplicableSymbolKindList>
        <TypeKind>Interface</TypeKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""d1796e78-ff66-463f-8576-eb46416060c0"" Name=""{CompilerExtensionsResources.Struct}"">
      <ApplicableSymbolKindList>
        <TypeKind>Struct</TypeKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""d8af8dc6-1ade-441d-9947-8946922e198a"" Name=""{CompilerExtensionsResources.Enum}"">
      <ApplicableSymbolKindList>
        <TypeKind>Enum</TypeKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""408a3347-b908-4b54-a954-1355e64c1de3"" Name=""{CompilerExtensionsResources.Delegate}"">
      <ApplicableSymbolKindList>
        <TypeKind>Delegate</TypeKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""830657f6-e7e5-4830-b328-f109d3b6c165"" Name=""{CompilerExtensionsResources.Event}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Event</SymbolKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""390caed4-f0a9-42bb-adbb-b44c4a302a22"" Name=""{CompilerExtensionsResources.Method}"">
      <ApplicableSymbolKindList>
        <MethodKind>Ordinary</MethodKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""af410767-f189-47c6-b140-aeccf1ff242e"" Name=""{CompilerExtensionsResources.Private_Method}"">
      <ApplicableSymbolKindList>
        <MethodKind>Ordinary</MethodKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Private</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""8076757e-6a4a-47f1-9b4b-ae8a3284e987"" Name=""{CompilerExtensionsResources.Abstract_Method}"">
      <ApplicableSymbolKindList>
        <MethodKind>Ordinary</MethodKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList>
        <ModifierKind>IsAbstract</ModifierKind>
      </RequiredModifierList>
    </SymbolSpecification>
    <SymbolSpecification ID=""16133061-a8e7-4392-92c3-1d93cd54c218"" Name=""{CompilerExtensionsResources.Static_Method}"">
      <ApplicableSymbolKindList>
        <MethodKind>Ordinary</MethodKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList>
        <ModifierKind>IsStatic</ModifierKind>
      </RequiredModifierList>
    </SymbolSpecification>
    <SymbolSpecification ID=""da6a2919-5aa6-4ad1-a24d-576776ed3974"" Name=""{CompilerExtensionsResources.Property}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Property</SymbolKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""b24a91ce-3501-4799-b6df-baf044156c83"" Name=""{CompilerExtensionsResources.Public_or_Protected_Field}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Field</SymbolKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""70af42cb-1741-4027-969c-9edc4877d965"" Name=""{CompilerExtensionsResources.Static_Field}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Field</SymbolKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList>
        <ModifierKind>IsStatic</ModifierKind>
      </RequiredModifierList>
    </SymbolSpecification>
    <SymbolSpecification ID=""10790aa6-0a0b-432d-a52d-d252ca92302b"" Name=""{CompilerExtensionsResources.Private_or_Internal_Field}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Field</SymbolKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""ac995be4-88de-4771-9dcc-a456a7c02d89"" Name=""{CompilerExtensionsResources.Private_or_Internal_Static_Field}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Field</SymbolKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList>
        <ModifierKind>IsStatic</ModifierKind>
      </RequiredModifierList>
    </SymbolSpecification>
    <SymbolSpecification ID=""2c07f5bf-bc81-4c2b-82b4-ae9b3ffd0ba4"" Name=""{CompilerExtensionsResources.Types}"">
      <ApplicableSymbolKindList>
        <TypeKind>Class</TypeKind>
        <TypeKind>Struct</TypeKind>
        <TypeKind>Interface</TypeKind>
        <TypeKind>Enum</TypeKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
    <SymbolSpecification ID=""5f3ddba1-279f-486c-801e-5c097c36dd85"" Name=""{CompilerExtensionsResources.Non_Field_Members}"">
      <ApplicableSymbolKindList>
        <SymbolKind>Property</SymbolKind>
        <SymbolKind>Event</SymbolKind>
        <MethodKind>Ordinary</MethodKind>
      </ApplicableSymbolKindList>
      <ApplicableAccessibilityList>
        <AccessibilityKind>Public</AccessibilityKind>
        <AccessibilityKind>Internal</AccessibilityKind>
        <AccessibilityKind>Private</AccessibilityKind>
        <AccessibilityKind>Protected</AccessibilityKind>
        <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
        <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
      </ApplicableAccessibilityList>
      <RequiredModifierList />
    </SymbolSpecification>
  </SymbolSpecifications>
  <NamingStyles>
    <NamingStyle ID=""87e7c501-9948-4b53-b1eb-a6cbe918feee"" Name=""{CompilerExtensionsResources.Pascal_Case}"" Prefix="""" Suffix="""" WordSeparator="""" CapitalizationScheme=""PascalCase"" />
    <NamingStyle ID=""1ecc5eb6-b5fc-49a5-a9f1-a980f3e48c92"" Name=""{CompilerExtensionsResources.Begins_with_I}"" Prefix=""I"" Suffix="""" WordSeparator="""" CapitalizationScheme=""PascalCase"" />
  </NamingStyles>
  <NamingRules>
    <SerializableNamingRule SymbolSpecificationID=""23d856b4-5089-4405-83ce-749aada99153"" NamingStyleID=""1ecc5eb6-b5fc-49a5-a9f1-a980f3e48c92"" EnforcementLevel=""Info"" />
    <SerializableNamingRule SymbolSpecificationID=""2c07f5bf-bc81-4c2b-82b4-ae9b3ffd0ba4"" NamingStyleID=""87e7c501-9948-4b53-b1eb-a6cbe918feee"" EnforcementLevel=""Info"" />
    <SerializableNamingRule SymbolSpecificationID=""5f3ddba1-279f-486c-801e-5c097c36dd85"" NamingStyleID=""87e7c501-9948-4b53-b1eb-a6cbe918feee"" EnforcementLevel=""Info"" />
  </NamingRules>
</NamingPreferencesInfo>
";
 
    [DataMember(Order = 0)]
    public readonly ImmutableArray<SymbolSpecification> SymbolSpecifications;
 
    [DataMember(Order = 1)]
    public readonly ImmutableArray<NamingStyle> NamingStyles;
 
    [DataMember(Order = 2)]
    public readonly ImmutableArray<SerializableNamingRule> NamingRules;
 
    private readonly Lazy<NamingStyleRules> _lazyRules;
 
    public NamingStylePreferences(
        ImmutableArray<SymbolSpecification> symbolSpecifications,
        ImmutableArray<NamingStyle> namingStyles,
        ImmutableArray<SerializableNamingRule> namingRules)
    {
        SymbolSpecifications = symbolSpecifications;
        NamingStyles = namingStyles;
        NamingRules = namingRules;
 
        _lazyRules = new Lazy<NamingStyleRules>(CreateRules, isThreadSafe: true);
    }
 
    public static NamingStylePreferences Default { get; } = FromXElement(XElement.Parse(DefaultNamingPreferencesString));
    public static NamingStylePreferences Empty { get; } = new([], [], []);
 
    public static string DefaultNamingPreferencesString => _defaultNamingPreferencesString;
 
    public bool IsEmpty
        => SymbolSpecifications.IsEmpty && NamingStyles.IsEmpty && NamingRules.IsEmpty;
 
    internal NamingStyle GetNamingStyle(Guid namingStyleID)
        => NamingStyles.Single(s => s.ID == namingStyleID);
 
    internal SymbolSpecification GetSymbolSpecification(Guid symbolSpecificationID)
        => SymbolSpecifications.Single(s => s.ID == symbolSpecificationID);
 
    public NamingStyleRules Rules => _lazyRules.Value;
 
    public NamingStyleRules CreateRules()
        => new(NamingRules.Select(r => r.GetRule(this)).ToImmutableArray());
 
    internal XElement CreateXElement()
    {
        return new XElement("NamingPreferencesInfo",
            new XAttribute("SerializationVersion", s_serializationVersion),
            new XElement("SymbolSpecifications", SymbolSpecifications.Select(s => s.CreateXElement())),
            new XElement("NamingStyles", NamingStyles.Select(n => n.CreateXElement())),
            new XElement("NamingRules", NamingRules.Select(n => n.CreateXElement())));
    }
 
    internal static NamingStylePreferences FromXElement(XElement element)
    {
        element = GetUpgradedSerializationIfNecessary(element);
 
        return new NamingStylePreferences(
            element.Element("SymbolSpecifications").Elements(nameof(SymbolSpecification))
                   .Select(SymbolSpecification.FromXElement).ToImmutableArray(),
            element.Element("NamingStyles").Elements(nameof(NamingStyle))
                   .Select(NamingStyle.FromXElement).ToImmutableArray(),
            element.Element("NamingRules").Elements(nameof(SerializableNamingRule))
                   .Select(SerializableNamingRule.FromXElement).ToImmutableArray());
    }
 
    public override bool Equals(object obj)
        => Equals(obj as NamingStylePreferences);
 
    public bool Equals(NamingStylePreferences other)
    {
        if (other is null)
            return false;
 
        return SymbolSpecifications.SequenceEqual(other.SymbolSpecifications)
            && NamingStyles.SequenceEqual(other.NamingStyles)
            && NamingRules.SequenceEqual(other.NamingRules);
    }
 
    public static bool operator ==(NamingStylePreferences left, NamingStylePreferences right)
    {
        if (left is null && right is null)
        {
            return true;
        }
        else if (left is null || right is null)
        {
            return false;
        }
 
        return left.Equals(right);
    }
 
    public static bool operator !=(NamingStylePreferences left, NamingStylePreferences right)
        => !(left == right);
 
    public override int GetHashCode()
    {
        return Hash.Combine(Hash.CombineValues(SymbolSpecifications),
            Hash.Combine(Hash.CombineValues(NamingStyles),
                Hash.CombineValues(NamingRules)));
    }
 
    private static XElement GetUpgradedSerializationIfNecessary(XElement rootElement)
    {
        var serializationVersion = int.Parse(rootElement.Attribute("SerializationVersion").Value);
 
        if (serializationVersion == 4)
        {
            UpgradeSerialization_4To5(rootElement = new XElement(rootElement));
            serializationVersion = 5;
        }
 
        // Add future version checks here. If the version is off by more than 1, these upgrades will run in sequence.
        // The next one should check serializationVersion == 5 and update it to 6.
        // It is also important to create a new roaming location in NamingStyleOptions.NamingPreferences
        // so that we never store the new format in an older version.
        Debug.Assert(s_serializationVersion == 5, "After increasing the serialization version, add an upgrade path here.");
 
        return serializationVersion == s_serializationVersion
            ? rootElement
            : XElement.Parse(DefaultNamingPreferencesString);
    }
 
    private static void UpgradeSerialization_4To5(XElement rootElement)
    {
        var methodElements = rootElement
            .Descendants()
            .Where(e => e.Name.LocalName == "SymbolKind" && e.Value == "Method").ToList();
 
        foreach (var element in methodElements)
        {
            element.ReplaceWith(XElement.Parse("<MethodKind>Ordinary</MethodKind>"));
        }
    }
}