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

Add prefer trailing commas code-style option #54859

Open
wants to merge 35 commits into
base: main
Choose a base branch
from

Conversation

Youssef1313
Copy link
Member

@Youssef1313 Youssef1313 commented Jul 16, 2021

Fixes #25206
Fixes #21784

  • What are other code generations that should respect this option?
  • CSharpUseObjectInitializerCodeFixProvider should respect this option
  • CSharpUseCollectionInitializerCodeFixProvider should respect this option.
  • Revise CSharpPopulateSwitchExpressionCodeFixProvider
  • Revise ConvertSwitchStatementToExpression
  • Address Implement Initializer expression wrappers #39878 (comment)

@@ -295,6 +295,12 @@ private static Option2<CodeStyleOption2<AddImportPlacement>> CreateUsingDirectiv
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental", CodeStyleOptions2.TrueWithSilentEnforcement),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(AllowBlankLineAfterColonInConstructorInitializer)}")});

public static readonly Option2<CodeStyleOption2<bool>> PreferTrailingCommas = CreateOption(
CSharpCodeStyleOptionGroups.ExpressionLevelPreferences, nameof(PreferTrailingCommas),
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure what is the suitable CSharpCodeStyleOptionGroups to use here.

@@ -295,6 +295,12 @@ private static Option2<CodeStyleOption2<AddImportPlacement>> CreateUsingDirectiv
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental", CodeStyleOptions2.TrueWithSilentEnforcement),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(AllowBlankLineAfterColonInConstructorInitializer)}")});

public static readonly Option2<CodeStyleOption2<bool>> PreferTrailingCommas = CreateOption(
CSharpCodeStyleOptionGroups.ExpressionLevelPreferences, nameof(PreferTrailingCommas),
defaultValue: s_trueWithSuggestionEnforcement,
Copy link
Member Author

Choose a reason for hiding this comment

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

Should this be true or false?

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 definitely be false.

Copy link
Member

Choose a reason for hiding this comment

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

and it should be silent.

Copy link
Member

@sharwell sharwell Jan 18, 2022

Choose a reason for hiding this comment

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

I would expect true/silent. This is a maintainability improvement, not a readability improvement (it improves the quality of git blame over time).

Copy link
Member

Choose a reason for hiding this comment

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

i'm somewhat amenable to that.. i do think 'silent' has to be case here.

Choose a reason for hiding this comment

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

true/silent makes the most sense.

Although, I do think it improves readability (for me personally), in that it helps clarify it is an existentially qualified collection. My mind reads opening line { with a series of lines ending in , followed by a matching } as one giant math expression "construct collection with these members..."

@Youssef1313
Copy link
Member Author

I have dropped this for long. I'll get to it back as soon as I can.

@Youssef1313 Youssef1313 marked this pull request as ready for review January 18, 2022 19:26
@Youssef1313 Youssef1313 requested a review from a team as a code owner January 18, 2022 19:26
@Youssef1313
Copy link
Member Author

@CyrusNajmabadi Can you take a look please? Thanks!

@@ -1785,7 +1785,7 @@ void Main()
enum Color : ulong
{
Green = 1UL << 0,
Blue = 1UL << 1
Blue = 1UL << 1,
Copy link
Member

Choose a reason for hiding this comment

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

this appears to eb a regression in all these tests. i would expect nothing to change unless the test explicitly turned this option on.

Copy link
Member

Choose a reason for hiding this comment

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

This is impacted by the ongoing discussion in https://github.com/dotnet/roslyn/pull/54859/files#r671040273

@@ -151,6 +151,7 @@ private IEnumerable<CodeStyleSetting> GetExpressionCodeStyleOptions(AnalyzerConf
yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferRangeOperator, description: ServicesVSResources.Prefer_range_operator, editorConfigOptions, visualStudioOptions, updaterService, FileName);
yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.ImplicitObjectCreationWhenTypeIsApparent, description: CSharpVSResources.Prefer_implicit_object_creation_when_type_is_apparent, editorConfigOptions, visualStudioOptions, updaterService, FileName);
yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferTupleSwap, description: ServicesVSResources.Prefer_tuple_swap, editorConfigOptions, visualStudioOptions, updaterService, FileName);
yield return CodeStyleSetting.Create(CSharpCodeStyleOptions.PreferTrailingCommas, description: ServicesVSResources.Prefer_trailing_commas, editorConfigOptions, visualStudioOptions, updaterService, FileName);
Copy link
Member

Choose a reason for hiding this comment

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

hrmm... i don't think this is an expression-style... This is all for declarations (or maybe variables). consider putting in the variable section? or making a new section?

Copy link
Member Author

Choose a reason for hiding this comment

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

@CyrusNajmabadi Actually even those in GetVariableCodeStyleOptions have CSharpCodeStyleOptionGroups.ExpressionLevelPreferences. I'm not sure what's the suitable group here.

public static readonly Option2<CodeStyleOption2<bool>> PreferTrailingCommas = CreateOption(
CSharpCodeStyleOptionGroups.ExpressionLevelPreferences, nameof(PreferTrailingCommas),
defaultValue: s_trueWithSuggestionEnforcement,
"csharp_style_prefer_trailing_commas",
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 it shoudl likely be csharp_style_prefer_trailing_comma. @sharwell thoughts? plural on singular here? We don't say prefer_switch_expressions for example, just prefer_switch_expression. And you can't have x,,, and this doesn't impact anything but the last member (since the other members must have a comma already). so it's really about a singular trailing comma in these lists.

--

i'm also somewhat on the fence here if we should say csharp_style_prefer_trailing_enum_comma. The reason here is that we have proposals (i think i'm championing one) for things like a trailing comma in a parameter list. i'm wary about having a single option now that some users might not want to apply to different constructs.

Copy link
Member Author

@Youssef1313 Youssef1313 Jan 18, 2022

Choose a reason for hiding this comment

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

@CyrusNajmabadi Per @sharwell's comment on the original issue:

it makes sense to add an editorconfig option to include this comma, which the various code generation features would then account for.

The "various code generation features" part implies that a single option is needed for everything. But I'm open to change.

Copy link
Member

Choose a reason for hiding this comment

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

reading through the critical decision was:

@CyrusNajmabadi it seems like only one option would be needed here. I don't think StyleCop Analyzers ever had a request to split the single option in two, and even if it did it would be an extreme minority scenario.

I'm hesitantly ok with this. However, if we have this, we need to update more than just the enum generator here. We also need to check that object/collection-initializers and arrays are generated with this and checked for this.

Copy link
Member

Choose a reason for hiding this comment

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

Also, switch/with expressions.

Copy link
Member Author

Choose a reason for hiding this comment

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

@CyrusNajmabadi Can you point me to where object/collection initializers and switch/with expressions generators are?

Copy link
Member

@CyrusNajmabadi CyrusNajmabadi left a comment

Choose a reason for hiding this comment

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

some changes def need to happen. tagging @sharwell for some thoughts (esp. about the future).

@CyrusNajmabadi
Copy link
Member

Leaving an analyzer and a codefix as follow-up for now.

i think this woudl be required as part of this. note: you can put this in a feature branch if you want. but to be merged into main, we would need teh analzyer/fixer.

@CyrusNajmabadi
Copy link
Member

i'm hesitant about true/false being enough here. Say i'm a user that likes new Point { X = 1, Y = 2 }, but also want:

new Point
{
    X = 1,
    Y = 2,
}

it doesn't feel like i have an approach that allow me to specify this. Note: this isn't speculative. I am this user :D

@CyrusNajmabadi
Copy link
Member

@sharwell i'd like your thoughts on #54859 (comment). I actually feel pretty darn strongly about this :)

@sharwell
Copy link
Member

sharwell commented Jan 18, 2022

@sharwell i'd like your thoughts on #54859 (comment). I actually feel pretty darn strongly about this :)

This style applies if and only if the ,, if present, will be the final token on its line. See also SA1413 (Use trailing commas in multi-line initializers). Ideally the wrapping refactoring can account for this to add/remove the trailing , to retain adherence to this rule.

@CyrusNajmabadi
Copy link
Member

This style applies if and only if the ,, if present, will be the final token on its line.

That is definitely fine with me. maybe style_prefer_trailing_comma_on_line? or maybe we can just doc.


if (options.Preferences.Options.GetOption(CSharpCodeStyleOptions.PreferTrailingCommas).Value)
{
members.Add(SyntaxFactory.Token(SyntaxKind.CommaToken));
}
Copy link
Member

Choose a reason for hiding this comment

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

i actually have mixed feelings about this approach. i actually think the generator should only do this if generating the first item. if generating a followup item it should take a 'when in rome' aproach where it follows what the existing code does.

Copy link
Member Author

@Youssef1313 Youssef1313 Feb 5, 2022

Choose a reason for hiding this comment

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

@CyrusNajmabadi Per my understanding of a previous conversation with @sharwell, code generation should adhere to code-style options when possible. If the user doesn't like the style, he should change the option value.

Copy link
Member

Choose a reason for hiding this comment

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

Some StyleCop Analyzers options have a tri-state true/false/preserve, where preserve means the current state should be followed when adding new items to the same location. The problem with a preserve value is it doesn't allow the user to also express a preference for the behavior when creating a brand new collection.

I lean towards following the defined code style in this case.

Copy link
Member Author

Choose a reason for hiding this comment

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

@sharwell The false value for analyzers in almost all code-style options means "preserve" (never report diagnostic). It doesn't enforce the opposite style. It's more of enable/disable rather than a real true/false.

@Youssef1313
Copy link
Member Author

@sharwell @CyrusNajmabadi Can you take a look? I haven't yet handled all possible syntax kinds, but the general idea will be the same.

@sharwell sharwell marked this pull request as draft June 24, 2022 14:46
{
using VerifyCS = CSharpCodeFixVerifier<PreferTrailingCommaDiagnosticAnalyzer, PreferTrailingCommaCodeFixProvider>;

public class PreferTrailingCommaTests
Copy link
Member Author

Choose a reason for hiding this comment

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

@sharwell I think I'm covering all of these scenarios (and more).
I'm moving one case that is not covered in StyleCopAnalyzers in DotNetAnalyzers/StyleCopAnalyzers#3533 :)

src/Analyzers/Core/Analyzers/IDEDiagnosticIds.cs Outdated Show resolved Hide resolved
public PreferTrailingCommaDiagnosticAnalyzer() : base(
diagnosticId: IDEDiagnosticIds.PreferTrailingCommaDiagnosticId,
enforceOnBuild: EnforceOnBuildValues.PreferTrailingComma,
option: CSharpCodeStyleOptions.PreferTrailingComma,
Copy link
Member

Choose a reason for hiding this comment

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

😢 Every time we add an option it makes me sad.

I've found in the case of this maintainability rule, it also adds to confusion. Some users think this is a readability rule that should be configurable. However, it is not a readability rule, and mistaking the goal here will lead some users to incorrectly configure the rule.

Copy link
Member Author

Choose a reason for hiding this comment

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

@sharwell Are you referring to the fact that csharp_style_prefer_trailing_comma = false will disable the analyzer rather than enforcing a non-trailing comma? Or in other words, you like this code-style option to be used by code generation but not by the analyzer?

If that's the case, I definitely agree, but #60584 needs a decision.

@Youssef1313 Youssef1313 marked this pull request as ready for review June 24, 2022 19:48
@Youssef1313
Copy link
Member Author

There are two failings tests (FixAll crashes when there are nested diagnostics). I'm not sure how to best approach this.

@Youssef1313
Copy link
Member Author

Youssef1313 commented Jul 8, 2022

It looks like "Use collection initializer" uses SyntaxFactory to produce the initializer node, where the start opening curly brace { has elastic trivia. Then, at some point (after the codefix is done, possibly the formatter) the { may or may not be followed by a new line.

Because the post processing behavior isn't consistent (sometimes a new line is put after { and sometimes not), the logic of always adding , for this codefix isn't good.

Should I force a new line after the {? (That too seems unreliable)

It's currently unclear to me under what conditions the (formatter?) doesn't add a new line.

@Youssef1313
Copy link
Member Author

I can just leave collection/object initializer codefixes as they was. Is that okay? @CyrusNajmabadi @sharwell

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

Successfully merging this pull request may close these issues.

C# - .editorconfig option for force trailing comma Code Style for trailing commas
4 participants