Skip to content

Commit d41464e

Browse files
committed
Merge 'release/dev17.13' into main
2 parents 0f804a3 + 5bcd795 commit d41464e

File tree

18 files changed

+525
-123
lines changed

18 files changed

+525
-123
lines changed

azure-pipelines-official.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,11 @@ extends:
121121
displayName: Build and Test
122122

123123
jobs:
124-
- ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/release/dev17.12') }}:
124+
- ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/release/dev17.13') }}:
125125
- template: /eng/common/templates-official/job/onelocbuild.yml@self
126126
parameters:
127127
MirrorRepo: roslyn
128-
MirrorBranch: release/dev17.12
128+
MirrorBranch: release/dev17.13
129129
LclSource: lclFilesfromPackage
130130
LclPackageId: 'LCL-JUNO-PROD-ROSLYN'
131131

src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/EditorConfigNamingStyleParserTests.cs

Lines changed: 155 additions & 39 deletions
Large diffs are not rendered by default.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Collections.Immutable;
8+
using System.Linq;
9+
using System.Xml.Linq;
10+
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
11+
using Microsoft.CodeAnalysis.NamingStyles;
12+
13+
namespace Microsoft.CodeAnalysis.Test.Utilities;
14+
15+
internal static class NamingStyleTestUtilities
16+
{
17+
public static string Inspect(this NamingRule rule)
18+
=> $"{rule.NamingStyle.Inspect()} {rule.SymbolSpecification.Inspect()} {rule.EnforcementLevel}";
19+
20+
public static string Inspect(this NamingStyle style)
21+
=> $"{style.Name} prefix='{style.Prefix}' suffix='{style.Suffix}' separator='{style.WordSeparator}'";
22+
23+
public static string Inspect(this SymbolSpecification symbol)
24+
=> $"{symbol.Name} {Inspect(symbol.ApplicableSymbolKindList)} {Inspect(symbol.ApplicableAccessibilityList)} {Inspect(symbol.RequiredModifierList)}";
25+
26+
public static string Inspect<T>(ImmutableArray<T> items) where T : notnull
27+
=> string.Join(",", items.Select(item => item.ToString()));
28+
29+
public static string Inspect(this NamingStylePreferences preferences, string[]? excludeNodes = null)
30+
{
31+
var xml = preferences.CreateXElement();
32+
33+
// filter out insignificant elements:
34+
var elementsToRemove = new List<XElement>();
35+
foreach (var element in xml.DescendantsAndSelf())
36+
{
37+
if (excludeNodes != null && excludeNodes.Contains(element.Name.LocalName))
38+
{
39+
elementsToRemove.Add(element);
40+
}
41+
}
42+
43+
foreach (var element in elementsToRemove)
44+
{
45+
element.Remove();
46+
}
47+
48+
// replaces GUIDs with unique deterministic numbers:
49+
var ordinal = 0;
50+
var guidMap = new Dictionary<Guid, int>();
51+
foreach (var element in xml.DescendantsAndSelf())
52+
{
53+
foreach (var attribute in element.Attributes())
54+
{
55+
if (Guid.TryParse(attribute.Value, out var guid))
56+
{
57+
if (!guidMap.TryGetValue(guid, out var existingOrdinal))
58+
{
59+
existingOrdinal = ordinal++;
60+
guidMap.Add(guid, existingOrdinal);
61+
}
62+
63+
attribute.Value = existingOrdinal.ToString();
64+
}
65+
}
66+
}
67+
68+
return xml.ToString();
69+
}
70+
}

src/LanguageServer/Protocol/Features/Options/SolutionAnalyzerConfigOptionsUpdater.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
using System.Composition;
99
using System.Diagnostics;
1010
using System.Linq;
11-
using System.Threading;
1211
using Microsoft.CodeAnalysis;
1312
using Microsoft.CodeAnalysis.Diagnostics;
13+
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
1414
using Microsoft.CodeAnalysis.ErrorReporting;
1515
using Microsoft.CodeAnalysis.Host;
1616
using Microsoft.CodeAnalysis.Host.Mef;
@@ -86,8 +86,21 @@ Solution UpdateOptions(Solution oldSolution)
8686

8787
// update changed values:
8888
var configName = key.Option.Definition.ConfigName;
89-
var configValue = key.Option.Definition.Serializer.Serialize(value);
90-
lazyBuilder[configName] = configValue;
89+
if (value is NamingStylePreferences preferences)
90+
{
91+
NamingStylePreferencesEditorConfigSerializer.WriteNamingStylePreferencesToEditorConfig(
92+
preferences.SymbolSpecifications,
93+
preferences.NamingStyles,
94+
preferences.NamingRules,
95+
language,
96+
entryWriter: (name, value) => lazyBuilder[name] = value,
97+
triviaWriter: null,
98+
setPrioritiesToPreserveOrder: true);
99+
}
100+
else
101+
{
102+
lazyBuilder[configName] = key.Option.Definition.Serializer.Serialize(value);
103+
}
91104
}
92105

93106
if (lazyBuilder != null)

src/LanguageServer/ProtocolUnitTests/Options/SolutionAnalyzerConfigOptionsUpdaterTests.cs

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@
33
// See the LICENSE file in the project root for more information.
44

55
using System.Collections.Generic;
6+
using System.IO;
67
using System.Linq;
8+
using System.Threading;
9+
using Microsoft.CodeAnalysis.CodeStyle;
10+
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
711
using Microsoft.CodeAnalysis.Formatting;
812
using Microsoft.CodeAnalysis.Host;
13+
using Microsoft.CodeAnalysis.Shared.Extensions;
914
using Microsoft.CodeAnalysis.Test.Utilities;
10-
using Roslyn.Utilities;
15+
using Microsoft.CodeAnalysis.UnitTests;
16+
using Roslyn.Test.Utilities;
1117
using Xunit;
1218

1319
namespace Microsoft.CodeAnalysis.Options.UnitTests;
@@ -95,6 +101,154 @@ void AssertOptionValue(IOption2 option, string language, string expectedValue)
95101
}
96102
}
97103

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+
98252
[Fact]
99253
public void IgnoresNonEditorConfigOptions()
100254
{

src/VisualStudio/Core/Test.Next/Options/VisualStudioOptionStorageTests.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,18 @@ public void LanguageSpecificOptionsHaveCorrectPrefix(string configName)
107107
return;
108108
}
109109

110-
if (!info.Option.Definition.IsEditorConfigOption)
110+
// TODO: https://github.com/dotnet/roslyn/issues/65787
111+
if (info.Option.Name is
112+
"csharp_format_on_return" or
113+
"csharp_format_on_typing" or
114+
"csharp_format_on_semicolon" or
115+
"csharp_format_on_close_brace" or
116+
"csharp_enable_inlay_hints_for_types" or
117+
"csharp_enable_inlay_hints_for_implicit_variable_types" or
118+
"csharp_enable_inlay_hints_for_lambda_parameter_types" or
119+
"csharp_enable_inlay_hints_for_implicit_object_creation" or
120+
"csharp_enable_inlay_hints_for_collection_expressions")
111121
{
112-
// TODO: remove condition once all options have config name https://github.com/dotnet/roslyn/issues/65787
113122
return;
114123
}
115124

src/Workspaces/CoreTestUtilities/Options/OptionsTestHelpers.cs

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using System.Collections.Immutable;
1010
using Microsoft.CodeAnalysis.CodeStyle;
1111
using Microsoft.CodeAnalysis.CSharp.Formatting;
12+
using Microsoft.CodeAnalysis.Diagnostics;
1213
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
1314
using Microsoft.CodeAnalysis.Formatting;
1415
using Microsoft.CodeAnalysis.NamingStyles;
@@ -147,33 +148,53 @@ private static object GetDifferentEnumValue(Type type, object defaultValue)
147148
}
148149

149150
public static NamingStylePreferences GetNonDefaultNamingStylePreference()
151+
=> CreateNamingStylePreferences(([TypeKind.Class], Capitalization.PascalCase, ReportDiagnostic.Error));
152+
153+
public static NamingStylePreferences CreateNamingStylePreferences(
154+
params (SymbolSpecification.SymbolKindOrTypeKind[], Capitalization capitalization, ReportDiagnostic severity)[] rules)
150155
{
151-
var symbolSpecification = new SymbolSpecification(
152-
Guid.NewGuid(),
153-
"Name",
154-
ImmutableArray.Create(new SymbolSpecification.SymbolKindOrTypeKind(TypeKind.Class)),
155-
accessibilityList: default,
156-
modifiers: default);
157-
158-
var namingStyle = new NamingStyle(
159-
Guid.NewGuid(),
160-
capitalizationScheme: Capitalization.PascalCase,
161-
name: "Name",
162-
prefix: "",
163-
suffix: "",
164-
wordSeparator: "");
165-
166-
var namingRule = new SerializableNamingRule()
156+
var symbolSpecifications = new List<SymbolSpecification>();
157+
var namingStyles = new List<NamingStyle>();
158+
var namingRules = new List<SerializableNamingRule>();
159+
160+
foreach (var (kinds, capitalization, severity) in rules)
167161
{
168-
SymbolSpecificationID = symbolSpecification.ID,
169-
NamingStyleID = namingStyle.ID,
170-
EnforcementLevel = ReportDiagnostic.Error
171-
};
162+
var id = namingRules.Count;
163+
164+
var symbolSpecification = new SymbolSpecification(
165+
Guid.NewGuid(),
166+
name: $"symbols{id}",
167+
[.. kinds],
168+
accessibilityList: default,
169+
modifiers: default);
170+
171+
symbolSpecifications.Add(symbolSpecification);
172+
173+
var namingStyle = new NamingStyle(
174+
Guid.NewGuid(),
175+
capitalizationScheme: capitalization,
176+
name: $"style{id}",
177+
prefix: "",
178+
suffix: "",
179+
wordSeparator: "");
180+
181+
namingStyles.Add(namingStyle);
182+
183+
namingRules.Add(new SerializableNamingRule()
184+
{
185+
SymbolSpecificationID = symbolSpecification.ID,
186+
NamingStyleID = namingStyle.ID,
187+
EnforcementLevel = severity
188+
});
189+
}
172190

173191
return new NamingStylePreferences(
174-
ImmutableArray.Create(symbolSpecification),
175-
ImmutableArray.Create(namingStyle),
176-
ImmutableArray.Create(namingRule));
192+
[.. symbolSpecifications],
193+
[.. namingStyles],
194+
[.. namingRules]);
177195
}
196+
197+
public static NamingStylePreferences ParseNamingStylePreferences(Dictionary<string, string> options)
198+
=> EditorConfigNamingStyleParser.ParseDictionary(new DictionaryAnalyzerConfigOptions(options.ToImmutableDictionary()));
178199
}
179200
}

0 commit comments

Comments
 (0)