|
3 | 3 | // See the LICENSE file in the project root for more information. |
4 | 4 |
|
5 | 5 | using System.Collections.Generic; |
| 6 | +using System.IO; |
6 | 7 | using System.Linq; |
| 8 | +using System.Threading; |
| 9 | +using Microsoft.CodeAnalysis.CodeStyle; |
| 10 | +using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles; |
7 | 11 | using Microsoft.CodeAnalysis.Formatting; |
8 | 12 | using Microsoft.CodeAnalysis.Host; |
| 13 | +using Microsoft.CodeAnalysis.Shared.Extensions; |
9 | 14 | using Microsoft.CodeAnalysis.Test.Utilities; |
10 | | -using Roslyn.Utilities; |
| 15 | +using Microsoft.CodeAnalysis.UnitTests; |
| 16 | +using Roslyn.Test.Utilities; |
11 | 17 | using Xunit; |
12 | 18 |
|
13 | 19 | namespace Microsoft.CodeAnalysis.Options.UnitTests; |
@@ -95,6 +101,154 @@ void AssertOptionValue(IOption2 option, string language, string expectedValue) |
95 | 101 | } |
96 | 102 | } |
97 | 103 |
|
| 104 | + [Fact] |
| 105 | + [WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2297536")] |
| 106 | + public void FlowsNamingStylePreferencesToWorkspace() |
| 107 | + { |
| 108 | + using var workspace = CreateWorkspace(); |
| 109 | + |
| 110 | + var testProjectWithoutConfig = new TestHostProject(workspace, "proj_without_config", LanguageNames.CSharp); |
| 111 | + |
| 112 | + testProjectWithoutConfig.AddDocument(new TestHostDocument(""" |
| 113 | + class MyClass1; |
| 114 | + """, |
| 115 | + filePath: Path.Combine(TempRoot.Root, "proj_without_config", "test.cs"))); |
| 116 | + |
| 117 | + var testProjectWithConfig = new TestHostProject(workspace, "proj_with_config", LanguageNames.CSharp); |
| 118 | + |
| 119 | + // explicitly specified style should override style specified in the fallback: |
| 120 | + testProjectWithConfig.AddAnalyzerConfigDocument(new TestHostDocument( |
| 121 | + """ |
| 122 | + [*.cs] |
| 123 | + dotnet_naming_rule.rule1.severity = warning |
| 124 | + dotnet_naming_rule.rule1.symbols = symbols1 |
| 125 | + dotnet_naming_rule.rule1.style = style1 |
| 126 | +
|
| 127 | + dotnet_naming_symbols.symbols1.applicable_kinds = class |
| 128 | + dotnet_naming_symbols.symbols1.applicable_accessibilities = * |
| 129 | + dotnet_naming_style.style1.capitalization = camel_case |
| 130 | + """, |
| 131 | + filePath: Path.Combine(TempRoot.Root, "proj_with_config", ".editorconfig"))); |
| 132 | + |
| 133 | + testProjectWithConfig.AddDocument(new TestHostDocument(""" |
| 134 | + class MyClass2; |
| 135 | + """, |
| 136 | + filePath: Path.Combine(TempRoot.Root, "proj_with_config", "test.cs"))); |
| 137 | + |
| 138 | + workspace.AddTestProject(testProjectWithoutConfig); |
| 139 | + workspace.AddTestProject(testProjectWithConfig); |
| 140 | + |
| 141 | + var globalOptions = workspace.GetService<IGlobalOptionService>(); |
| 142 | + |
| 143 | + var hostPeferences = OptionsTestHelpers.CreateNamingStylePreferences( |
| 144 | + ([MethodKind.Ordinary], Capitalization.PascalCase, ReportDiagnostic.Error), |
| 145 | + ([MethodKind.Ordinary, SymbolKind.Field], Capitalization.PascalCase, ReportDiagnostic.Error)); |
| 146 | + |
| 147 | + globalOptions.SetGlobalOption(NamingStyleOptions.NamingPreferences, LanguageNames.CSharp, hostPeferences); |
| 148 | + |
| 149 | + Assert.True(workspace.CurrentSolution.FallbackAnalyzerOptions.TryGetValue(LanguageNames.CSharp, out var fallbackOptions)); |
| 150 | + |
| 151 | + // Note: rules are ordered but symbol and naming style specifications are not. |
| 152 | + AssertEx.Equal( |
| 153 | + hostPeferences.Rules.NamingRules.Select(r => r.Inspect()), |
| 154 | + fallbackOptions.GetNamingStylePreferences().Rules.NamingRules.Select(r => r.Inspect())); |
| 155 | + |
| 156 | + var projectWithConfig = workspace.CurrentSolution.GetRequiredProject(testProjectWithConfig.Id); |
| 157 | + var treeWithConfig = projectWithConfig.Documents.Single().GetSyntaxTreeSynchronously(CancellationToken.None); |
| 158 | + Assert.NotNull(treeWithConfig); |
| 159 | + var documentOptions = projectWithConfig.HostAnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(treeWithConfig); |
| 160 | + |
| 161 | + Assert.True(documentOptions.TryGetEditorConfigOption<NamingStylePreferences>(NamingStyleOptions.NamingPreferences, out var documentPreferences)); |
| 162 | + Assert.NotNull(documentPreferences); |
| 163 | + |
| 164 | + // Only naming styles specified in the editorconfig are present. |
| 165 | + // Host preferences are ignored. This behavior is consistent with VS 16.11. |
| 166 | + AssertEx.EqualOrDiff(""" |
| 167 | + <NamingPreferencesInfo SerializationVersion="5"> |
| 168 | + <SymbolSpecifications> |
| 169 | + <SymbolSpecification ID="0" Name="symbols1"> |
| 170 | + <ApplicableSymbolKindList> |
| 171 | + <TypeKind>Class</TypeKind> |
| 172 | + </ApplicableSymbolKindList> |
| 173 | + <ApplicableAccessibilityList> |
| 174 | + <AccessibilityKind>NotApplicable</AccessibilityKind> |
| 175 | + <AccessibilityKind>Public</AccessibilityKind> |
| 176 | + <AccessibilityKind>Internal</AccessibilityKind> |
| 177 | + <AccessibilityKind>Private</AccessibilityKind> |
| 178 | + <AccessibilityKind>Protected</AccessibilityKind> |
| 179 | + <AccessibilityKind>ProtectedAndInternal</AccessibilityKind> |
| 180 | + <AccessibilityKind>ProtectedOrInternal</AccessibilityKind> |
| 181 | + </ApplicableAccessibilityList> |
| 182 | + <RequiredModifierList /> |
| 183 | + </SymbolSpecification> |
| 184 | + </SymbolSpecifications> |
| 185 | + <NamingStyles> |
| 186 | + <NamingStyle ID="1" Name="style1" Prefix="" Suffix="" WordSeparator="" CapitalizationScheme="CamelCase" /> |
| 187 | + </NamingStyles> |
| 188 | + <NamingRules> |
| 189 | + <SerializableNamingRule SymbolSpecificationID="0" NamingStyleID="1" EnforcementLevel="Warning" /> |
| 190 | + </NamingRules> |
| 191 | + </NamingPreferencesInfo> |
| 192 | + """, |
| 193 | + documentPreferences.Inspect()); |
| 194 | + |
| 195 | + var projectWithoutConfig = workspace.CurrentSolution.GetRequiredProject(testProjectWithoutConfig.Id); |
| 196 | + var treeWithoutConfig = projectWithoutConfig.Documents.Single().GetSyntaxTreeSynchronously(CancellationToken.None); |
| 197 | + Assert.NotNull(treeWithoutConfig); |
| 198 | + documentOptions = projectWithoutConfig.HostAnalyzerOptions.AnalyzerConfigOptionsProvider.GetOptions(treeWithoutConfig); |
| 199 | + |
| 200 | + Assert.True(documentOptions.TryGetEditorConfigOption(NamingStyleOptions.NamingPreferences, out documentPreferences)); |
| 201 | + Assert.NotNull(documentPreferences); |
| 202 | + |
| 203 | + // Host preferences: |
| 204 | + AssertEx.EqualOrDiff(""" |
| 205 | + <NamingPreferencesInfo SerializationVersion="5"> |
| 206 | + <SymbolSpecifications> |
| 207 | + <SymbolSpecification ID="0" Name="symbols1"> |
| 208 | + <ApplicableSymbolKindList> |
| 209 | + <MethodKind>Ordinary</MethodKind> |
| 210 | + <SymbolKind>Field</SymbolKind> |
| 211 | + </ApplicableSymbolKindList> |
| 212 | + <ApplicableAccessibilityList> |
| 213 | + <AccessibilityKind>NotApplicable</AccessibilityKind> |
| 214 | + <AccessibilityKind>Public</AccessibilityKind> |
| 215 | + <AccessibilityKind>Internal</AccessibilityKind> |
| 216 | + <AccessibilityKind>Private</AccessibilityKind> |
| 217 | + <AccessibilityKind>Protected</AccessibilityKind> |
| 218 | + <AccessibilityKind>ProtectedAndInternal</AccessibilityKind> |
| 219 | + <AccessibilityKind>ProtectedOrInternal</AccessibilityKind> |
| 220 | + </ApplicableAccessibilityList> |
| 221 | + <RequiredModifierList /> |
| 222 | + </SymbolSpecification> |
| 223 | + <SymbolSpecification ID="1" Name="symbols0"> |
| 224 | + <ApplicableSymbolKindList> |
| 225 | + <MethodKind>Ordinary</MethodKind> |
| 226 | + </ApplicableSymbolKindList> |
| 227 | + <ApplicableAccessibilityList> |
| 228 | + <AccessibilityKind>NotApplicable</AccessibilityKind> |
| 229 | + <AccessibilityKind>Public</AccessibilityKind> |
| 230 | + <AccessibilityKind>Internal</AccessibilityKind> |
| 231 | + <AccessibilityKind>Private</AccessibilityKind> |
| 232 | + <AccessibilityKind>Protected</AccessibilityKind> |
| 233 | + <AccessibilityKind>ProtectedAndInternal</AccessibilityKind> |
| 234 | + <AccessibilityKind>ProtectedOrInternal</AccessibilityKind> |
| 235 | + </ApplicableAccessibilityList> |
| 236 | + <RequiredModifierList /> |
| 237 | + </SymbolSpecification> |
| 238 | + </SymbolSpecifications> |
| 239 | + <NamingStyles> |
| 240 | + <NamingStyle ID="2" Name="style1" Prefix="" Suffix="" WordSeparator="" CapitalizationScheme="PascalCase" /> |
| 241 | + <NamingStyle ID="3" Name="style0" Prefix="" Suffix="" WordSeparator="" CapitalizationScheme="PascalCase" /> |
| 242 | + </NamingStyles> |
| 243 | + <NamingRules> |
| 244 | + <SerializableNamingRule SymbolSpecificationID="1" NamingStyleID="3" EnforcementLevel="Error" /> |
| 245 | + <SerializableNamingRule SymbolSpecificationID="0" NamingStyleID="2" EnforcementLevel="Error" /> |
| 246 | + </NamingRules> |
| 247 | + </NamingPreferencesInfo> |
| 248 | + """, |
| 249 | + documentPreferences.Inspect()); |
| 250 | + } |
| 251 | + |
98 | 252 | [Fact] |
99 | 253 | public void IgnoresNonEditorConfigOptions() |
100 | 254 | { |
|
0 commit comments