Skip to content

Commit

Permalink
Merge pull request dotnet#16510 from jmarolf/bugfix/naming-styles-edi…
Browse files Browse the repository at this point in the history
…torconfig-override

Append editorconfig namingstyle options to the top of the workspace o…
  • Loading branch information
jmarolf authored Feb 28, 2017
2 parents ab6cfc4 + 2f07e1d commit d5b1768
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public DocumentOptions(ICodingConventionsSnapshot codingConventionSnapshot, IErr
_errorLogger = errorLogger;
}

public bool TryGetDocumentOption(Document document, OptionKey option, out object value)
public bool TryGetDocumentOption(Document document, OptionKey option, OptionSet underlyingOptions, out object value)
{
var editorConfigPersistence = option.Option.StorageLocations.OfType<EditorConfigStorageLocation>().SingleOrDefault();
if (editorConfigPersistence == null)
Expand All @@ -32,7 +32,8 @@ public bool TryGetDocumentOption(Document document, OptionKey option, out object
var allRawConventions = _codingConventionSnapshot.AllRawConventions;
try
{
return editorConfigPersistence.TryParseReadonlyDictionary(allRawConventions, option.Option.Type, out value);
var underlyingOption = underlyingOptions.GetOption(option);
return editorConfigPersistence.TryGetOption(underlyingOption, allRawConventions, option.Option.Type, out value);
}
catch (Exception ex)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

namespace Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles
{
internal static class NamingStylePreferencesExtensions
{
public static NamingStylePreferences PrependNamingStylePreferences(this NamingStylePreferences original, NamingStylePreferences newPreferences)
{
var symbolSpecifications = original.SymbolSpecifications.InsertRange(0, newPreferences.SymbolSpecifications);
var namingStyles = original.NamingStyles.InsertRange(0, newPreferences.NamingStyles);
var namingRules = original.NamingRules.InsertRange(0, newPreferences.NamingRules);
return new NamingStylePreferences(symbolSpecifications, namingStyles, namingRules);
}
}
}
35 changes: 23 additions & 12 deletions src/Workspaces/Core/Portable/Options/EditorConfigStorageLocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,29 +41,40 @@ internal sealed class EditorConfigStorageLocation : OptionStorageLocation
}
};

private Func<IReadOnlyDictionary<string, object>, Type, (object result, bool succeeded)> _tryParseDictionary;
private Func<object, IReadOnlyDictionary<string, object>, Type, (object result, bool succeeded)> _tryParseDictionary;

private static Func<IReadOnlyDictionary<string, object>, Type, (object result, bool succeeded)> _cachedTryParseDictionary = (dictionary, type) =>
private static Func<object, IReadOnlyDictionary<string, object>, Type, (object result, bool succeeded)> _cachedTryParseDictionary = (underlyingOption, dictionary, type) =>
{
if (type == typeof(NamingStylePreferences))
{
var result = EditorConfigNamingStyleParser.GetNamingStylesFromDictionary(dictionary);
if (!result.NamingRules.Any() &&
!result.NamingStyles.Any() &&
!result.SymbolSpecifications.Any())
var editorconfigNamingStylePreferences = EditorConfigNamingStyleParser.GetNamingStylesFromDictionary(dictionary);

if (!editorconfigNamingStylePreferences.NamingRules.Any() &&
!editorconfigNamingStylePreferences.NamingStyles.Any() &&
!editorconfigNamingStylePreferences.SymbolSpecifications.Any())
{
// We were not able to parse any rules from editorconfig, tell the caller that the parse failed
return (result: editorconfigNamingStylePreferences, succeeded: false);
}

var workspaceNamingStylePreferences = underlyingOption as NamingStylePreferences;
if (workspaceNamingStylePreferences != null)
{
return (result: result, succeeded: false);
// We parsed naming styles from editorconfig, append them to our existing styles
var combinedNamingStylePreferences = workspaceNamingStylePreferences.PrependNamingStylePreferences(editorconfigNamingStylePreferences);
return (result: (object)combinedNamingStylePreferences, succeeded: true);
}

return (result: result, succeeded: true);
// no existing naming styles were passed so just return the set of styles that were parsed from editorconfig
return (result: editorconfigNamingStylePreferences, succeeded: true);
}
else
{
throw new NotSupportedException(WorkspacesResources.Option_0_has_an_unsupported_type_to_use_with_1_You_should_specify_a_parsing_function);
}
};

public bool TryParseReadonlyDictionary(IReadOnlyDictionary<string, object> allRawConventions, Type type, out object result)
public bool TryGetOption(object underlyingOption, IReadOnlyDictionary<string, object> allRawConventions, Type type, out object result)
{
if (_parseValue != null && KeyName != null)
{
Expand All @@ -75,7 +86,7 @@ public bool TryParseReadonlyDictionary(IReadOnlyDictionary<string, object> allRa
}
else if (_tryParseDictionary != null)
{
var tuple = _tryParseDictionary(allRawConventions, type);
var tuple = _tryParseDictionary(underlyingOption, allRawConventions, type);
result = tuple.result;
return tuple.succeeded;
}
Expand Down Expand Up @@ -104,10 +115,10 @@ public EditorConfigStorageLocation()
_tryParseDictionary = _cachedTryParseDictionary;
}

public EditorConfigStorageLocation(Func<IReadOnlyDictionary<string, object>, (object result, bool succeeded)> tryParseDictionary)
public EditorConfigStorageLocation(Func<object, IReadOnlyDictionary<string, object>, (object result, bool succeeded)> tryParseDictionary)
{
// If we're explicitly given a parsing function we can throw away the type when parsing
_tryParseDictionary = (dictionary, type) => tryParseDictionary(dictionary);
_tryParseDictionary = (underlyingOption, dictionary, type) => tryParseDictionary(underlyingOption, dictionary);
}
}
}
2 changes: 1 addition & 1 deletion src/Workspaces/Core/Portable/Options/IDocumentOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ namespace Microsoft.CodeAnalysis.Options
/// </summary>
interface IDocumentOptions
{
bool TryGetDocumentOption(Document document, OptionKey option, out object value);
bool TryGetDocumentOption(Document document, OptionKey option, OptionSet underlyingOptions, out object value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public override object GetOption(OptionKey optionKey)

foreach (var documentOptionSource in _documentOptions)
{
if (documentOptionSource.TryGetDocumentOption(_document, optionKey, out value))
if (documentOptionSource.TryGetDocumentOption(_document, optionKey, _underlyingOptions, out value))
{
// Cache and return
lock (_gate)
Expand Down
1 change: 1 addition & 0 deletions src/Workspaces/Core/Portable/Workspaces.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,7 @@
<Compile Include="FindSymbols\SyntaxTree\SyntaxTreeIndex.cs" />
<Compile Include="FindSymbols\SyntaxTree\SyntaxTreeIndex.DeclarationInfo.cs" />
<Compile Include="FindSymbols\SyntaxTree\SyntaxTreeIndex.IdentifierInfo.cs" />
<Compile Include="NamingStyles\Serialization\NamingStylePreferencesExtensions.cs" />
<Compile Include="PatternMatching\PatternMatch.cs" />
<Compile Include="PatternMatching\PatternMatcher.cs" />
<Compile Include="PatternMatching\PatternMatcher.Segment.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,87 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles;
using Microsoft.CodeAnalysis.NamingStyles;
using Microsoft.CodeAnalysis.Options;
using Xunit;
using static Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles.EditorConfigNamingStyleParser;

namespace Microsoft.CodeAnalysis.UnitTests.EditorConfig.StorageLocation
{
public class EditorConfigStorageLocationTests
{
[Fact]
public static void TestEmptyDictionaryReturnFalse()
public static void TestEmptyDictionaryReturnNoNamingStylePreferencesObjectReturnsFalse()
{
var editorConfigStorageLocation = new EditorConfigStorageLocation();
var result = editorConfigStorageLocation.TryParseReadonlyDictionary(new Dictionary<string, object>(), typeof(NamingStylePreferences), out var @object);
var result = editorConfigStorageLocation.TryGetOption(new object(), new Dictionary<string, object>(), typeof(NamingStylePreferences), out var @object);
Assert.False(result, "Expected TryParseReadonlyDictionary to return 'false' for empty dictionary");
}

[Fact]
public static void TestEmptyDictionaryDefaultNamingStylePreferencesObjectReturnsFalse()
{
var editorConfigStorageLocation = new EditorConfigStorageLocation();
var existingNamingStylePreferences = new NamingStylePreferences(
ImmutableArray.Create<SymbolSpecification>(),
ImmutableArray.Create<NamingStyle>(),
ImmutableArray.Create<SerializableNamingRule>());

var result = editorConfigStorageLocation.TryGetOption(
existingNamingStylePreferences,
new Dictionary<string, object>(),
typeof(NamingStylePreferences),
out var @object);

Assert.False(result, "Expected TryParseReadonlyDictionary to return 'false' for empty dictionary");
}

[Fact]
public static void TestNonEmptyDictionaryReturnsTrue()
{
var initialDictionary = new Dictionary<string, object>()
{
["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 existingNamingStylePreferences = ParseDictionary(initialDictionary);

var editorConfigStorageLocation = new EditorConfigStorageLocation();
var newDictionary = new Dictionary<string, object>()
{
["dotnet_naming_rule.methods_and_properties_must_be_pascal_case.severity"] = "error",
["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 = editorConfigStorageLocation.TryGetOption(
existingNamingStylePreferences,
newDictionary,
typeof(NamingStylePreferences),
out var combinedNamingStyles);

Assert.True(result, "Expected non-empty dictionary to return true");
var isNamingStylePreferencesObject = combinedNamingStyles is NamingStylePreferences;
Assert.True(isNamingStylePreferencesObject, $"Expected returned object to be of type '{nameof(NamingStylePreferences)}'");
Assert.Equal(DiagnosticSeverity.Error, ((NamingStylePreferences)combinedNamingStyles).Rules.NamingRules[0].EnforcementLevel);
}

[Fact]
public static void TestObjectTypeThrowsNotSupportedException()
{
var editorConfigStorageLocation = new EditorConfigStorageLocation();
Assert.Throws<NotSupportedException>(() =>
{
editorConfigStorageLocation.TryParseReadonlyDictionary(new Dictionary<string, object>(), typeof(object), out var @object);
editorConfigStorageLocation.TryGetOption(new object(), new Dictionary<string, object>(), typeof(object), out var @object);
});
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Workspaces/CoreTest/ServicesTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@
<ItemGroup>
<Compile Include="CodeStyle\EditorConfigCodeStyleParserTests.cs" />
<Compile Include="DependentTypeFinderTests.cs" />
<Compile Include="EditorConfigStorageLocation\EditorConfigStorageLocationTests.cs" />
<Compile Include="Differencing\LongestCommonSubsequenceTests.cs" />
<Compile Include="Editting\SyntaxEditorTests.cs" />
<Compile Include="Execution\Extensions.cs" />
<Compile Include="EditorConfigStorageLocation\EditorConfigStorageLocationTests.cs" />
<Compile Include="Execution\SnapshotSerializationTestBase.cs" />
<Compile Include="Execution\SnapshotSerializationTests.cs" />
<Compile Include="ExtensionOrdererTests.cs" />
Expand Down

0 comments on commit d5b1768

Please sign in to comment.