Page MenuHomePhabricator

[clang-format] add option for dangling parenthesis
Needs ReviewPublic

Authored by stringham on May 9 2017, 9:47 PM.
Tags
  • Restricted Project
  • Restricted Project
Tokens
"Doubloon" token, awarded by mjmaurer."Love" token, awarded by FederAndInk."Like" token, awarded by zvilius."Mountain of Wealth" token, awarded by ruzyysmartt."Love" token, awarded by GuillerLT."Love" token, awarded by orvar.karason."Love" token, awarded by loic-joly-sonarsource."Love" token, awarded by kuznetsss."Love" token, awarded by mgaunard."Love" token, awarded by Qub1."Like" token, awarded by thorbenk."Like" token, awarded by jakar."Love" token, awarded by kring."Like" token, awarded by Weyzu."Love" token, awarded by blandcr."Like" token, awarded by jj."Like" token, awarded by alex1701c."Like" token, awarded by Thyrum."Love" token, awarded by geckyan."Like" token, awarded by houbaron."100" token, awarded by njakob."Like" token, awarded by huyage."Love" token, awarded by Damio."Love" token, awarded by Folling."Love" token, awarded by rdaly525."Like" token, awarded by budnyjj."Love" token, awarded by Blackhex."Love" token, awarded by iolojz."100" token, awarded by PhilDakinOcient."Love" token, awarded by samwalls."Love" token, awarded by Tiedye."Love" token, awarded by JPMMaia."Love" token, awarded by njaldea."Love" token, awarded by glfmn."Mountain of Wealth" token, awarded by aaptho."Love" token, awarded by teohhanhui."Like" token, awarded by quezak."Love" token, awarded by mdrohmann."Love" token, awarded by mmpestorich."Love" token, awarded by mnemotic."Like" token, awarded by msafi."Like" token, awarded by nyaapa."Love" token, awarded by fdwr."Mountain of Wealth" token, awarded by paulreimer."Like" token, awarded by reupen."Like" token, awarded by hyldmo."Like" token, awarded by Rapatas."Like" token, awarded by fiblit."Like" token, awarded by dmcyk."Like" token, awarded by tadeu."Like" token, awarded by DevAlone."Like" token, awarded by sciencemanx."Like" token, awarded by catskul."Like" token, awarded by kc9jud."Like" token, awarded by zroug."Like" token, awarded by VZ."Like" token, awarded by jtbandes.

Details

Summary

This adds an option to clang-format to support dangling parenthesis.

If you have a parameter list like

Foo(
    param1,
    param2,
    param3,
);

clang-format currently only supports putting the closing paren on the same line as param3:

Foo(
    param1,
    param2,
    param3, );

This makes it difficult to see the change from the function signature to the function body, for example:

class Foo extends Bar {
    constructor(
        param1,
        param2,
        param3, ) {
        super(param1, 'x');
        this.param2 = param2;
    }
}

would now be formatted like:

class Foo extends Bar {
    constructor(
        param1,
        param2,
        param3, 
    ) {
        super(param1, 'x');
        this.param2 = param2;
    }
}

and it is much easier to see the difference between the parameter list and the function body.

Diff Detail

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
iolojz added a subscriber: iolojz.EditedFeb 17 2020, 1:46 AM

Will this option also work with dangling braces from initializers?

I have some code that looks like this:

return some_type_with_a_long_name{
    get_param_number_one(),
    get_param_number_two()
};

Clang format will put the brace at the end of the line:

return some_type_with_a_long_name{
    get_param_number_one(),
    get_param_number_two()};

I would like to keep the style of the first snippet.

I would also love that (y)!

It should also be noted that the CLion formatter also supports this for all kinds of brackets (function declaration/call, template declaration/instantiation parameters, lambda capture lists, for statements, binary expressions as well as initializer lists).

bbassi added a subscriber: bbassi.Mar 26 2020, 1:42 PM

@MyDeveloperDay Is someone working on fixing the breaking tests and merging it? I need this feature so if someone isn't working on it already, I can take it.

@bbassi, I don't have plans to address this, and am supportive of you finishing it up.

The plan was to move this as one of the options in the BracketAlignmentStyle configuration (value of AlwaysBreakWithDanglingParenthesis) rather than have it be a brand new config property.

So we need to do that switch, and write new tests for this feature.

@MyDeveloperDay Is someone working on fixing the breaking tests and merging it? I need this feature so if someone isn't working on it already, I can take it.

as @stringham isn't working on it feel free to take it. Include me as a reviewer and I'll try and give you a timely response.

@bbassi @MyDeveloperDay I'll buy whoever manages to land this feature a case of their favorite beer/beverage (max $50). Or $50 worth of girlscout cookies. : )

Just ping me when it lands and we can arrange the shipment.

Here's a commit rebased as of yesterday that is still adding DanglingParenthesis option instead of extending the BracketAlignmentStyle configuration.

https://github.com/lucidsoftware/llvm-project/commit/3c04de1feffaa9787234da8fe3cf288eebc979d6

@stringham @MyDeveloperDay I have some questions.

  • As some have pointed out DanglingParenthesis might be a confusing name, so should we try to call it something like BreakBeforeClosingBracket? When this option when is set to true we will always break before closing bracket.
  • My use case is slightly different. I want each argument/parameter on it's own line and always have a break after opening bracket and before closing bracket. In other words, I don't want it to try to bin packing at all. What do you think about incorporating that use-case in current one? or if that should be a separate change.
msafi removed a subscriber: msafi.Mar 27 2020, 10:58 AM

I think that adding AlwaysBreakWithDanglingParenthesis as an option to BracketAlignmentStyle should not impact any of the binpacking settings - they should be distinct configuration.

I don't think that's quite right. Then you will also have to have a AlignWithDanglingParenthesis for cases when people still want closing parenthesis on new line but want parameters as well as closing parenthesis to be aligned with opening parenthesis. I think we need a separate option, something like BreakBeforeClosingBracket.

@MyDeveloperDay Can you please share your thoughts on my comment above?

I don't think that's quite right. Then you will also have to have a AlignWithDanglingParenthesis for cases when people still want closing parenthesis on new line but want parameters as well as closing parenthesis to be aligned with opening parenthesis. I think we need a separate option, something like BreakBeforeClosingBracket.

I think that's the biggest problem with making changes to Clang-Format, every name does things that the name doesn't imply.

Here it's very similar to the bin packing options, in my case it's BraceWrapping.AfterExternBlock also indenting by default, making adding a IndentExternBlock a real pain.

I personally say fuck backwards compatibility when maintaining it requires ever more workarounds to fix an obviously flawed abstraction, but I'm just some dude and AFAIK the guys in charge support the status quo.

I personally say fuck backwards compatibility when maintaining it requires ever more workarounds to fix an obviously flawed abstraction, but I'm just some dude and AFAIK the guys in charge support the status quo.

When LLVM itself isn't prepared to run clang-format over the the whole project because of the churn and the annoyance it would cause for existing forks and "git blame" (despite their being work around to ignore whitespace change commits), Then I feel we have to keep one eye on ensuring we allow people to incrementally upgrade their clang-format.

Even including a new option to the .clang-format file is a problem because it forces all users of that project to upgrade to a version of clang-format that recognises the option or clang-format will complain. With this in mind it means we need to allow existing users to use new binaries which where possible has zero changes to the code UNLESS they use the option. (even if that means many people delaying in using an option until its been supported for some time)

With Visual Studio and ClangPower tools always lagging behind the current capabilities due to the binaries they ship, we need to understand it may be a year+ before the majority of users are on a version that supports such changes.

Then we can multiply that annoyance to every other project using clang-format (both public and private projects), and so whilst a commit that changes such things are good by their own merit, that goodness might be lost in the noise of the complaints that come from time to time that clang-format causes huge changes version to version (even when those changes are bug fixes, let alone new features)

This is not about supporting the status quo, but about ensuring we aren't fairly/unfairly the brunt of peoples complaints. (https://travisdowns.github.io/blog/2019/11/19/toupper.html, http://lists.llvm.org/pipermail/cfe-dev/2017-June/054342.html)

@MyDeveloperDay Can you please share your thoughts on my comment above?

I'm tempted to agree that perhaps having as an enumeration BreakBeforeClosingBracket with various options might give us greater control

its recently also come to my attention that perhaps we should NEVER use a boolean as an option, as nearly every option that started out as a boolean ended up either needing to add another additional new option or being changed from a boolean to an enumeration later.

@MyDeveloperDay hey, I am currently working on this, and adding a new option called BreakBeforeClosingBracket. I have some questions to understand the existing code, they might not be directly linked to this change so I am not sure if this the best place to ask those questions. What do you think? e.g. I don't understand what FakeLParens is and have some questions about that.

Folling added a subscriber: Folling.

@MyDeveloperDay hey, I am currently working on this, and adding a new option called BreakBeforeClosingBracket. I have some questions to understand the existing code, they might not be directly linked to this change so I am not sure if this the best place to ask those questions. What do you think? e.g. I don't understand what FakeLParens is and have some questions about that.

@bbassi, you might want to consider starting a new revision if you are going to add a different option. Its probably best to discuss questions about adding that option in its own revision rather than confusing the conversation here. (you can always cross reference with a mention.

@MyDeveloperDay Thanks. This would be my first revision and I have few questions before I start coding. Would you be able to answer those over email? They are mainly about the design of clang-format and some existing options.

@MyDeveloperDay Thanks. This would be my first revision and I have few questions before I start coding. Would you be able to answer those over email? They are mainly about the design of clang-format and some existing options.

ask away

Damio awarded a token.May 22 2020, 3:48 PM
Damio added a subscriber: Damio.
detly added a subscriber: detly.Aug 3 2020, 5:55 PM
huyage added a subscriber: huyage.
njakob added a subscriber: njakob.
houbaron added a subscriber: houbaron.

@MyDeveloperDay I'm going to upload a re-based version of this. Should I rebase it off the top of master? Tip of 11? and/or create a new diff/review for each?

catskul added inline comments.Oct 25 2020, 9:15 PM
include/clang/Format/Format.h
793

@djasper I made the changes to @stringham's diff to implement your request to replace the bool with new item of BracketAlignmentStyle enum, and wondered if it might be backing us into a corner.

As strange as some of these may be I've seen a few similar to:

return_t myfunc(int x,
                int y,
                int z
                );
return_t myfunc(int x,
                int somelongy,
                int z         );
return_t myfunc(
             int x,
             int y,
             int z
         );

and even my least favorite

return_t myfunc(
    int x,
    int y,
    int z
                );

Without advocating for supporting all such styles it would seem desireable to avoid an NxM enum of two, at least theoretically, independent style parameters. With that in mind should I instead create a different parameter AlignClosingBracket with a respective enum which includes AfterFinalParameter by default, and NextLineWhenMultiline to handle the variations this diff was opened for?

On the other hand, I can just stick with adding a new variation to BracketAlignmentStyle and deal with potential variations in the future if they're requested.

jtbandes removed a subscriber: jtbandes.Oct 26 2020, 2:35 PM
Thyrum added a subscriber: Thyrum.
kring added a subscriber: kring.Dec 14 2020, 2:08 AM
jj awarded a token.Jan 17 2021, 9:28 AM
blandcr added a subscriber: blandcr.

Hi, this is a pretty desirable feature for me and I see there hasn't been any work for a few months. Is this still being worked on and/or is there anything I can do to help it along?

kring awarded a token.Mar 2 2021, 2:41 AM

If I understood it correctly there is a BraceWrapping group where BraceWrappingAfterControlStatementStyle AfterControlStatement is quite similar. It has a Never, MultiLine, and Always options. There is also a bool AfterFunction option which is currently only a bool. If it were changed to an enum it could provide this option as the MultiLine option. I do not really understand why there is a different option for control statements and functions though...

jakar awarded a token.Mar 22 2021, 4:17 PM
jakar added a subscriber: jakar.
jakar added a comment.Mar 23 2021, 9:43 AM

@catskul can you share your changes somehow? If you did, sorry, I'm new to Phabricator.

Before I saw @catskul's comment, I rebased @stringham's diff and got the existing unit tests to work. But after some experimentation, I found that it didn't always behave like I expected. It would sometimes do things like this:

someFunction(anotherLongFunctionName(arg1, arg2, "this is a description")
);

void SomeClass::someMemberFunction(const std::string& whatever, int blah
) const {
}

I think it should only break before closing when the matching opening was followed by a new line (with possible whitespace or comment).

And it also didn't handle brace-initialized vars consistently:

LongClassName longVarName1(
  somethingElseThatIsLong(arg1, arg2, arg3, arg4),
  anotherLongVarName
);

LongClassName longVarName2{
  somethingElseThatIsLong(arg1, arg2, arg3, arg4),
  anotherLongVarName};

So I think there is still a bit of work here to be done. And unit tests could definitely help.

I can take my best stab at this, when I get time, but I'm honestly not familiar with any of it. Any suggestions would be great. Please let me know how I can help.

Anyhow, I'm excited about this feature, mostly because I find it more readable, but also because it allows consistency with other languages, like Python (see PEP8):

my_list = [
    1, 2, 3,
    4, 5, 6,
]
result = some_function_that_takes_arguments(
    'a', 'b', 'c',
    'd', 'e', 'f',
)
thorbenk added a subscriber: thorbenk.
wmmc88 added a subscriber: wmmc88.Apr 8 2021, 10:51 AM
Qub1 added a subscriber: Qub1.

@jakar I did a bit of sleuthing (google catskul github, which I feel a little weird about but whatever) and I think I found their changes in a fork:
https://github.com/catskul/llvm-project/tree/catskul/dangling-parenthesis-as-bracket-alignment
It seems there are a few different branches that might be what you're looking for. I think what probably happened was they never received a response for what to rebase the changes off of so this end up falling by the wayside.

@catskul I'm not sure what the proper etiquette is for this kind of searching but given the easily associated identity I'm going to assume it's probably OK. If you do want me to edit the comment to remove the link to your GH profile just let me know.

catskul added a comment.EditedMay 6 2021, 10:02 AM

@blandcr @jakar It's all good for me. Sorry for slow responses. My time to work on this is few and far between. I'm happy if anyone picks it up or any energy put into this. Would love to see it land.

mgaunard added a subscriber: mgaunard.
seesemichaelj added inline comments.
include/clang/Format/Format.h
793

@catskul, are we waiting for a response from @djasper (or other maintainer) concerning your last question about whether or not to keep BracketAligngmentStyle or to use a new parameter AlignClosingBracket that you proposed? I'm just throwing my hand in the pile seeing what I can do to help push this issue towards landing without creating duplicate work (or just forking your code and compiling it for myself selfishly haha).

From what information I can gather, it sounds like you have a solution @catskul that meets the requested changes by @djasper, and if those changes are still desired provided your last comment here, a revision could be posted for review (after rebase, tests pass, etc).

Am I understanding correctly here?

zvilius added a subscriber: zvilius.
FederAndInk added a subscriber: FederAndInk.
mjmaurer added a subscriber: mjmaurer.