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

[clang-format] Ability to configure breaking after short return types #78010

Closed
rmarker opened this issue Jan 13, 2024 · 14 comments · Fixed by #78011
Closed

[clang-format] Ability to configure breaking after short return types #78010

rmarker opened this issue Jan 13, 2024 · 14 comments · Fixed by #78011

Comments

@rmarker
Copy link
Contributor

rmarker commented Jan 13, 2024

Currently, when AlwaysBreakAfterReturnType is None, line breaks are prevented after 'short' return types.

From ContinuationIndenter.cpp

// Don't break after very short return types (e.g. "void") as that is often
// unexpected.
if (Current.is(TT_FunctionDeclarationName) && State.Column < 6) {
  if (Style.AlwaysBreakAfterReturnType == FormatStyle::RTBS_None)
    return false;
}

Note, that 'short' here isn't the length of the type itself, but the column where the type finishes.
So, the indentation can change the behaviour.

While the comment says that breaking would be unexpected, this behaviour takes precedence over any of the penalty options for configuring where to place line breaks. It is fairly specific behaviour and can also be unexpected as it avoids breaking after the return type seemly ignoring the penalty configuration.

It would be helpful if there was a way that this could be configured.

@rmarker
Copy link
Contributor Author

rmarker commented Jan 13, 2024

It isn't exactly clear the best way that this behaviour could be configured.
It appears to have been a part of clang-format for some time.
There is the possibility of doing something more complex, for example shifting the behaviour to be a penalty instead.
However, since the current behaviour is so specific, in order to ensure backwards compatibility, a simple enable/disable is probably more suitable.

While investigating this, I experimented with an option to configure the column limit to mark the short return type.
Since this can effectively work as an on/off switch, it didn't seem prudent to limit it to a boolean.

I made a pull request (#78011) with the result of this work for consideration.

@mydeveloperday
Copy link
Contributor

Should we use a field called ShortTypes where users could define the types they want

@rmarker
Copy link
Contributor Author

rmarker commented Jan 14, 2024

Do you mean an option where you would list all the types you want to be affected by the short type behaviour?
I hadn't considered an approach like that.
That would address my scenario, allowing for the behaviour to be avoided by not enabling it for any types.

The main consideration would be whether it would cause a formatting change when upgrading to the new version.
All the affected types would have to be specified, otherwise the formatting would change.
Not sure if that is something you would want to avoid.

Also, if changing previous formatting, having it be based on the type itself rather than the column would make things clearer.
Since, it depending on the indentation is one way that it can be surprising.
For example:

// Short type, break after return type disallowed.
Type Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong::
    FunctionDeclaration();

struct S {
  // Not short type due to indentation for the struct. Break after return type allowed.
  Type
  Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong::
      FunctionDeclaration();
}

Though, I wonder whether it could be frustrating if someone wanted it to apply to all types under a certain length. I.e. having to manually add lots of types to the list.

@owenca
Copy link
Contributor

owenca commented Jan 22, 2024

Currently, when AlwaysBreakAfterReturnType is None, line breaks are prevented after 'short' return types.

From ContinuationIndenter.cpp

// Don't break after very short return types (e.g. "void") as that is often
// unexpected.
if (Current.is(TT_FunctionDeclarationName) && State.Column < 6) {
  if (Style.AlwaysBreakAfterReturnType == FormatStyle::RTBS_None)
    return false;
}

Note, that 'short' here isn't the length of the type itself, but the column where the type finishes. So, the indentation can change the behaviour.

That's likely a bug. IMO we should fix the bug and optionally add a new boolean value to AlwaysBreakAfterReturnType for backward compatibility.

@rmarker
Copy link
Contributor Author

rmarker commented Jan 22, 2024

Yeah, the fact that the short return type is based on the column and not the type itself is strange and could be a bug.
When investigating I took a conservative approach and looked to maintain the existing behaviour.

Though I wouldn't say that just changing it to use the type length would completely solve the issue.
The indentation changing the behaviour does make the short return type behaviour much more unexpected/unintuitive.
However, I would say that the entire concept of preventing breaks after short return types is also potentially confusing in of itself.
There isn't any configuration options for it, and it can seem to conflict with PenaltyReturnTypeOnItsOwnLine.
Especially since by preventing the break, it can cause a line to go over the ColumnLimit for no 'apparent' reason.

Hence how I ended up at a possible solution which enables the functionality to be effectively disabled.
I went with a top-level option, but it does make sense for it to be connected to AlwaysBreakAfterReturnType.
Being a top-level option I thought it could be a configurable size rather than a boolean, but that was more why not, rather than there being a strong motivation for having that part configurable.
So maybe three possible states, column (current), type-length, off?

@owenca
Copy link
Contributor

owenca commented Jan 26, 2024

Yeah, the fact that the short return type is based on the column and not the type itself is strange and could be a bug.

IMO the bug is not that it doesn't use the length of the type. Instead, the issue is that the (absolute) column position is not adjusted for any indentation. So using the offset relative to the starting column position of the line should be enough to fix the problem.

@rmarker
Copy link
Contributor Author

rmarker commented Jan 26, 2024

I agree with that.
In my mind, I'm just using return type length to mean the same thing as the relative column. In contrast to the absolute column.

With regards to the different states, should it be another enum for AlwaysBreakAfterReturnType, or maybe turning it into a struct, or leaving it at the top level?

@owenca
Copy link
Contributor

owenca commented Jan 26, 2024

How about adding a new boolean suboption enum value (e.g. RTBS_Short) to AlwaysBreakAfterReturnType?

@rmarker
Copy link
Contributor Author

rmarker commented Jan 26, 2024

I'm just wondering whether that would suggest that it would always break short return types, rather than the opposite of allowing to break after short return types.
If we could change RTBS_None to be the option for no restrictions, the new one could be RTBS_NeverShort? Otherwise, what about RTBS_AllowShort?

@owenca
Copy link
Contributor

owenca commented Jan 26, 2024

You are right! I think RTBS_AllowShortType should work. We probably want to rename AlwaysBreakAfterReturnType to BreakAfterReturnType and change RTBS_None to RTBS_Automatic in another patch.

@rmarker
Copy link
Contributor Author

rmarker commented Jan 26, 2024

Right. I will look into reworking the PR to use RTBS_AllowShortType.
Thanks.

@rmarker
Copy link
Contributor Author

rmarker commented Jan 26, 2024

PR has now been updated with this new approach.

owenca pushed a commit that referenced this issue Feb 4, 2024
…eakAfterReturnType. (#78011)

The RTBS_None option in Clang-format avoids breaking after a short
return type.
However, there was an issue with the behaviour in that it wouldn't take
the leading indentation of the line into account.
This meant that the behaviour wasn't applying when intended.

In order to address this situation without breaking the existing
formatting, RTBS_None has been deprecated.
In its place are two new options for AlwaysBreakAfterReturnType.
The option RTBS_Automatic will break after the return type based on
PenaltyReturnTypeOnItsOwnLine.
The option RTBS_ExceptShortType will take the leading indentation into
account and prevent breaking after short return types.

This allows the inconsistent behaviour of RTBS_None to be avoided and
users to decide whether they want to allow breaking after short return
types or not.

Resolves #78010
@rmarker
Copy link
Contributor Author

rmarker commented Feb 5, 2024

@owenca, now that the change is in, do you want me to look at renaming AlwaysBreakAfterReturnType to BreakAfterReturnType?
I assume that AlwaysBreakAfterReturnType would be deprecated similarly to AlwaysBreakAfterDefinitionReturnType.

agozillon pushed a commit to agozillon/llvm-project that referenced this issue Feb 5, 2024
…eakAfterReturnType. (llvm#78011)

The RTBS_None option in Clang-format avoids breaking after a short
return type.
However, there was an issue with the behaviour in that it wouldn't take
the leading indentation of the line into account.
This meant that the behaviour wasn't applying when intended.

In order to address this situation without breaking the existing
formatting, RTBS_None has been deprecated.
In its place are two new options for AlwaysBreakAfterReturnType.
The option RTBS_Automatic will break after the return type based on
PenaltyReturnTypeOnItsOwnLine.
The option RTBS_ExceptShortType will take the leading indentation into
account and prevent breaking after short return types.

This allows the inconsistent behaviour of RTBS_None to be avoided and
users to decide whether they want to allow breaking after short return
types or not.

Resolves llvm#78010
@owenca
Copy link
Contributor

owenca commented Feb 6, 2024

@owenca, now that the change is in, do you want me to look at renaming AlwaysBreakAfterReturnType to BreakAfterReturnType? I assume that AlwaysBreakAfterReturnType would be deprecated similarly to AlwaysBreakAfterDefinitionReturnType.

Yep. Thanks!

owenca pushed a commit that referenced this issue Feb 13, 2024
Changes the option to BreakAfterReturnType option, with a more relevant
name, deprecating and replacing AlwaysBreakAfterReturnType.
Following up on #78010.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants