File: Diagnostics\NamingStyles\EditorConfigNamingStyleParserTests.cs
Web Access
Project: src\src\EditorFeatures\CSharpTest\Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests.csproj (Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests)
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
using Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics.NamingStyles;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.UnitTests;
using Roslyn.Test.Utilities;
using Xunit;
using static Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles.SymbolSpecification;
 
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.NamingStyles;
 
public class EditorConfigNamingStyleParserTests
{
    [Fact]
    public void TestPascalCaseRule()
    {
        var dictionary = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.methods_and_properties_must_be_pascal_case.severity"] = "warning",
            ["dotnet_naming_rule.methods_and_properties_must_be_pascal_case.symbols"] = "method_and_property_symbols",
            ["dotnet_naming_rule.methods_and_properties_must_be_pascal_case.style"] = "pascal_case_style",
            ["dotnet_naming_symbols.method_and_property_symbols.applicable_kinds"] = "method,property",
            ["dotnet_naming_symbols.method_and_property_symbols.applicable_accessibilities"] = "*",
            ["dotnet_naming_style.pascal_case_style.capitalization"] = "pascal_case"
        };
        var result = OptionsTestHelpers.ParseNamingStylePreferences(dictionary);
        Assert.Single(result.NamingRules);
        var namingRule = result.NamingRules.Single();
        Assert.Single(result.NamingStyles);
        var namingStyle = result.NamingStyles.Single();
        Assert.Single(result.SymbolSpecifications);
        var symbolSpec = result.SymbolSpecifications.Single();
        Assert.Equal(namingStyle.ID, namingRule.NamingStyleID);
        Assert.Equal(symbolSpec.ID, namingRule.SymbolSpecificationID);
        Assert.Equal(ReportDiagnostic.Warn, namingRule.EnforcementLevel);
        Assert.Equal("method_and_property_symbols", symbolSpec.Name);
        var expectedApplicableSymbolKindList = new[]
        {
            new SymbolKindOrTypeKind(MethodKind.Ordinary),
            new SymbolKindOrTypeKind(SymbolKind.Property)
        };
        AssertEx.SetEqual(expectedApplicableSymbolKindList, symbolSpec.ApplicableSymbolKindList);
        Assert.Empty(symbolSpec.RequiredModifierList);
        var expectedApplicableAccessibilityList = new[]
        {
            Accessibility.NotApplicable,
            Accessibility.Public,
            Accessibility.Internal,
            Accessibility.Private,
            Accessibility.Protected,
            Accessibility.ProtectedAndInternal,
            Accessibility.ProtectedOrInternal
        };
        AssertEx.SetEqual(expectedApplicableAccessibilityList, symbolSpec.ApplicableAccessibilityList);
        Assert.Equal("pascal_case_style", namingStyle.Name);
        Assert.Equal("", namingStyle.Prefix);
        Assert.Equal("", namingStyle.Suffix);
        Assert.Equal("", namingStyle.WordSeparator);
        Assert.Equal(Capitalization.PascalCase, namingStyle.CapitalizationScheme);
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/40705")]
    public void TestPascalCaseRuleWithKeyCapitalization()
    {
        var dictionary = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.methods_and_properties_must_be_pascal_case.severity"] = "warning",
            ["dotnet_naming_rule.methods_and_properties_must_be_pascal_case.symbols"] = "Method_and_Property_symbols",
            ["dotnet_naming_rule.methods_and_properties_must_be_pascal_case.style"] = "Pascal_Case_style",
            ["dotnet_naming_symbols.method_and_property_symbols.applicable_kinds"] = "method,property",
            ["dotnet_naming_symbols.method_and_property_symbols.applicable_accessibilities"] = "*",
            ["dotnet_naming_style.pascal_case_style.capitalization"] = "pascal_case"
        };
        var result = OptionsTestHelpers.ParseNamingStylePreferences(dictionary);
        var namingRule = Assert.Single(result.NamingRules);
        var namingStyle = Assert.Single(result.NamingStyles);
        var symbolSpec = Assert.Single(result.SymbolSpecifications);
        Assert.Equal(namingStyle.ID, namingRule.NamingStyleID);
        Assert.Equal(symbolSpec.ID, namingRule.SymbolSpecificationID);
        Assert.Equal(ReportDiagnostic.Warn, namingRule.EnforcementLevel);
    }
 
    [Fact]
    public void TestAsyncMethodsAndLocalFunctionsRule()
    {
        var dictionary = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.async_methods_must_end_with_async.severity"] = "error",
            ["dotnet_naming_rule.async_methods_must_end_with_async.symbols"] = "method_symbols",
            ["dotnet_naming_rule.async_methods_must_end_with_async.style"] = "end_in_async_style",
            ["dotnet_naming_symbols.method_symbols.applicable_kinds"] = "method,local_function",
            ["dotnet_naming_symbols.method_symbols.required_modifiers"] = "async",
            ["dotnet_naming_style.end_in_async_style.capitalization "] = "pascal_case",
            ["dotnet_naming_style.end_in_async_style.required_suffix"] = "Async",
        };
        var result = OptionsTestHelpers.ParseNamingStylePreferences(dictionary);
        Assert.Single(result.NamingRules);
        var namingRule = result.NamingRules.Single();
        Assert.Single(result.NamingStyles);
        var namingStyle = result.NamingStyles.Single();
        Assert.Single(result.SymbolSpecifications);
        var symbolSpec = result.SymbolSpecifications.Single();
        Assert.Equal(namingStyle.ID, namingRule.NamingStyleID);
        Assert.Equal(symbolSpec.ID, namingRule.SymbolSpecificationID);
        Assert.Equal(ReportDiagnostic.Error, namingRule.EnforcementLevel);
        Assert.Equal("method_symbols", symbolSpec.Name);
        var expectedApplicableSymbolKindList = new[]
        {
            new SymbolKindOrTypeKind(MethodKind.Ordinary),
            new SymbolKindOrTypeKind(MethodKind.LocalFunction)
        };
        AssertEx.SetEqual(expectedApplicableSymbolKindList, symbolSpec.ApplicableSymbolKindList);
        Assert.Single(symbolSpec.RequiredModifierList);
        Assert.Contains(new ModifierKind(ModifierKindEnum.IsAsync), symbolSpec.RequiredModifierList);
        Assert.Equal(
            new[] { Accessibility.NotApplicable, Accessibility.Public, Accessibility.Internal, Accessibility.Private, Accessibility.Protected, Accessibility.ProtectedAndInternal, Accessibility.ProtectedOrInternal },
            symbolSpec.ApplicableAccessibilityList);
        Assert.Equal("end_in_async_style", namingStyle.Name);
        Assert.Equal("", namingStyle.Prefix);
        Assert.Equal("Async", namingStyle.Suffix);
        Assert.Equal("", namingStyle.WordSeparator);
        Assert.Equal(Capitalization.PascalCase, namingStyle.CapitalizationScheme);
    }
 
    [Fact]
    public void TestRuleWithoutCapitalization()
    {
        var dictionary = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.async_methods_must_end_with_async.symbols"] = "any_async_methods",
            ["dotnet_naming_rule.async_methods_must_end_with_async.style"] = "end_in_async",
            ["dotnet_naming_rule.async_methods_must_end_with_async.severity"] = "suggestion",
            ["dotnet_naming_symbols.any_async_methods.applicable_kinds"] = "method",
            ["dotnet_naming_symbols.any_async_methods.applicable_accessibilities"] = "*",
            ["dotnet_naming_symbols.any_async_methods.required_modifiers"] = "async",
            ["dotnet_naming_style.end_in_async.required_suffix"] = "Async",
        };
        var result = OptionsTestHelpers.ParseNamingStylePreferences(dictionary);
        Assert.Empty(result.NamingStyles);
    }
 
    [Fact]
    public void TestPublicMembersCapitalizedRule()
    {
        var dictionary = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.public_members_must_be_capitalized.severity"] = "suggestion",
            ["dotnet_naming_rule.public_members_must_be_capitalized.symbols"] = "public_symbols",
            ["dotnet_naming_rule.public_members_must_be_capitalized.style"] = "first_word_upper_case_style",
            ["dotnet_naming_symbols.public_symbols.applicable_kinds"] = "property,method,field,event,delegate",
            ["dotnet_naming_symbols.public_symbols.applicable_accessibilities"] = "public,internal,protected,protected_internal",
            ["dotnet_naming_style.first_word_upper_case_style.capitalization"] = "first_word_upper",
        };
        var result = OptionsTestHelpers.ParseNamingStylePreferences(dictionary);
        Assert.Single(result.NamingRules);
        var namingRule = result.NamingRules.Single();
        Assert.Single(result.NamingStyles);
        var namingStyle = result.NamingStyles.Single();
        Assert.Single(result.SymbolSpecifications);
        var symbolSpec = result.SymbolSpecifications.Single();
        Assert.Equal(namingStyle.ID, namingRule.NamingStyleID);
        Assert.Equal(symbolSpec.ID, namingRule.SymbolSpecificationID);
        Assert.Equal(ReportDiagnostic.Info, namingRule.EnforcementLevel);
        Assert.Equal("public_symbols", symbolSpec.Name);
        var expectedApplicableSymbolKindList = new[]
        {
            new SymbolKindOrTypeKind(SymbolKind.Property),
            new SymbolKindOrTypeKind(MethodKind.Ordinary),
            new SymbolKindOrTypeKind(SymbolKind.Field),
            new SymbolKindOrTypeKind(SymbolKind.Event),
            new SymbolKindOrTypeKind(TypeKind.Delegate)
        };
        AssertEx.SetEqual(expectedApplicableSymbolKindList, symbolSpec.ApplicableSymbolKindList);
        var expectedApplicableAccessibilityList = new[]
        {
            Accessibility.Public,
            Accessibility.Internal,
            Accessibility.Protected,
            Accessibility.ProtectedOrInternal
        };
        AssertEx.SetEqual(expectedApplicableAccessibilityList, symbolSpec.ApplicableAccessibilityList);
        Assert.Empty(symbolSpec.RequiredModifierList);
        Assert.Equal("first_word_upper_case_style", namingStyle.Name);
        Assert.Equal("", namingStyle.Prefix);
        Assert.Equal("", namingStyle.Suffix);
        Assert.Equal("", namingStyle.WordSeparator);
        Assert.Equal(Capitalization.FirstUpper, namingStyle.CapitalizationScheme);
    }
 
    [Fact]
    public void TestNonPublicMembersLowerCaseRule()
    {
        var dictionary = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.non_public_members_must_be_lower_case.severity"] = "incorrect",
            ["dotnet_naming_rule.non_public_members_must_be_lower_case.symbols "] = "non_public_symbols",
            ["dotnet_naming_rule.non_public_members_must_be_lower_case.style   "] = "all_lower_case_style",
            ["dotnet_naming_symbols.non_public_symbols.applicable_kinds  "] = "property,method,field,event,delegate",
            ["dotnet_naming_symbols.non_public_symbols.applicable_accessibilities"] = "private",
            ["dotnet_naming_style.all_lower_case_style.capitalization"] = "all_lower",
        };
        var result = OptionsTestHelpers.ParseNamingStylePreferences(dictionary);
        Assert.Single(result.NamingRules);
        var namingRule = result.NamingRules.Single();
        Assert.Single(result.NamingStyles);
        var namingStyle = result.NamingStyles.Single();
        Assert.Single(result.SymbolSpecifications);
        var symbolSpec = result.SymbolSpecifications.Single();
        Assert.Equal(namingStyle.ID, namingRule.NamingStyleID);
        Assert.Equal(symbolSpec.ID, namingRule.SymbolSpecificationID);
        Assert.Equal(ReportDiagnostic.Hidden, namingRule.EnforcementLevel);
        Assert.Equal("non_public_symbols", symbolSpec.Name);
        var expectedApplicableSymbolKindList = new[]
        {
            new SymbolKindOrTypeKind(SymbolKind.Property),
            new SymbolKindOrTypeKind(MethodKind.Ordinary),
            new SymbolKindOrTypeKind(SymbolKind.Field),
            new SymbolKindOrTypeKind(SymbolKind.Event),
            new SymbolKindOrTypeKind(TypeKind.Delegate)
        };
        AssertEx.SetEqual(expectedApplicableSymbolKindList, symbolSpec.ApplicableSymbolKindList);
        Assert.Single(symbolSpec.ApplicableAccessibilityList);
        Assert.Contains(Accessibility.Private, symbolSpec.ApplicableAccessibilityList);
        Assert.Empty(symbolSpec.RequiredModifierList);
        Assert.Equal("all_lower_case_style", namingStyle.Name);
        Assert.Equal("", namingStyle.Prefix);
        Assert.Equal("", namingStyle.Suffix);
        Assert.Equal("", namingStyle.WordSeparator);
        Assert.Equal(Capitalization.AllLower, namingStyle.CapitalizationScheme);
    }
 
    [Fact]
    public void TestParametersAndLocalsAreCamelCaseRule()
    {
        var dictionary = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.parameters_and_locals_are_camel_case.severity"] = "suggestion",
            ["dotnet_naming_rule.parameters_and_locals_are_camel_case.symbols"] = "parameters_and_locals",
            ["dotnet_naming_rule.parameters_and_locals_are_camel_case.style"] = "camel_case_style",
            ["dotnet_naming_symbols.parameters_and_locals.applicable_kinds"] = "parameter,local",
            ["dotnet_naming_style.camel_case_style.capitalization"] = "camel_case",
        };
 
        var result = OptionsTestHelpers.ParseNamingStylePreferences(dictionary);
        Assert.Single(result.NamingRules);
        var namingRule = result.NamingRules.Single();
        Assert.Single(result.NamingStyles);
        var namingStyle = result.NamingStyles.Single();
        Assert.Single(result.SymbolSpecifications);
 
        var symbolSpec = result.SymbolSpecifications.Single();
        Assert.Equal(namingStyle.ID, namingRule.NamingStyleID);
        Assert.Equal(symbolSpec.ID, namingRule.SymbolSpecificationID);
        Assert.Equal(ReportDiagnostic.Info, namingRule.EnforcementLevel);
 
        Assert.Equal("parameters_and_locals", symbolSpec.Name);
        var expectedApplicableSymbolKindList = new[]
        {
            new SymbolKindOrTypeKind(SymbolKind.Parameter),
            new SymbolKindOrTypeKind(SymbolKind.Local),
        };
        AssertEx.SetEqual(expectedApplicableSymbolKindList, symbolSpec.ApplicableSymbolKindList);
        Assert.Equal(
            new[] { Accessibility.NotApplicable, Accessibility.Public, Accessibility.Internal, Accessibility.Private, Accessibility.Protected, Accessibility.ProtectedAndInternal, Accessibility.ProtectedOrInternal },
            symbolSpec.ApplicableAccessibilityList);
        Assert.Empty(symbolSpec.RequiredModifierList);
 
        Assert.Equal("camel_case_style", namingStyle.Name);
        Assert.Equal("", namingStyle.Prefix);
        Assert.Equal("", namingStyle.Suffix);
        Assert.Equal("", namingStyle.WordSeparator);
        Assert.Equal(Capitalization.CamelCase, namingStyle.CapitalizationScheme);
    }
 
    [Fact]
    public void TestLocalFunctionsAreCamelCaseRule()
    {
        var dictionary = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.local_functions_are_camel_case.severity"] = "suggestion",
            ["dotnet_naming_rule.local_functions_are_camel_case.symbols"] = "local_functions",
            ["dotnet_naming_rule.local_functions_are_camel_case.style"] = "camel_case_style",
            ["dotnet_naming_symbols.local_functions.applicable_kinds"] = "local_function",
            ["dotnet_naming_style.camel_case_style.capitalization"] = "camel_case",
        };
 
        var result = OptionsTestHelpers.ParseNamingStylePreferences(dictionary);
        Assert.Single(result.NamingRules);
        var namingRule = result.NamingRules.Single();
        Assert.Single(result.NamingStyles);
        var namingStyle = result.NamingStyles.Single();
        Assert.Single(result.SymbolSpecifications);
 
        var symbolSpec = result.SymbolSpecifications.Single();
        Assert.Equal(namingStyle.ID, namingRule.NamingStyleID);
        Assert.Equal(symbolSpec.ID, namingRule.SymbolSpecificationID);
        Assert.Equal(ReportDiagnostic.Info, namingRule.EnforcementLevel);
 
        Assert.Equal("local_functions", symbolSpec.Name);
        var expectedApplicableSymbolKindList = new[] { new SymbolKindOrTypeKind(MethodKind.LocalFunction) };
        AssertEx.SetEqual(expectedApplicableSymbolKindList, symbolSpec.ApplicableSymbolKindList);
        Assert.Equal(
            new[] { Accessibility.NotApplicable, Accessibility.Public, Accessibility.Internal, Accessibility.Private, Accessibility.Protected, Accessibility.ProtectedAndInternal, Accessibility.ProtectedOrInternal },
            symbolSpec.ApplicableAccessibilityList);
        Assert.Empty(symbolSpec.RequiredModifierList);
 
        Assert.Equal("camel_case_style", namingStyle.Name);
        Assert.Equal("", namingStyle.Prefix);
        Assert.Equal("", namingStyle.Suffix);
        Assert.Equal("", namingStyle.WordSeparator);
        Assert.Equal(Capitalization.CamelCase, namingStyle.CapitalizationScheme);
    }
 
    [Fact]
    public void TestNoRulesAreReturned()
    {
        var dictionary = new Dictionary<string, string>()
        {
            ["dotnet_naming_symbols.non_public_symbols.applicable_kinds  "] = "property,method,field,event,delegate",
            ["dotnet_naming_symbols.non_public_symbols.applicable_accessibilities"] = "private",
            ["dotnet_naming_style.all_lower_case_style.capitalization"] = "all_lower",
        };
        var result = OptionsTestHelpers.ParseNamingStylePreferences(dictionary);
        Assert.Empty(result.NamingRules);
        Assert.Empty(result.NamingStyles);
        Assert.Empty(result.SymbolSpecifications);
    }
 
    [Theory]
    [InlineData("property,method", new object[] { SymbolKind.Property, MethodKind.Ordinary })]
    [InlineData("namespace", new object[] { SymbolKind.Namespace })]
    [InlineData("type_parameter", new object[] { SymbolKind.TypeParameter })]
    [InlineData("interface", new object[] { TypeKind.Interface })]
    [InlineData("*", new object[] { SymbolKind.Namespace, TypeKind.Class, TypeKind.Struct, TypeKind.Interface, TypeKind.Enum, SymbolKind.Property, MethodKind.Ordinary, MethodKind.LocalFunction, SymbolKind.Field, SymbolKind.Event, TypeKind.Delegate, SymbolKind.Parameter, SymbolKind.TypeParameter, SymbolKind.Local })]
    [InlineData(null, new object[] { SymbolKind.Namespace, TypeKind.Class, TypeKind.Struct, TypeKind.Interface, TypeKind.Enum, SymbolKind.Property, MethodKind.Ordinary, MethodKind.LocalFunction, SymbolKind.Field, SymbolKind.Event, TypeKind.Delegate, SymbolKind.Parameter, SymbolKind.TypeParameter, SymbolKind.Local })]
    [InlineData("property,method,invalid", new object[] { SymbolKind.Property, MethodKind.Ordinary })]
    [InlineData("invalid", new object[] { })]
    [InlineData("", new object[] { })]
    [WorkItem("https://github.com/dotnet/roslyn/issues/20907")]
    public void TestApplicableKindsParse(string specification, object[] typeOrSymbolKinds)
    {
        var rule = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.kinds_parse.severity"] = "error",
            ["dotnet_naming_rule.kinds_parse.symbols"] = "kinds",
            ["dotnet_naming_rule.kinds_parse.style"] = "pascal_case",
            ["dotnet_naming_style.pascal_case.capitalization "] = "pascal_case",
        };
 
        if (specification != null)
        {
            rule["dotnet_naming_symbols.kinds.applicable_kinds"] = specification;
        }
 
        var kinds = typeOrSymbolKinds.Select(NamingStylesTestOptionSets.ToSymbolKindOrTypeKind).ToArray();
        var result = OptionsTestHelpers.ParseNamingStylePreferences(rule);
        Assert.Equal(kinds, result.SymbolSpecifications.SelectMany(x => x.ApplicableSymbolKindList));
    }
 
    [Theory]
    [InlineData("internal,protected_internal", new[] { Accessibility.Internal, Accessibility.ProtectedOrInternal })]
    [InlineData("friend,protected_friend", new[] { Accessibility.Friend, Accessibility.ProtectedOrFriend })]
    [InlineData("private_protected", new[] { Accessibility.ProtectedAndInternal })]
    [InlineData("local", new[] { Accessibility.NotApplicable })]
    [InlineData("*", new[] { Accessibility.NotApplicable, Accessibility.Public, Accessibility.Internal, Accessibility.Private, Accessibility.Protected, Accessibility.ProtectedAndInternal, Accessibility.ProtectedOrInternal })]
    [InlineData(null, new[] { Accessibility.NotApplicable, Accessibility.Public, Accessibility.Internal, Accessibility.Private, Accessibility.Protected, Accessibility.ProtectedAndInternal, Accessibility.ProtectedOrInternal })]
    [InlineData("internal,protected,invalid", new[] { Accessibility.Internal, Accessibility.Protected })]
    [InlineData("invalid", new Accessibility[] { })]
    [InlineData("", new Accessibility[] { })]
    [WorkItem("https://github.com/dotnet/roslyn/issues/20907")]
    public void TestApplicableAccessibilitiesParse(string specification, Accessibility[] accessibilities)
    {
        var rule = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.accessibilities_parse.severity"] = "error",
            ["dotnet_naming_rule.accessibilities_parse.symbols"] = "accessibilities",
            ["dotnet_naming_rule.accessibilities_parse.style"] = "pascal_case",
            ["dotnet_naming_style.pascal_case.capitalization "] = "pascal_case",
        };
 
        if (specification != null)
        {
            rule["dotnet_naming_symbols.accessibilities.applicable_accessibilities"] = specification;
        }
 
        var result = OptionsTestHelpers.ParseNamingStylePreferences(rule);
        Assert.Equal(accessibilities, result.SymbolSpecifications.SelectMany(x => x.ApplicableAccessibilityList));
    }
 
    [Fact]
    public void TestRequiredModifiersParse()
    {
        var charpRule = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.modifiers_parse.severity"] = "error",
            ["dotnet_naming_rule.modifiers_parse.symbols"] = "modifiers",
            ["dotnet_naming_rule.modifiers_parse.style"] = "pascal_case",
            ["dotnet_naming_symbols.modifiers.required_modifiers"] = "abstract,static",
            ["dotnet_naming_style.pascal_case.capitalization "] = "pascal_case",
        };
        var vbRule = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.modifiers_parse.severity"] = "error",
            ["dotnet_naming_rule.modifiers_parse.symbols"] = "modifiers",
            ["dotnet_naming_rule.modifiers_parse.style"] = "pascal_case",
            ["dotnet_naming_symbols.modifiers.required_modifiers"] = "must_inherit,shared",
            ["dotnet_naming_style.pascal_case.capitalization "] = "pascal_case",
        };
 
        var csharpResult = OptionsTestHelpers.ParseNamingStylePreferences(charpRule);
        var vbResult = OptionsTestHelpers.ParseNamingStylePreferences(vbRule);
 
        Assert.Equal(csharpResult.SymbolSpecifications.SelectMany(x => x.RequiredModifierList.Select(y => y.Modifier)),
                     vbResult.SymbolSpecifications.SelectMany(x => x.RequiredModifierList.Select(y => y.Modifier)));
        Assert.Equal(csharpResult.SymbolSpecifications.SelectMany(x => x.RequiredModifierList.Select(y => y.ModifierKindWrapper)),
                     vbResult.SymbolSpecifications.SelectMany(x => x.RequiredModifierList.Select(y => y.ModifierKindWrapper)));
    }
 
    [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/38513")]
    public void TestPrefixParse()
    {
        var rule = new Dictionary<string, string>()
        {
            ["dotnet_naming_style.pascal_case_and_prefix_style.required_prefix"] = "I",
            ["dotnet_naming_style.pascal_case_and_prefix_style.capitalization"] = "pascal_case",
            ["dotnet_naming_symbols.symbols.applicable_kinds"] = "interface",
            ["dotnet_naming_symbols.symbols.applicable_accessibilities"] = "*",
            ["dotnet_naming_rule.must_be_pascal_cased_and_prefixed.symbols"] = "symbols",
            ["dotnet_naming_rule.must_be_pascal_cased_and_prefixed.style"] = "pascal_case_and_prefix_style",
            ["dotnet_naming_rule.must_be_pascal_cased_and_prefixed.severity"] = "warning",
        };
 
        var result = OptionsTestHelpers.ParseNamingStylePreferences(rule);
        Assert.Single(result.NamingRules);
        var namingRule = result.NamingRules.Single();
        Assert.Single(result.NamingStyles);
        var namingStyle = result.NamingStyles.Single();
        Assert.Single(result.SymbolSpecifications);
        var symbolSpec = result.SymbolSpecifications.Single();
        Assert.Equal(namingStyle.ID, namingRule.NamingStyleID);
        Assert.Equal(symbolSpec.ID, namingRule.SymbolSpecificationID);
        Assert.Equal(ReportDiagnostic.Warn, namingRule.EnforcementLevel);
        Assert.Equal("symbols", symbolSpec.Name);
        var expectedApplicableTypeKindList = new[] { new SymbolKindOrTypeKind(TypeKind.Interface) };
        AssertEx.SetEqual(expectedApplicableTypeKindList, symbolSpec.ApplicableSymbolKindList);
        Assert.Equal("pascal_case_and_prefix_style", namingStyle.Name);
        Assert.Equal("I", namingStyle.Prefix);
        Assert.Equal("", namingStyle.Suffix);
        Assert.Equal("", namingStyle.WordSeparator);
        Assert.Equal(Capitalization.PascalCase, namingStyle.CapitalizationScheme);
    }
 
    [Fact]
    public void TestEditorConfigParseForApplicableSymbolKinds()
    {
        var symbolSpecifications = CreateDefaultSymbolSpecification();
        foreach (var applicableSymbolKind in symbolSpecifications.ApplicableSymbolKindList)
        {
            var editorConfigString = EditorConfigNamingStyleParser.ToEditorConfigString(ImmutableArray.Create(applicableSymbolKind));
            Assert.True(!string.IsNullOrEmpty(editorConfigString));
        }
    }
 
    [Theory]
    [InlineData("a", "b", "a", "public", "public, private")]
    [InlineData("b", "a", "a", "public, private", "public")]
    [InlineData("b", "a", "b", "public", "public, private")]
    [InlineData("a", "b", "b", "public, private", "public")]
    [InlineData("a", "b", "a", "*", "*")]
    [InlineData("b", "a", "a", "*", "*")]
    [InlineData("A", "b", "A", "*", "*")]
    [InlineData("b", "A", "A", "*", "*")]
    [InlineData("a", "B", "a", "*", "*")]
    [InlineData("B", "a", "a", "*", "*")]
    [InlineData("A", "B", "A", "*", "*")]
    [InlineData("B", "A", "A", "*", "*")]
    public void TestOrderedByAccessibilityBeforeName(string firstName, string secondName, string firstNameAfterOrdering, string firstAccessibility, string secondAccessibility)
    {
        var namingStylePreferences = OptionsTestHelpers.ParseNamingStylePreferences(new Dictionary<string, string>()
        {
            [$"dotnet_naming_rule.{firstName}.severity"] = "error",
            [$"dotnet_naming_rule.{firstName}.symbols"] = "first_symbols",
            [$"dotnet_naming_rule.{firstName}.style"] = $"{firstName}_style",
            ["dotnet_naming_symbols.first_symbols.applicable_kinds"] = "method,property",
            ["dotnet_naming_symbols.first_symbols.applicable_accessibilities"] = firstAccessibility,
            [$"dotnet_naming_style.{firstName}_style.capitalization"] = "pascal_case",
            [$"dotnet_naming_style.{secondName}_style.capitalization"] = "camel_case",
            [$"dotnet_naming_rule.{secondName}.severity"] = "error",
            [$"dotnet_naming_rule.{secondName}.symbols"] = "second_symbols",
            [$"dotnet_naming_rule.{secondName}.style"] = $"{secondName}_style",
            ["dotnet_naming_symbols.second_symbols.applicable_kinds"] = "method,property",
            ["dotnet_naming_symbols.second_symbols.applicable_accessibilities"] = secondAccessibility,
        });
 
        var secondNameAfterOrdering = firstNameAfterOrdering == firstName ? secondName : firstName;
        Assert.Equal($"{firstNameAfterOrdering}_style", namingStylePreferences.Rules.NamingRules[0].NamingStyle.Name);
        Assert.Equal($"{secondNameAfterOrdering}_style", namingStylePreferences.Rules.NamingRules[1].NamingStyle.Name);
    }
 
    [Theory]
    [InlineData("a", "b", "a", "static, readonly", "static")]
    [InlineData("b", "a", "a", "static", "static, readonly")]
    [InlineData("b", "a", "b", "static, readonly", "static")]
    [InlineData("a", "b", "b", "static", "static, readonly")]
    [InlineData("a", "b", "a", "", "")]
    [InlineData("b", "a", "a", "", "")]
    [InlineData("A", "b", "A", "", "")]
    [InlineData("b", "A", "A", "", "")]
    [InlineData("a", "B", "a", "", "")]
    [InlineData("B", "a", "a", "", "")]
    [InlineData("A", "B", "A", "", "")]
    [InlineData("B", "A", "A", "", "")]
    public void TestOrderedByModifiersBeforeName(string firstName, string secondName, string firstNameAfterOrdering, string firstModifiers, string secondModifiers)
    {
        var namingStylePreferences = OptionsTestHelpers.ParseNamingStylePreferences(new Dictionary<string, string>()
        {
            [$"dotnet_naming_rule.{firstName}.severity"] = "error",
            [$"dotnet_naming_rule.{firstName}.symbols"] = "first_symbols",
            [$"dotnet_naming_rule.{firstName}.style"] = $"{firstName}_style",
            ["dotnet_naming_symbols.first_symbols.applicable_kinds"] = "method,property",
            ["dotnet_naming_symbols.first_symbols.required_modifiers"] = firstModifiers,
            [$"dotnet_naming_style.{firstName}_style.capitalization"] = "pascal_case",
            [$"dotnet_naming_style.{secondName}_style.capitalization"] = "camel_case",
            [$"dotnet_naming_rule.{secondName}.severity"] = "error",
            [$"dotnet_naming_rule.{secondName}.symbols"] = "second_symbols",
            [$"dotnet_naming_rule.{secondName}.style"] = $"{secondName}_style",
            ["dotnet_naming_symbols.second_symbols.applicable_kinds"] = "method,property",
            ["dotnet_naming_symbols.second_symbols.required_modifiers"] = secondModifiers,
        });
 
        var secondNameAfterOrdering = firstNameAfterOrdering == firstName ? secondName : firstName;
        Assert.Equal($"{firstNameAfterOrdering}_style", namingStylePreferences.Rules.NamingRules[0].NamingStyle.Name);
        Assert.Equal($"{secondNameAfterOrdering}_style", namingStylePreferences.Rules.NamingRules[1].NamingStyle.Name);
    }
 
    [Theory]
    [InlineData("a", "b", "a", "method", "method, property")]
    [InlineData("b", "a", "a", "method, property", "method")]
    [InlineData("b", "a", "b", "method", "method, property")]
    [InlineData("a", "b", "b", "method, property", "method")]
    [InlineData("a", "b", "a", "*", "*")]
    [InlineData("b", "a", "a", "*", "*")]
    [InlineData("A", "b", "A", "*", "*")]
    [InlineData("b", "A", "A", "*", "*")]
    [InlineData("a", "B", "a", "*", "*")]
    [InlineData("B", "a", "a", "*", "*")]
    [InlineData("A", "B", "A", "*", "*")]
    [InlineData("B", "A", "A", "*", "*")]
    public void TestOrderedBySymbolsBeforeName(string firstName, string secondName, string firstNameAfterOrdering, string firstSymbols, string secondSymbols)
    {
        var namingStylePreferences = OptionsTestHelpers.ParseNamingStylePreferences(new Dictionary<string, string>()
        {
            [$"dotnet_naming_rule.{firstName}.severity"] = "error",
            [$"dotnet_naming_rule.{firstName}.symbols"] = "first_symbols",
            [$"dotnet_naming_rule.{firstName}.style"] = $"{firstName}_style",
            ["dotnet_naming_symbols.first_symbols.applicable_kinds"] = firstSymbols,
            [$"dotnet_naming_style.{firstName}_style.capitalization"] = "pascal_case",
            [$"dotnet_naming_style.{secondName}_style.capitalization"] = "camel_case",
            [$"dotnet_naming_rule.{secondName}.severity"] = "error",
            [$"dotnet_naming_rule.{secondName}.symbols"] = "second_symbols",
            [$"dotnet_naming_rule.{secondName}.style"] = $"{secondName}_style",
            ["dotnet_naming_symbols.second_symbols.applicable_kinds"] = secondSymbols,
        });
 
        var secondNameAfterOrdering = firstNameAfterOrdering == firstName ? secondName : firstName;
        Assert.Equal($"{firstNameAfterOrdering}_style", namingStylePreferences.Rules.NamingRules[0].NamingStyle.Name);
        Assert.Equal($"{secondNameAfterOrdering}_style", namingStylePreferences.Rules.NamingRules[1].NamingStyle.Name);
    }
 
    /// <summary>
    /// Two rules with different names but same specification.
    /// </summary>
    [Fact(Skip = "https://github.com/dotnet/roslyn/issues/76381")]
    public void DuplicateRuleKeys()
    {
        var dictionary = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.R1.severity"] = "warning",
            ["dotnet_naming_rule.R1.symbols"] = "SYMBOLS",
            ["dotnet_naming_rule.R1.style"] = "STYLE",
            ["dotnet_naming_rule.R1.priority"] = "0",
 
            ["dotnet_naming_rule.R2.severity"] = "warning",
            ["dotnet_naming_rule.R2.symbols"] = "SYMBOLS",
            ["dotnet_naming_rule.R2.style"] = "STYLE",
            ["dotnet_naming_rule.R2.priority"] = "1",
 
            ["dotnet_naming_symbols.SYMBOLS.applicable_kinds"] = "method",
            ["dotnet_naming_symbols.SYMBOLS.applicable_accessibilities"] = "*",
            ["dotnet_naming_style.STYLE.capitalization"] = "pascal_case",
        };
 
        var result = OptionsTestHelpers.ParseNamingStylePreferences(dictionary);
 
        AssertEx.AssertEqualToleratingWhitespaceDifferences("""
            <NamingPreferencesInfo SerializationVersion="5">
              <SymbolSpecifications>
                <SymbolSpecification ID="0" Name="SYMBOLS">
                  <ApplicableSymbolKindList>
                    <MethodKind>Ordinary</MethodKind>
                  </ApplicableSymbolKindList>
                  <ApplicableAccessibilityList>
                    <AccessibilityKind>NotApplicable</AccessibilityKind>
                    <AccessibilityKind>Public</AccessibilityKind>
                    <AccessibilityKind>Internal</AccessibilityKind>
                    <AccessibilityKind>Private</AccessibilityKind>
                    <AccessibilityKind>Protected</AccessibilityKind>
                    <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
                    <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
                  </ApplicableAccessibilityList>
                  <RequiredModifierList />
                </SymbolSpecification>
                <SymbolSpecification ID="1" Name="SYMBOLS">
                  <ApplicableSymbolKindList>
                    <MethodKind>Ordinary</MethodKind>
                  </ApplicableSymbolKindList>
                  <ApplicableAccessibilityList>
                    <AccessibilityKind>NotApplicable</AccessibilityKind>
                    <AccessibilityKind>Public</AccessibilityKind>
                    <AccessibilityKind>Internal</AccessibilityKind>
                    <AccessibilityKind>Private</AccessibilityKind>
                    <AccessibilityKind>Protected</AccessibilityKind>
                    <AccessibilityKind>ProtectedAndInternal</AccessibilityKind>
                    <AccessibilityKind>ProtectedOrInternal</AccessibilityKind>
                  </ApplicableAccessibilityList>
                  <RequiredModifierList />
                </SymbolSpecification>
              </SymbolSpecifications>
              <NamingStyles>
                <NamingStyle ID="2" Name="STYLE" Prefix="" Suffix="" WordSeparator="" CapitalizationScheme="PascalCase" />
                <NamingStyle ID="3" Name="STYLE" Prefix="" Suffix="" WordSeparator="" CapitalizationScheme="PascalCase" />
              </NamingStyles>
              <NamingRules>
                <SerializableNamingRule SymbolSpecificationID="1" NamingStyleID="3" EnforcementLevel="Warning" />
                <SerializableNamingRule SymbolSpecificationID="0" NamingStyleID="2" EnforcementLevel="Warning" />
              </NamingRules>
            </NamingPreferencesInfo>
            """,
            result.Inspect());
    }
 
    [Fact]
    public void Priorities()
    {
        var dictionary = new Dictionary<string, string>()
        {
            ["dotnet_naming_rule.R1.severity"] = "warning",
            ["dotnet_naming_rule.R1.symbols"] = "SYMBOLS1",
            ["dotnet_naming_rule.R1.style"] = "STYLE1",
 
            ["dotnet_naming_rule.R2.severity"] = "error",
            ["dotnet_naming_rule.R2.symbols"] = "SYMBOLS2",
            ["dotnet_naming_rule.R2.style"] = "STYLE2",
 
            ["dotnet_naming_symbols.SYMBOLS1.applicable_kinds"] = "method",
            ["dotnet_naming_symbols.SYMBOLS1.applicable_accessibilities"] = "*",
            ["dotnet_naming_style.STYLE1.capitalization"] = "pascal_case",
 
            ["dotnet_naming_symbols.SYMBOLS2.applicable_kinds"] = "method, field",
            ["dotnet_naming_symbols.SYMBOLS2.applicable_accessibilities"] = "*",
            ["dotnet_naming_style.STYLE2.capitalization"] = "pascal_case",
        };
 
        AssertEx.AssertEqualToleratingWhitespaceDifferences("""
            <NamingPreferencesInfo SerializationVersion="5">
              <NamingRules>
                <SerializableNamingRule SymbolSpecificationID="0" NamingStyleID="1" EnforcementLevel="Warning" />
                <SerializableNamingRule SymbolSpecificationID="2" NamingStyleID="3" EnforcementLevel="Error" />
              </NamingRules>
            </NamingPreferencesInfo>
            """,
            OptionsTestHelpers.ParseNamingStylePreferences(dictionary).Inspect(excludeNodes: ["SymbolSpecifications", "NamingStyles"]));
 
        // adding priorities reverses the order - R2 (P0) is now ordered before R1 (P1):
        dictionary.Add("dotnet_naming_rule.R2.priority", "0");
        dictionary.Add("dotnet_naming_rule.R1.priority", "1");
 
        AssertEx.AssertEqualToleratingWhitespaceDifferences("""
            <NamingPreferencesInfo SerializationVersion="5">
              <NamingRules>
                <SerializableNamingRule SymbolSpecificationID="0" NamingStyleID="1" EnforcementLevel="Error" />
                <SerializableNamingRule SymbolSpecificationID="2" NamingStyleID="3" EnforcementLevel="Warning" />
              </NamingRules>
            </NamingPreferencesInfo>
            """,
            OptionsTestHelpers.ParseNamingStylePreferences(dictionary).Inspect(excludeNodes: ["SymbolSpecifications", "NamingStyles"]));
    }
}