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] Add SpacesInParensOption for filtering repeated parens #77522

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

Conversation

gedare
Copy link
Contributor

@gedare gedare commented Jan 9, 2024

The __attribute((specifier-list)) currently is formatted based on the SpacesInParensOptions.Other (previously, SpacesInParentheses). This change allows finer control over addition of spaces between the consecutive parens, and between the inner parens and the list of attribute specifiers.

Differential Revision: https://reviews.llvm.org/D155529

This is migrated from Phabricator, see more discussion there.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang-format labels Jan 9, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented Jan 9, 2024

@llvm/pr-subscribers-clang-format

@llvm/pr-subscribers-clang

Author: Gedare Bloom (gedare)

Changes

The __attribute((specifier-list)) currently is formatted based on the SpacesInParensOptions.Other (previously, SpacesInParentheses). This change allows finer control over addition of spaces between the consecutive parens, and between the inner parens and the list of attribute specifiers.

Differential Revision: https://reviews.llvm.org/D155529

This is migrated from Phabricator, see more discussion there. I will next provide an option for SpacesInParensOptions.Doubled to control (and override) injection of spaces within doubled parens (( )).


Full diff: https://github.com/llvm/llvm-project/pull/77522.diff

7 Files Affected:

  • (modified) clang/docs/ClangFormatStyleOptions.rst (+7)
  • (modified) clang/docs/ReleaseNotes.rst (+2)
  • (modified) clang/include/clang/Format/Format.h (+14-5)
  • (modified) clang/lib/Format/Format.cpp (+2)
  • (modified) clang/lib/Format/TokenAnnotator.cpp (+12-4)
  • (modified) clang/unittests/Format/ConfigParseTest.cpp (+9-4)
  • (modified) clang/unittests/Format/FormatTest.cpp (+7)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 3d42571e82d8a0..bfd393eb033459 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -5625,6 +5625,13 @@ the configuration (without a prefix: ``Auto``).
       InConditionalStatements: true
       Other: true
 
+  * ``bool InAttributeSpecifiers`` Put a space in parentheses of attribute specifiers.
+
+    .. code-block:: c++
+
+       true:                                  false:
+       __attribute__( ( noreturn ) )    vs.     __attribute__((noreturn))
+
   * ``bool InConditionalStatements`` Put a space in parentheses only inside conditional statements
     (``for/if/while/switch...``).
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ddeb1186d65ac8..4a23286c2b61a7 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -1079,6 +1079,8 @@ clang-format
 - Add ``ObjCPropertyAttributeOrder`` which can be used to sort ObjC property
   attributes (like ``nonatomic, strong, nullable``).
 - Add ``.clang-format-ignore`` files.
+- Add ``InAttributeSpecifiers`` style option to ``SpacesInParensOptions``
+  to control addition of spaces after the ``__attribute__`` keyword.
 
 libclang
 --------
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 8604dea689f937..c9f0617711157d 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -4515,6 +4515,12 @@ struct FormatStyle {
   ///     Other: true
   /// \endcode
   struct SpacesInParensCustom {
+    /// Put a space in parentheses of attribute specifiers.
+    /// \code
+    ///    true:                                  false:
+    ///    __attribute__( ( noreturn ) )    vs.     __attribute__((noreturn))
+    /// \endcode
+    bool InAttributeSpecifiers;
     /// Put a space in parentheses only inside conditional statements
     /// (``for/if/while/switch...``).
     /// \code
@@ -4548,17 +4554,20 @@ struct FormatStyle {
     bool Other;
 
     SpacesInParensCustom()
-        : InConditionalStatements(false), InCStyleCasts(false),
-          InEmptyParentheses(false), Other(false) {}
+        : InAttributeSpecifiers(false), InConditionalStatements(false),
+          InCStyleCasts(false), InEmptyParentheses(false), Other(false) {}
 
-    SpacesInParensCustom(bool InConditionalStatements, bool InCStyleCasts,
+    SpacesInParensCustom(bool InAttributeSpecifiers,
+                         bool InConditionalStatements, bool InCStyleCasts,
                          bool InEmptyParentheses, bool Other)
-        : InConditionalStatements(InConditionalStatements),
+        : InAttributeSpecifiers(InAttributeSpecifiers),
+          InConditionalStatements(InConditionalStatements),
           InCStyleCasts(InCStyleCasts), InEmptyParentheses(InEmptyParentheses),
           Other(Other) {}
 
     bool operator==(const SpacesInParensCustom &R) const {
-      return InConditionalStatements == R.InConditionalStatements &&
+      return InAttributeSpecifiers == R.InAttributeSpecifiers &&
+             InConditionalStatements == R.InConditionalStatements &&
              InCStyleCasts == R.InCStyleCasts &&
              InEmptyParentheses == R.InEmptyParentheses && Other == R.Other;
     }
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index f798d555bf9929..97a457f2733ade 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -753,6 +753,7 @@ template <> struct MappingTraits<FormatStyle::SpacesInLineComment> {
 
 template <> struct MappingTraits<FormatStyle::SpacesInParensCustom> {
   static void mapping(IO &IO, FormatStyle::SpacesInParensCustom &Spaces) {
+    IO.mapOptional("InAttributeSpecifiers", Spaces.InAttributeSpecifiers);
     IO.mapOptional("InCStyleCasts", Spaces.InCStyleCasts);
     IO.mapOptional("InConditionalStatements", Spaces.InConditionalStatements);
     IO.mapOptional("InEmptyParentheses", Spaces.InEmptyParentheses);
@@ -1191,6 +1192,7 @@ template <> struct MappingTraits<FormatStyle> {
       if (SpacesInParentheses) {
         // set all options except InCStyleCasts and InEmptyParentheses
         // to true for backward compatibility.
+        Style.SpacesInParensOptions.InAttributeSpecifiers = true;
         Style.SpacesInParensOptions.InConditionalStatements = true;
         Style.SpacesInParensOptions.InCStyleCasts =
             SpacesInCStyleCastParentheses;
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 8b43438c72dfe1..7dbc0d8f31819c 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -3999,10 +3999,18 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
   }
 
   if (Left.is(tok::l_paren) || Right.is(tok::r_paren)) {
-    return (Right.is(TT_CastRParen) ||
-            (Left.MatchingParen && Left.MatchingParen->is(TT_CastRParen)))
-               ? Style.SpacesInParensOptions.InCStyleCasts
-               : Style.SpacesInParensOptions.Other;
+    if (Right.is(TT_CastRParen) ||
+        (Left.MatchingParen && Left.MatchingParen->is(TT_CastRParen))) {
+      return Style.SpacesInParensOptions.InCStyleCasts;
+    }
+    const auto isAttributeParen = [](const FormatToken *Paren) {
+      return Paren && Paren->isOneOf(TT_AttributeLParen, TT_AttributeRParen);
+    };
+    if (isAttributeParen(&Left) || isAttributeParen(&Right) ||
+        isAttributeParen(Left.Previous) || isAttributeParen(Right.Next)) {
+      return Style.SpacesInParensOptions.InAttributeSpecifiers;
+    }
+    return Style.SpacesInParensOptions.Other;
   }
   if (Right.isOneOf(tok::semi, tok::comma))
     return false;
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index 0c9f68f303d865..d537be9a11a910 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -231,6 +231,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
   CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterIfMacros);
   CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterOverloadedOperator);
   CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, BeforeNonEmptyParentheses);
+  CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, InAttributeSpecifiers);
   CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, InCStyleCasts);
   CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, InConditionalStatements);
   CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, InEmptyParentheses);
@@ -626,19 +627,23 @@ TEST(ConfigParseTest, ParsesConfiguration) {
   Style.SpacesInParens = FormatStyle::SIPO_Never;
   Style.SpacesInParensOptions = {};
   CHECK_PARSE("SpacesInParentheses: true", SpacesInParensOptions,
-              FormatStyle::SpacesInParensCustom(true, false, false, true));
+              FormatStyle::SpacesInParensCustom(true, true, false, false,
+                  true));
   Style.SpacesInParens = FormatStyle::SIPO_Never;
   Style.SpacesInParensOptions = {};
   CHECK_PARSE("SpacesInConditionalStatement: true", SpacesInParensOptions,
-              FormatStyle::SpacesInParensCustom(true, false, false, false));
+              FormatStyle::SpacesInParensCustom(false, true, false, false,
+                  false));
   Style.SpacesInParens = FormatStyle::SIPO_Never;
   Style.SpacesInParensOptions = {};
   CHECK_PARSE("SpacesInCStyleCastParentheses: true", SpacesInParensOptions,
-              FormatStyle::SpacesInParensCustom(false, true, false, false));
+              FormatStyle::SpacesInParensCustom(false, false, true, false,
+                  false));
   Style.SpacesInParens = FormatStyle::SIPO_Never;
   Style.SpacesInParensOptions = {};
   CHECK_PARSE("SpaceInEmptyParentheses: true", SpacesInParensOptions,
-              FormatStyle::SpacesInParensCustom(false, false, true, false));
+              FormatStyle::SpacesInParensCustom(false, false, false, true,
+                  false));
   Style.SpacesInParens = FormatStyle::SIPO_Never;
   Style.SpacesInParensOptions = {};
 
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 25ef5c680af862..97f397410f0002 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -16741,6 +16741,7 @@ TEST_F(FormatTest, ConfigurableSpacesInParens) {
   Spaces.SpacesInParensOptions = {};
   Spaces.SpacesInParensOptions.Other = true;
   Spaces.SpacesInParensOptions.InConditionalStatements = true;
+  Spaces.SpacesInParensOptions.InAttributeSpecifiers = true;
   verifyFormat("do_something( ::globalVar );", Spaces);
   verifyFormat("call( x, y, z );", Spaces);
   verifyFormat("call();", Spaces);
@@ -16815,6 +16816,12 @@ TEST_F(FormatTest, ConfigurableSpacesInParens) {
   verifyFormat("void __attribute__((naked)) foo(int bar)", Spaces);
   verifyFormat("void f( ) __attribute__((asdf));", Spaces);
 
+  Spaces.SpacesInParensOptions.InAttributeSpecifiers = true;
+  verifyFormat("SomeType *__attribute__( ( attr ) ) *a = NULL;", Spaces);
+  verifyFormat("void __attribute__( ( naked ) ) foo(int bar)", Spaces);
+  verifyFormat("void f( ) __attribute__( ( asdf ) );", Spaces);
+  Spaces.SpacesInParensOptions.InAttributeSpecifiers = false;
+
   // Run the first set of tests again with:
   Spaces.SpaceAfterCStyleCast = true;
   verifyFormat("call(x, y, z);", Spaces);

@gedare
Copy link
Contributor Author

gedare commented Jan 9, 2024

I provide a sub-option to InAttributeSpecifiers that allows to control the injection of spaces within the (( x )) vs ( ( x ) ) cases. See the phabricator discussion for more.

@owenca
Copy link
Contributor

owenca commented Jan 11, 2024

The __attribute((specifier-list)) currently is formatted based on the SpacesInParensOptions.Other (previously, SpacesInParentheses). This change allows finer control over addition of spaces between the consecutive parens, and between the inner parens and the list of attribute specifiers.

Differential Revision: https://reviews.llvm.org/D155529

This is migrated from Phabricator, see more discussion there.

I expressed my opinion there:

I would have no problem if this new option is extended to handle all double parens, e.g. if (( i = j )), decltype(( x )), etc.

So I still prefer that we have a boolean suboption (e.g. ConsecutiveParentheses) that covers all double parens.

Copy link
Contributor

@mydeveloperday mydeveloperday left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@HazardyKnusperkeks HazardyKnusperkeks left a comment

Choose a reason for hiding this comment

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

But Owen's proposal isn't bad either.

@gedare
Copy link
Contributor Author

gedare commented Jan 11, 2024

The __attribute((specifier-list)) currently is formatted based on the SpacesInParensOptions.Other (previously, SpacesInParentheses). This change allows finer control over addition of spaces between the consecutive parens, and between the inner parens and the list of attribute specifiers.
Differential Revision: https://reviews.llvm.org/D155529
This is migrated from Phabricator, see more discussion there.

I expressed my opinion there:

I would have no problem if this new option is extended to handle all double parens, e.g. if (( i = j )), decltype(( x )), etc.

So I still prefer that we have a boolean suboption (e.g. ConsecutiveParentheses) that covers all double parens.

I will provide something shortly along these lines by duplicating the logic I used for this InAttributeSpecifiers to apply to the other suboptions of SpacesInParensOptions. Covering all double parens in a single sub-option is not precise enough for code bases that may want to have __attribute__(( x )) but also allow if ( ( x ) ).

@gedare gedare force-pushed the spacesinparens-attr-rebase-4 branch from 060ad65 to 79b4c34 Compare January 11, 2024 20:45
Copy link

github-actions bot commented Jan 11, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@gedare
Copy link
Contributor Author

gedare commented Jan 11, 2024

I'm still making changes on this. Not sure how to mark that in GH. Need to add test cases for double parens, and get it working for the newly added suboptions.

@gedare gedare changed the title [clang-format] Add SpaceInParensOption for __attribute__ keyword [clang-format] Add SpacesInParensOption for attributes and filtering for repeated parens Jan 12, 2024
@gedare
Copy link
Contributor Author

gedare commented Jan 12, 2024

The __attribute((specifier-list)) currently is formatted based on the SpacesInParensOptions.Other (previously, SpacesInParentheses). This change allows finer control over addition of spaces between the consecutive parens, and between the inner parens and the list of attribute specifiers.
Differential Revision: https://reviews.llvm.org/D155529
This is migrated from Phabricator, see more discussion there.

I expressed my opinion there:

I would have no problem if this new option is extended to handle all double parens, e.g. if (( i = j )), decltype(( x )), etc.

So I still prefer that we have a boolean suboption (e.g. ConsecutiveParentheses) that covers all double parens.

Please have a look, I have moved this PR more toward this direction providing fine-grained controls over double parens.

Someone will have to add the decltype understanding in order to make decltype(( x )) work without having to enable Other.NonConsecutive.

clang/lib/Format/TokenAnnotator.cpp Outdated Show resolved Hide resolved
clang/unittests/Format/FormatTest.cpp Outdated Show resolved Hide resolved
clang/unittests/Format/FormatTest.cpp Outdated Show resolved Hide resolved
clang/unittests/Format/FormatTest.cpp Outdated Show resolved Hide resolved
@owenca
Copy link
Contributor

owenca commented Jan 20, 2024

Covering all double parens in a single sub-option is not precise enough for code bases that may want to have __attribute__(( x )) but also allow if ( ( x ) ).

We should not go overboard with supporting all kinds of options/suboptions imaginable. I don't think we should support spaces between consecutive parens in the first place, which was likely a bug.

We can't just disregard the long-standing policy on adding new options. If anyone thinks that policy is outdated, they should go through the RFC process and get it updated.

@gedare
Copy link
Contributor Author

gedare commented Jan 21, 2024

Covering all double parens in a single sub-option is not precise enough for code bases that may want to have __attribute__(( x )) but also allow if ( ( x ) ).

We should not go overboard with supporting all kinds of options/suboptions imaginable. I don't think we should support spaces between consecutive parens in the first place, which was likely a bug.

Well, it seems to be far too late to go back and change the default behavior.

We can't just disregard the long-standing policy on adding new options. If anyone thinks that policy is outdated, they should go through the RFC process and get it updated.

I have no problem with this policy. I think it is a good policy. I can advocate for my position. My patches relate to the style maintenance of a widely-used real-time operating system. The code is self-hosted and mirrored on GitHub with a documented style guide. In addition, I have been and will continue to be willing to provide bug fix and other maintenance support to clang-format based on the changes submitted to support these style needs.

At the moment, I need to satisfy a particular style rule that requires me to add spaces inside of conditional and compound expressions. This support basically exists in clang-format but the compound expressions fall under the SpacesInParensOption.Other category. In order to control compound expressions separately from other uses of parens, it seems to be the easiest thing to explicitly define "everything else" and control the injection of spaces within them. Eventually, it results in a much more controlled and configurable way to manage whitespace.

If this is not desired, I will need to step back to consider my options further, and probably have to take this up with the other maintainers of my code base to see what, if any, concessions we can make based on what is allowed with clang-format. I'm trying to get to a point where we are able to use clang-format on our code base with enforced CI actions. It is still a ways to go before I get there.

@owenca
Copy link
Contributor

owenca commented Jan 21, 2024

The code is self-hosted and mirrored on GitHub with a documented style guide. In addition, I have been and will continue to be willing to provide bug fix and other maintenance support to clang-format based on the changes submitted to support these style needs.

At the moment, I need to satisfy a particular style rule that requires me to add spaces inside of conditional and compound expressions.

It seems the RTEMS style guide you linked above doesn't have any examples for conditional expressions and compound expressions, and by conditional expressions I assume it means conditionals of control statements rather than ternary conditional expressions.

Would __attribute__((noreturn)), if ((i = j)), decltype((x)), and while (((i + 1) * j - 2) * k > 3) be formatted as __attribute__(( noreturn )), if (( i = j )), decltype(( x )), and while ( ( ( i + 1 ) * j - 2 ) * k > 3 ), respectively?

@gedare
Copy link
Contributor Author

gedare commented Jan 21, 2024

The code is self-hosted and mirrored on GitHub with a documented style guide. In addition, I have been and will continue to be willing to provide bug fix and other maintenance support to clang-format based on the changes submitted to support these style needs.
At the moment, I need to satisfy a particular style rule that requires me to add spaces inside of conditional and compound expressions.

It seems the RTEMS style guide you linked above doesn't have any examples for conditional expressions and compound expressions, and by conditional expressions I assume it means conditionals of control statements rather than ternary conditional expressions.

Correct. Unfortunately, no one (myself) has taken the time to thoroughly document all examples. The existing style has been inherited from the original author of our code base, as these things tend to be for older projects.

Would __attribute__((noreturn)), if ((i = j)), decltype((x)), and while (((i + 1) * j - 2) * k > 3) be formatted as __attribute__(( noreturn )), if (( i = j )), decltype(( x )), and while ( ( ( i + 1 ) * j - 2 ) * k > 3 ), respectively?

Almost, it would also be if ( ( i = j ) ), Although repeated parens are typically removed. So a better example is if ( ( i = j ) && ( k > 3 ) )

We don't have decltype in our C code base. I can add support for it as an option if desired.

We also have cases such as __attribute__(( __aligned__( x ) ))

@owenca
Copy link
Contributor

owenca commented Jan 21, 2024

Would __attribute__((noreturn)), if ((i = j)), decltype((x)), and while (((i + 1) * j - 2) * k > 3) be formatted as __attribute__(( noreturn )), if (( i = j )), decltype(( x )), and while ( ( ( i + 1 ) * j - 2 ) * k > 3 ), respectively?

Almost, it would also be if ( ( i = j ) ), Although repeated parens are typically removed. So a better example is if ( ( i = j ) && ( k > 3 ) )

We don't have decltype in our C code base. I can add support for it as an option if desired.

We also have cases such as __attribute__(( __aligned__( x ) ))

I'm ok with all of the above except for if ( ( i = j ) ), which should be formatted as if (( i = j )) because the extra pair of parentheses is not superfluous. This is similar to decltype(( x )), and theRemoveParentheses option doesn't strip the extra pair for either.

It seems adding a boolean sub-option that targets double pairs of parentheses as I suggested before is feasible although a better name than ConsecutiveParentheses may be needed.

@gedare
Copy link
Contributor Author

gedare commented Jan 22, 2024

Would __attribute__((noreturn)), if ((i = j)), decltype((x)), and while (((i + 1) * j - 2) * k > 3) be formatted as __attribute__(( noreturn )), if (( i = j )), decltype(( x )), and while ( ( ( i + 1 ) * j - 2 ) * k > 3 ), respectively?

Almost, it would also be if ( ( i = j ) ), Although repeated parens are typically removed. So a better example is if ( ( i = j ) && ( k > 3 ) )
We don't have decltype in our C code base. I can add support for it as an option if desired.
We also have cases such as __attribute__(( __aligned__( x ) ))

I'm ok with all of the above except for if ( ( i = j ) ), which should be formatted as if (( i = j )) because the extra pair of parentheses is not superfluous. This is similar to decltype(( x )), and theRemoveParentheses option doesn't strip the extra pair for either.

Yes, good point.

It seems adding a boolean sub-option that targets double pairs of parentheses as I suggested before is feasible although a better name than ConsecutiveParentheses may be needed.

I'm fine to fix but request a concrete suggestion or proposal before I spend much more time on this. It might also work to suppress optionally the additional space on the first/last parenthesis pair, for these special "paired" parens. I think the way I already worked this patch is the right way to go to preserve backward compatibility while still allowing control over the repeated parentheses.

Looks like it is called MultipleParentheses in the RemoveParentheses option. I should rename to that.

@owenca
Copy link
Contributor

owenca commented Jan 22, 2024

It seems adding a boolean sub-option that targets double pairs of parentheses as I suggested before is feasible although a better name than ConsecutiveParentheses may be needed.

I'm fine to fix but request a concrete suggestion or proposal before I spend much more time on this. It might also work to suppress optionally the additional space on the first/last parenthesis pair, for these special "paired" parens. I think the way I already worked this patch is the right way to go to preserve backward compatibility while still allowing control over the repeated parentheses.

Looks like it is called MultipleParentheses in the RemoveParentheses option. I should rename to that.

My idea was:

  • Keep the existing boolean suboptions and add a new one. Let's call it ExceptDoubleParentheses for now.
  • If it's set to true, assuming all other suboptions are set to true, the above examples would be formatted as:
__attribute__(( noreturn ))
__attribute__(( __aligned__( x ) ))

if (( i = j ))
if ( ( i = j ) && ( k > 3 ) ) // not affected by ExceptDoubleParentheses

decltype(( x ))

while ( ( ( i + 1 ) * j - 2 ) * k > 3 ) // not affected by ExceptDoubleParentheses
  • If it's set to false, assuming all other suboptions are set to true, the above examples would be formatted as:
__attribute__( ( noreturn ) )
__attribute__( ( __aligned__( x ) ) )

if ( ( i = j ) )
if ( ( i = j ) && ( k > 3 ) ) // not affected by ExceptDoubleParentheses

decltype( ( x ) )

while ( ( ( i + 1 ) * j - 2 ) * k > 3 ) // not affected by ExceptDoubleParentheses

This way, we would avoid making all suboptions an enum and repeating NonConsecutive in every suboption.

@gedare
Copy link
Contributor Author

gedare commented Jan 22, 2024

It seems adding a boolean sub-option that targets double pairs of parentheses as I suggested before is feasible although a better name than ConsecutiveParentheses may be needed.

I'm fine to fix but request a concrete suggestion or proposal before I spend much more time on this. It might also work to suppress optionally the additional space on the first/last parenthesis pair, for these special "paired" parens. I think the way I already worked this patch is the right way to go to preserve backward compatibility while still allowing control over the repeated parentheses.
Looks like it is called MultipleParentheses in the RemoveParentheses option. I should rename to that.

My idea was:

  • Keep the existing boolean suboptions and add a new one. Let's call it ExceptDoubleParentheses for now.
  • If it's set to true, assuming all other suboptions are set to true, the above examples would be formatted as:
__attribute__(( noreturn ))
__attribute__(( __aligned__( x ) ))

if (( i = j ))
if ( ( i = j ) && ( k > 3 ) ) // not affected by ExceptDoubleParentheses

decltype(( x ))

while ( ( ( i + 1 ) * j - 2 ) * k > 3 ) // not affected by ExceptDoubleParentheses
  • If it's set to false, assuming all other suboptions are set to true, the above examples would be formatted as:
__attribute__( ( noreturn ) )
__attribute__( ( __aligned__( x ) ) )

if ( ( i = j ) )
if ( ( i = j ) && ( k > 3 ) ) // not affected by ExceptDoubleParentheses

decltype( ( x ) )

while ( ( ( i + 1 ) * j - 2 ) * k > 3 ) // not affected by ExceptDoubleParentheses

This way, we would avoid making all suboptions an enum and repeating NonConsecutive in every suboption.

OK, I think I understand. This would be greatly simplified if we can annotate the DoubleParenthesis situation inside of UnwrappedLineParser::parseParens.

@gedare gedare force-pushed the spacesinparens-attr-rebase-4 branch from e4b2d9b to d5a563b Compare January 23, 2024 05:12
@gedare
Copy link
Contributor Author

gedare commented Jan 23, 2024

  • Rebased to resolve conflict in ReleaseNotes.
  • Added ExceptDoubleParentheses as proposed by @owenca

This seems to work reasonably well for the cases I care about, as far as I can tell. It also works for a few additional cases I hadn't thought of that I picked up from the RemoveParentheses tests.

clang/include/clang/Format/Format.h Outdated Show resolved Hide resolved
clang/include/clang/Format/Format.h Show resolved Hide resolved
clang/lib/Format/TokenAnnotator.cpp Outdated Show resolved Hide resolved
clang/lib/Format/TokenAnnotator.cpp Outdated Show resolved Hide resolved
clang/lib/Format/TokenAnnotator.cpp Outdated Show resolved Hide resolved
@owenca
Copy link
Contributor

owenca commented Jan 26, 2024

This would be greatly simplified if we can annotate the DoubleParenthesis situation inside of UnwrappedLineParser::parseParens.

+1.

clang/include/clang/Format/Format.h Outdated Show resolved Hide resolved
clang/include/clang/Format/Format.h Outdated Show resolved Hide resolved
clang/include/clang/Format/Format.h Outdated Show resolved Hide resolved
@gedare
Copy link
Contributor Author

gedare commented Mar 30, 2024

I saw some comments on this. Looks like some were deleted maybe. This is still in my queue, but unclear when I'll get back to it.

@gedare gedare force-pushed the spacesinparens-attr-rebase-4 branch from d5a563b to a423067 Compare May 17, 2024 18:34
@gedare gedare changed the title [clang-format] Add SpacesInParensOption for attributes and filtering for repeated parens [clang-format] Add SpacesInParensOption for filtering repeated parens May 17, 2024
Copy link
Contributor

@owenca owenca 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 we can simply check if a sequence of two l_parens have a matching sequence of two r_parens and insert spaces only if ExceptDoubleParentheses is false. See #93439.

clang/unittests/Format/FormatTest.cpp Show resolved Hide resolved
gedare and others added 3 commits May 31, 2024 12:53
This change allows finer control over addition of spaces between a pair of
consecutive opening parentheses with a pair of closing parentheses.
@gedare gedare force-pushed the spacesinparens-attr-rebase-4 branch from 0c1bc54 to b76bafa Compare May 31, 2024 18:54
@gedare
Copy link
Contributor Author

gedare commented May 31, 2024

I had to rebase for merge conflicts, so I also cleaned up the commit history a bit. This seems to be working fine and tests pass.

Copy link
Contributor

@owenca owenca left a comment

Choose a reason for hiding this comment

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

Do we need all these new test cases? If yes, consider moving the test (and other ConfigurableSpacesIn... tests) to a separate file.

Comment on lines +4658 to +4660
/// Override any of the following options to prevent addition of space
/// between the first two parentheses in situations where a pair of
/// parentheses have been used.
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems unclear or imprecise.

Comment on lines +4700 to +4703
/// decltype( ( x ) ) decltype((x))
/// x = ( (int32)y ) x = ((int32))y
/// y = ( (int ( * )( int ))foo )( x ); y = ((int (*)(int))foo)(x);
/// __attribute__( ( noreturn ) ) __attribute__((noreturn))
Copy link
Contributor

Choose a reason for hiding this comment

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

These new examples are a little confusing IMO. I would just delete them.

@@ -1175,8 +1176,8 @@ template <> struct MappingTraits<FormatStyle> {
(SpacesInParentheses || SpaceInEmptyParentheses ||
SpacesInConditionalStatement || SpacesInCStyleCastParentheses)) {
if (SpacesInParentheses) {
// set all options except InCStyleCasts and InEmptyParentheses
// to true for backward compatibility.
// for backward compatibility.
Copy link
Contributor

Choose a reason for hiding this comment

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

Capitalize the first letter of "for".

@a1batross
Copy link

I saw some comments on this. Looks like some were deleted maybe. This is still in my queue, but unclear when I'll get back to it.

Yeah, sorry, I posted a comment here but decided it was a bit irrelevant. :)

But going to ask anyway then. Can this setting remove the space from repeated parentheses pattern like this?

foo( (void *)buf, size );

to:

foo((void *)buf, size );

So it does remove it on one side where the C-style cast parentheses, but doesn't on the other side?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category clang-format
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants