Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

editor config naming style #15065

Merged

Conversation

jmarolf
Copy link
Contributor

@jmarolf jmarolf commented Nov 7, 2016

@dotnet/roslyn-ide @jasonmalinowski
Adds the following options

Naming Style Options

editorconfig name possible values
dotnet_naming_rule.<naming rule title>.severity none , suggestion , warning , error
dotnet_naming_rule.<naming rule title>.symbols <naming symbols title>
dotnet_naming_rule.<naming rule title>.style <naming stlye title>
dotnet_naming_symbols.<naming symbols title>.applicable_kinds class,struct,interface,enum:property,method,field,event,namespace,delegate,type_parameter
dotnet_naming_symbols.<naming symbols title>.applicable_accessibilities public,internal,private,protected,protected_internal
dotnet_naming_symbols.<naming symbols title>.required_modifiers abstract,async,const,readonly,static
dotnet_naming_style.<naming style title>.required_prefix string
dotnet_naming_style.<naming style title>.required_suffix string
dotnet_naming_style.<naming style title>.word_separator string
dotnet_naming_style.<naming style title>.capitalization pascal_case , camel_case , first_word_upper , all_upper , all_lower

Naming Style Examples

# Example: Pascal Casing
[*.{cs,vb}]
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
# Example: async methods end in Async
[*.{cs,vb}]
dotnet_naming_rule.async_methods_must_end_with_async.severity = warning
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
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
# Example: public members must be capitalized
[*.{cs,vb}]
dotnet_naming_rule.public_members_must_be_capitalized.severity = warning
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.required_modifiers = public,internal,protected,protected_internal

dotnet_naming_style.first_word_upper_case_style.capitalization = first_word_upper
# Example: non-public members must be lower-case
[*.{cs,vb}]
dotnet_naming_rule.non_public_members_must_be_lower_case.severity = warning
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.required_modifiers = private

dotnet_naming_style.all_lower_case_style.capitalization = all_lower

@jmarolf jmarolf added the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Nov 7, 2016
@jmarolf
Copy link
Contributor Author

jmarolf commented Nov 7, 2016

test vsi please

@jmarolf jmarolf force-pushed the feature/editorconfig-namingstyle-options branch from 528fc96 to 0132d23 Compare November 10, 2016 03:02
@jmarolf jmarolf removed the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Nov 10, 2016
@jmarolf jmarolf changed the title editor config naming style WIP editor config naming style Nov 10, 2016
Copy link
Member

@jasonmalinowski jasonmalinowski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many comments but a few high level concerns:

First, let's use stronger types in a lot of places. I'd happily prefer some manually created types than Tuple<string, string, string, string> that is very challenging to maintain.

Second, there's a few places where we're building caches solely based on GetHashCode which means correctness is dependent upon an implementation that is explicitly allowed to not be correct.

{
try
{
value = editorConfigPersistence.ParseValue(value.ToString(), option.Option.Type);
value = EditorConfigNamingStyleParser.GetNamingStylesStringFromDictionary(_codingConventionSnapshot.AllRawConventions);
Copy link
Member

@jasonmalinowski jasonmalinowski Nov 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we just update EditorConfigPersistence to always be given the full set, and then the simple case just throws away everything else? That'll avoid the duplicate code here.
#Resolved

public partial class EditorConfigNamingStyleParserTests
{

/// <summary>
Copy link
Member

@jasonmalinowski jasonmalinowski Nov 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always think of <summary> to mean "I want this to be shown in QuickInfo". I'm not sure that applies here. #Resolved
#Resolved

{
internal static partial class EditorConfigNamingStyleParser
{
public static IEnumerable<Tuple<string, string, Tuple<string, string, string, string>, Tuple<string, string, string, string, string>>> ParseDictionary(IReadOnlyDictionary<string, object> allRawConventions)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make an actual type for this, or use our new tuples or something. This makes the code very difficult to understand.

public static IEnumerable<Tuple<string, string, Tuple<string, string, string, string>, Tuple<string, string, string, string, string>>> ParseDictionary(IReadOnlyDictionary<string, object> allRawConventions)
{
var namingRules = (from kvp in allRawConventions
where kvp.Key.Trim().StartsWith("dotnet_naming_rule.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TrimStart, just to avoid extra garbage if the end has stuff?

select kvp).ToDictionary(x => x.Key, x => x.Value);

var namingRulesSeverity = (from kvp in namingRules
where kvp.Key.Trim().EndsWith(".severity")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider trimming the keys once rather than doing it during each query.


namespace Microsoft.CodeAnalysis.CSharp.CodeStyle
{
internal static class CSharpCodeStyleOptions
{
// TODO: get sign off on public api changes.
public static readonly Option<bool> UseVarWhenDeclaringLocals = new Option<bool>(nameof(CodeStyleOptions), nameof(UseVarWhenDeclaringLocals), defaultValue: true,
storageLocations: new RoamingProfileStorageLocation("TextEditor.CSharp.Specific.UseVarWhenDeclaringLocals"));
storageLocations: new OptionStorageLocation[]{
Copy link
Member

@jasonmalinowski jasonmalinowski Nov 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't have to name the type, which might make it a bit less verbose. Does this being params help? #Resolved

Copy link
Contributor Author

@jmarolf jmarolf Nov 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

params array doesn't work with named arguments.

It can either look like this:

        public static readonly Option<bool> UseVarWhenDeclaringLocals = new Option<bool>(nameof(CodeStyleOptions), nameof(UseVarWhenDeclaringLocals), defaultValue: true,
            storageLocations: new OptionStorageLocation[]{
                new EditorConfigStorageLocation("csharp_style_var_for_locals", ParseEditorConfigCodeStyleOption),
                new RoamingProfileStorageLocation("TextEditor.CSharp.Specific.UseVarWhenDeclaringLocals")});

Or this:

        public static readonly Option<bool> UseVarWhenDeclaringLocals = new Option<bool>(nameof(CodeStyleOptions), nameof(UseVarWhenDeclaringLocals), true,
                new EditorConfigStorageLocation("csharp_style_var_for_locals", ParseEditorConfigCodeStyleOption),
                new RoamingProfileStorageLocation("TextEditor.CSharp.Specific.UseVarWhenDeclaringLocals"));

#Resolved

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what @jasonmalinowski meant was that you could omit OptionStorageLocation in the first one and just have new [] { ... }.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C# does not allow you to use new [] { ... } syntax if the array contains two separate types


switch (args[1].Trim())
{
case "none": return new CodeStyleOption<bool>(value: isEnabled, notification: NotificationOption.None);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For code styles, did we ever discuss having it be more explicit to say severity=none? My worry is an option that is "false, none" isn't exactly obvious in context what that means for somebody first seeing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

severity=none could be ambiguous to some parsers. maybe :?

csharp_style_inlined_variable_declaration = severity=none

csharp_style_inlined_variable_declaration = severity:none


internal static readonly PerLanguageOption<CodeStyleOption<bool>> PreferInlinedVariableDeclaration = new PerLanguageOption<CodeStyleOption<bool>>(
nameof(CodeStyleOptions),
nameof(PreferInlinedVariableDeclaration),
defaultValue: TrueWithSuggestionEnforcement,
storageLocations: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferInlinedVariableDeclaration"));
storageLocations: new OptionStorageLocation[]{
new EditorConfigStorageLocation("csharp_style_inlined_variable_declaration", ParseEditorConfigCodeStyleOption),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 'csharp' in the core layer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PreferInlinedVariableDeclaration only applies to C#. I'll move it out.


namespace Microsoft.CodeAnalysis.Options
{
internal sealed class NamingEditorConfigStorageLocation : OptionStorageLocation { }
Copy link
Member

@jasonmalinowski jasonmalinowski Nov 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to comment why this type is there. I don't disagree with it's existence (and now want to refactor the language settings one for clarity too...) but a comment is good. #Resolved
#Resolved

var optionSet = await options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).ConfigureAwait(false);
string language = context.Compilation.Language;
var currentValue = optionSet.GetOption(SimplificationOptions.NamingPreferences, language);
int hashCode = currentValue.GetHashCode();
Copy link
Member

@jasonmalinowski jasonmalinowski Nov 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this assuming possibly incorrect things about hash codes? #Resolved

where kvp.Key.Trim().StartsWith("dotnet_naming_style.")
select kvp).ToDictionary(x => x.Key, x => x.Value);

var namingStyleRequiredPrefix = (from kvp in namingStyles
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would seem easier to just loop through all keys, do the split you're doing around line 63, and then just switch on the last component.

@jmarolf jmarolf force-pushed the feature/editorconfig-namingstyle-options branch 2 times, most recently from 76bee8b to 57526e2 Compare November 11, 2016 03:26
Copy link
Member

@Pilchie Pilchie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perf terrifies me here:

  1. Enumerating the editorconfig dictionary a bunch of times.
  2. Allocating tons of yield enumerators.
  3. No caching, so we do all of this work for every symbol the analyzer runs on (hopefully every symbol in the solution soon, currently every symbol in an open file).

Assert.Equal("method_and_property_symbols", symbolSpec.Name);


Assert.Equal(2 ,symbolSpec.ApplicableSymbolKindList.Count);
Copy link
Member

@Pilchie Pilchie Nov 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have AssertEx.SetEqual which might make this easier.
#Resolved

var optionSet = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();
if (optionSet == null)
{
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we at least assert or something here? It seems bad that all of our analyzers would just silently do nothing if we broke getting options.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not WaitAndGetResult?

{
internal static partial class EditorConfigNamingStyleParser
{
private static bool TryGeySerializableNamingRule(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: TryGet

{
diagnosticSeverity = default(DiagnosticSeverity);
var result = (from kvp in allRawConventions
where kvp.Key.Trim() == $"dotnet_naming_rule.{namingRuleName}.severity"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the whole point of a dictionary that you can look up the keys faster than iterating over the whole thing? Why not Trim while populating the dictionary, and just use TryGetValue here?

{
namingStyleName = null;
var result = (from kvp in allRawConventions
where kvp.Key.Trim() == $"dotnet_naming_rule.{namingRuleName}.style"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment about switching to TryGetValue


public bool Equals(AccessibilityKind other)
{
return this.Accessibility == other.Accessibility;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't test for other == null but it could be from your object.Equals override.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed with change to struct type

{
get
{
internal DeclarationModifiers Modifier {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WHAT HAVE YOU DONE?!?!

int hashCode = currentValue.GetHashCode();
var viewModel = SerializableNamingStylePreferencesInfo.FromXElement(XElement.Parse(currentValue));
var preferences = viewModel.GetPreferencesInfo();
return preferences;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It terrifies me that we don't do any caching here and instead populate all the rules from editorconfig, serialize them to xml and then deserialize back from xml ON EVERY SYMBOL?

if (optionSet == null)
{
return;
}

Copy link
Member

@Pilchie Pilchie Nov 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whitespace
#Resolved


namespace Microsoft.CodeAnalysis.CSharp.CodeStyle
{
internal static class CSharpCodeStyleOptions
{
// TODO: get sign off on public api changes.
public static readonly Option<bool> UseVarWhenDeclaringLocals = new Option<bool>(nameof(CodeStyleOptions), nameof(UseVarWhenDeclaringLocals), defaultValue: true,
storageLocations: new RoamingProfileStorageLocation("TextEditor.CSharp.Specific.UseVarWhenDeclaringLocals"));
storageLocations: new OptionStorageLocation[]{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what @jasonmalinowski meant was that you could omit OptionStorageLocation in the first one and just have new [] { ... }.

@Pilchie
Copy link
Member

Pilchie commented Nov 11, 2016

Oh - also, did we decide on a way to deal with asynchrony here?

@jmarolf jmarolf force-pushed the feature/editorconfig-namingstyle-options branch from 57526e2 to 26f1ba8 Compare November 11, 2016 23:54
{
// TODO: report this somewhere?
Copy link
Member

@CyrusNajmabadi CyrusNajmabadi Nov 15, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. We can report a NFW here.
#Resolved

var options = context.Options;
var syntaxTree = context.Node.SyntaxTree;
var cancellationToken = context.CancellationToken;
var optionSet = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lots of duplication across all our analyzers. Can you have a single extnsion method that does this?

{
return (analyzerOptions as WorkspaceAnalyzerOptions)?.Workspace.Options;
var workspace = (analyzerOptions as WorkspaceAnalyzerOptions)?.Workspace;
if (workspace == null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does this happen?

return null;
}

var documentId = workspace.CurrentSolution.GetDocumentId(syntaxTree);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh god... we're accessing mutable stuff in a BG analyzer? :(

{
case "pascal_case": return Capitalization.PascalCase;
case "camel_case": return Capitalization.CamelCase;
case "first_word_upper": return Capitalization.FirstUpper;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be Capitalization.FirstWordUpper? or shoudl the string be "first_upper"?

object result;
if (conventionsDictionary.TryGetValue($"dotnet_naming_style.{namingStyleName}.word_separator", out result))
{
return (string)result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this cast fail?

private static string GetNamingRequiredPrefix(string namingStyleName, IReadOnlyDictionary<string, object> conventionsDictionary)
{
object result;
if (conventionsDictionary.TryGetValue($"dotnet_naming_style.{namingStyleName}.required_prefix", out result))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use out-var

var requiredModifiers = GetSymbolsRequiredModifiers(symbolSpecName, conventionsDictionary);

symbolSpec = new SymbolSpecification(
Guid.NewGuid(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation weird.

switch (symbolSpecApplicableKind)
{
case "class":
return new[] { new SymbolKindOrTypeKind(TypeKind.Class) };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. can we use immutable arrays.
  2. can we cache these somewhere so we don't keep allocating what are essentially constants.

@jmarolf jmarolf force-pushed the feature/editorconfig-namingstyle-options branch from 16a271b to d1b7a09 Compare November 17, 2016 06:32
@jmarolf jmarolf changed the base branch from master to dev15-rc2 November 17, 2016 06:32
@jmarolf jmarolf force-pushed the feature/editorconfig-namingstyle-options branch 4 times, most recently from 82cac5d to e3d55c8 Compare November 17, 2016 10:30
Copy link
Contributor

@dpoeschl dpoeschl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only minor requests. 👍

@@ -0,0 +1,245 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: We typically use underscores in file names when just splitting up the functionality of a type into partials, and use dots when separating out nested types.

private static readonly SymbolKindOrTypeKind _method = new SymbolKindOrTypeKind(SymbolKind.Method);
private static readonly SymbolKindOrTypeKind _field = new SymbolKindOrTypeKind(SymbolKind.Field);
private static readonly SymbolKindOrTypeKind _event = new SymbolKindOrTypeKind(SymbolKind.Event);
private static readonly SymbolKindOrTypeKind _namespace = new SymbolKindOrTypeKind(SymbolKind.Namespace);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We got rid of namespaces and type parameters, at least from the UI. Did I miss removing them somewhere else that tricked you into including them here, or were you operating off the old UI?

case "public":
builder.Add(Accessibility.Public);
break;
case "internal":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we standardizing on "internal" for both C# and VB? The UI in VS makes the distinction, though I don't think the old serialization code made the distinction (but that didn't matter because it wasn't user-facing).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline, maybe we can just use either language's nomenclature interchangeably.


namespace Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles
{
internal class SymbolSpecification
internal partial class SymbolSpecification
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this partial? I'm not seeing another part.

return Modifier == other.Modifier;
}

public static bool operator ==(ModifierKind left, ModifierKind right)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need the operators here but not in SymbolKindOrTypeKind?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oversight. Added the operators

@@ -5,11 +5,11 @@

namespace Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles
{
internal class NamingStylePreferencesInfo
internal class NamingStyleRules
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename the file to match.

/// 2. Name Style
/// 3. Naming Rule (points to Symbol Specification IDs)
/// </summary>
internal class NamingStylePreferences : IEquatable<NamingStylePreferences>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename file to match.

Copy link
Member

@jasonmalinowski jasonmalinowski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will work. Many small questions, and I see a number of other comments that should be cleaned up. But those are only local fixes.


var optionSet = await context.Options.GetDocumentOptionSetAsync(location.SourceTree, context.CancellationToken).ConfigureAwait(false);
var namimgStylePreferences = optionSet.GetOption(SimplificationOptions.NamingPreferences, context.Compilation.Language);
return namimgStylePreferences.GetNamingStyleRules();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is related to your change at this point, but it's really confusing what the difference is between "preferences" and "rules"...

@@ -134,6 +133,19 @@ public bool TryFetch(OptionKey optionKey, out object value)
return false;
}
}
else if (optionKey.Option.Type ==typeof(NamingStylePreferences))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space.

// We store these as strings, so deserialize
if (value is string serializedValue)
{
value = NamingStylePreferences.FromXElement(XElement.Parse(serializedValue));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be resilient to if that parse fails. It's the responsibility of the persister to deal with it.

@@ -202,7 +214,18 @@ public bool TryPersist(OptionKey optionKey, object value)
}
}

this._settingManager.SetValueAsync(storageKey, value, isMachineLocal: false);
if (optionKey.Option.Type == typeof(NamingStylePreferences))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put as an else if with the previous test?

var options = OptionService.GetOption(SimplificationOptions.NamingPreferences, _languageName);
if (string.IsNullOrEmpty(options))
var preferencesInfo = this.OptionService.GetOption(SimplificationOptions.NamingPreferences, _languageName);
if (preferencesInfo == null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"preferences" now that we dropped "info" in the type name?

@@ -325,7 +361,7 @@ internal override IEnumerable<OptionKey> GetChangedOptions(OptionSet optionSet)
foreach (var kvp in _values)
{
var currentValue = optionSet.GetOption(kvp.Key);
if (!object.Equals(currentValue, kvp.Value))
if (currentValue?.Equals(kvp.Value) == false)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or just use the static object.Equals() which does this right...

{
// TODO: report this somewhere?
return false;
return editorConfigPersistence.TryParseReadonlyDictionary(allRawConventions, option.Option.Type, out value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the point of such a refactoring like this that instead of this having naming-styles special case knowledge, that you'd delete NamingEditorConfigStorageLocation and make it be just a new EditorConfigStorageLocation(EditorConfigNamingStyleParser.GetNamingStylesFromDictionary)?

{
value = editorConfigPersistence.ParseValue(value.ToString(), option.Option.Type);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you've removed the only call to the public ParseValue, and you could delete it.

@jmarolf jmarolf force-pushed the feature/editorconfig-namingstyle-options branch from 955be72 to e021d50 Compare December 14, 2016 06:02
Copy link
Member

@Pilchie Pilchie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still a little bit concerned about the defaults assumed when things can't be found/parsed, and about what string comparisons we use.

@@ -5,10 +5,11 @@
using System.Xml.Linq;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Simplification;
using System.Collections.Generic;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

System first.

@@ -290,6 +290,42 @@ private OptionSet ReadOptionFrom<T>(OptionSet options, string language, PerLangu
return options.WithChangedOption(option, language, value);
}

protected void WriteOptionTo(OptionSet options, Option<NamingStylePreferences> option, ObjectWriter writer, CancellationToken cancellationToken)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the language-neutral ones here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not today. But I've added them for consistency.

WriteOptionTo(options, language, CodeStyleOptions.PreferCoalesceExpression, writer, cancellationToken);
WriteOptionTo(options, language, CodeStyleOptions.PreferCollectionInitializer, writer, cancellationToken);
WriteOptionTo(options, language, CodeStyleOptions.PreferExplicitTupleNames, writer, cancellationToken);
WriteOptionTo(options, language, CodeStyleOptions.PreferInlinedVariableDeclaration, writer, cancellationToken);
WriteOptionTo(options, language, CodeStyleOptions.PreferNullPropagation, writer, cancellationToken);
WriteOptionTo(options, language, CodeStyleOptions.PreferObjectInitializer, writer, cancellationToken);
WriteOptionTo(options, language, CodeStyleOptions.PreferThrowExpression, writer, cancellationToken);
WriteOptionTo(options, language, SimplificationOptions.NamingPreferences, writer, cancellationToken);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing before, but it didn't matter because we don't run the naming analyzer OOP?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep


internal static Accessibility FromXElement(XElement accessibilityElement)
{
return (Accessibility)Enum.Parse(typeof(Accessibility), accessibilityElement.Value);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do all callers handle exceptions in case the this doesn't parse (the file has been modified, etc)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't do this for code styles, are you saying we should handle this for all cases where we read XML from the option persistence service? Sounds like a good idea

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added try/catch for all the callsites that parse XElements


public override bool Equals(object obj)
{
return Equals((ModifierKind)obj);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably do an is check here.

private static Capitalization GetNamingCapitalization(string namingStyleName, IReadOnlyDictionary<string, object> conventionsDictionary)
{
var result = GetStringFromConventionsDictionary(namingStyleName, "capitalization", conventionsDictionary);
return ParseCapitalizationScheme(result);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will return PascalCase if it can't be found, or isn't a legal value. Is that what we want?

return ImmutableArray<SymbolKindOrTypeKind>.Empty;
}

var builder = ArrayBuilder<SymbolKindOrTypeKind>.GetInstance();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create the builder after the _all return.

var builder = ArrayBuilder<Accessibility>.GetInstance();
if (symbolSpecApplicableAccessibilities.Trim() == "*")
{
builder.AddRange(Accessibility.Public, Accessibility.Internal, Accessibility.Private, Accessibility.Protected, Accessibility.ProtectedOrInternal);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cache the _all case as above?

return ImmutableArray<ModifierKind>.Empty;
}

private static readonly ModifierKind _abstractModifierKind = new ModifierKind(ModifierKindEnum.IsAbstract);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually - these are structs right? Do we gain anything from caching them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perf was not measured.

var builder = ArrayBuilder<ModifierKind>.GetInstance();
if (symbolSpecRequiredModifiers.Trim() == "*")
{
builder.AddRange(_abstractModifierKind, _asyncModifierKind, _constModifierKind, _readonlyModifierKind, _staticModifierKind);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider caching the * case.

@jmarolf jmarolf force-pushed the feature/editorconfig-namingstyle-options branch from e021d50 to 275258f Compare December 19, 2016 04:05
@jmarolf jmarolf force-pushed the feature/editorconfig-namingstyle-options branch from 275258f to 92e3d18 Compare December 19, 2016 04:15
@jmarolf jmarolf mentioned this pull request Dec 19, 2016
@jmarolf jmarolf dismissed Pilchie’s stale review December 20, 2016 23:49

Pilchie is OOF

@jmarolf
Copy link
Contributor Author

jmarolf commented Dec 20, 2016

@srivatsn for approval. @Pilchie is off so he can't signoff but I've addressed his comments.

@srivatsn
Copy link
Contributor

Looks like there's been a bunch of commits to address feedback. Can someone else review the recent changes in @Pilchie's absence?

@Pilchie
Copy link
Member

Pilchie commented Dec 21, 2016 via email

@jmarolf
Copy link
Contributor Author

jmarolf commented Dec 21, 2016

@Pilchie thanks!

Copy link
Member

@Pilchie Pilchie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, except the dictionary comparer issue, but we need to follow-up with the editor team about that.

Let's merge this and make any adjustment there after the fact if necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants