From 54f040fff301a629f2ed032863408ed119789b0e Mon Sep 17 00:00:00 2001 From: Gedare Bloom Date: Tue, 2 Jul 2024 01:00:14 -0600 Subject: [PATCH] [clang-format] Add SpacesInParensOption for filtering repeated parens (#77522) 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. --------- Co-authored-by: Owen Pan --- clang/docs/ClangFormatStyleOptions.rst | 23 ++++- clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/Format/Format.h | 35 +++++-- clang/lib/Format/Format.cpp | 5 +- clang/lib/Format/TokenAnnotator.cpp | 9 ++ clang/unittests/Format/ConfigParseTest.cpp | 21 ++-- clang/unittests/Format/FormatTest.cpp | 109 +++++++++++++++++++++ 7 files changed, 182 insertions(+), 23 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 080cba90c4a8bf..6c2e6da5948478 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -6242,6 +6242,7 @@ the configuration (without a prefix: ``Auto``). # Example of usage: SpacesInParens: Custom SpacesInParensOptions: + ExceptDoubleParentheses: false InConditionalStatements: true InEmptyParentheses: true @@ -6254,9 +6255,22 @@ the configuration (without a prefix: ``Auto``). # Should be declared this way: SpacesInParens: Custom SpacesInParensOptions: + ExceptDoubleParentheses: false InConditionalStatements: true Other: true + * ``bool ExceptDoubleParentheses`` Override any of the following options to prevent addition of space + when both opening and closing parentheses use multiple parentheses. + + .. code-block:: c++ + + true: + __attribute__(( noreturn )) + __decltype__(( x )) + if (( a = b )) + false: + Uses the applicable option. + * ``bool InConditionalStatements`` Put a space in parentheses only inside conditional statements (``for/if/while/switch...``). @@ -6270,8 +6284,9 @@ the configuration (without a prefix: ``Auto``). .. code-block:: c++ - true: false: - x = ( int32 )y vs. x = (int32)y + true: false: + x = ( int32 )y vs. x = (int32)y + y = (( int (*)(int) )foo)(x); y = ((int (*)(int))foo)(x); * ``bool InEmptyParentheses`` Insert a space in empty parentheses, i.e. ``()``. @@ -6289,8 +6304,8 @@ the configuration (without a prefix: ``Auto``). .. code-block:: c++ - true: false: - t f( Deleted & ) & = delete; vs. t f(Deleted &) & = delete; + true: false: + t f( Deleted & ) & = delete; vs. t f(Deleted &) & = delete; .. _SpacesInParentheses: diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 90c2469c1c89a3..fd8d9ad34fb100 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -1130,6 +1130,9 @@ clang-format - Adds ``LeftWithLastLine`` suboption to ``AlignEscapedNewlines``. - Adds ``KeepEmptyLines`` option to deprecate ``KeepEmptyLinesAtEOF`` and ``KeepEmptyLinesAtTheStartOfBlocks``. +- Add ``ExceptDoubleParentheses`` sub-option for ``SpacesInParensOptions`` + to override addition of spaces between multiple, non-redundant parentheses + similar to the rules used for ``RemoveParentheses``. libclang -------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index efc2e450b723fa..c454ab2bc0ce29 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -4679,10 +4679,22 @@ struct FormatStyle { /// # Should be declared this way: /// SpacesInParens: Custom /// SpacesInParensOptions: + /// ExceptDoubleParentheses: false /// InConditionalStatements: true /// Other: true /// \endcode struct SpacesInParensCustom { + /// Override any of the following options to prevent addition of space + /// when both opening and closing parentheses use multiple parentheses. + /// \code + /// true: + /// __attribute__(( noreturn )) + /// __decltype__(( x )) + /// if (( a = b )) + /// \endcode + /// false: + /// Uses the applicable option. + bool ExceptDoubleParentheses; /// Put a space in parentheses only inside conditional statements /// (``for/if/while/switch...``). /// \code @@ -4693,8 +4705,9 @@ struct FormatStyle { bool InConditionalStatements; /// Put a space in C style casts. /// \code - /// true: false: - /// x = ( int32 )y vs. x = (int32)y + /// true: false: + /// x = ( int32 )y vs. x = (int32)y + /// y = (( int (*)(int) )foo)(x); y = ((int (*)(int))foo)(x); /// \endcode bool InCStyleCasts; /// Insert a space in empty parentheses, i.e. ``()``. @@ -4710,23 +4723,26 @@ struct FormatStyle { bool InEmptyParentheses; /// Put a space in parentheses not covered by preceding options. /// \code - /// true: false: - /// t f( Deleted & ) & = delete; vs. t f(Deleted &) & = delete; + /// true: false: + /// t f( Deleted & ) & = delete; vs. t f(Deleted &) & = delete; /// \endcode bool Other; SpacesInParensCustom() - : InConditionalStatements(false), InCStyleCasts(false), - InEmptyParentheses(false), Other(false) {} + : ExceptDoubleParentheses(false), InConditionalStatements(false), + InCStyleCasts(false), InEmptyParentheses(false), Other(false) {} - SpacesInParensCustom(bool InConditionalStatements, bool InCStyleCasts, + SpacesInParensCustom(bool ExceptDoubleParentheses, + bool InConditionalStatements, bool InCStyleCasts, bool InEmptyParentheses, bool Other) - : InConditionalStatements(InConditionalStatements), + : ExceptDoubleParentheses(ExceptDoubleParentheses), + InConditionalStatements(InConditionalStatements), InCStyleCasts(InCStyleCasts), InEmptyParentheses(InEmptyParentheses), Other(Other) {} bool operator==(const SpacesInParensCustom &R) const { - return InConditionalStatements == R.InConditionalStatements && + return ExceptDoubleParentheses == R.ExceptDoubleParentheses && + InConditionalStatements == R.InConditionalStatements && InCStyleCasts == R.InCStyleCasts && InEmptyParentheses == R.InEmptyParentheses && Other == R.Other; } @@ -4744,6 +4760,7 @@ struct FormatStyle { /// # Example of usage: /// SpacesInParens: Custom /// SpacesInParensOptions: + /// ExceptDoubleParentheses: false /// InConditionalStatements: true /// InEmptyParentheses: true /// \endcode diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 259ef1dd00e3f9..6a8883b77a7305 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -729,6 +729,7 @@ template <> struct MappingTraits { template <> struct MappingTraits { static void mapping(IO &IO, FormatStyle::SpacesInParensCustom &Spaces) { + IO.mapOptional("ExceptDoubleParentheses", Spaces.ExceptDoubleParentheses); IO.mapOptional("InCStyleCasts", Spaces.InCStyleCasts); IO.mapOptional("InConditionalStatements", Spaces.InConditionalStatements); IO.mapOptional("InEmptyParentheses", Spaces.InEmptyParentheses); @@ -1184,8 +1185,8 @@ template <> struct MappingTraits { (SpacesInParentheses || SpaceInEmptyParentheses || SpacesInConditionalStatement || SpacesInCStyleCastParentheses)) { if (SpacesInParentheses) { - // set all options except InCStyleCasts and InEmptyParentheses - // to true for backward compatibility. + // For backward compatibility. + Style.SpacesInParensOptions.ExceptDoubleParentheses = false; Style.SpacesInParensOptions.InConditionalStatements = true; Style.SpacesInParensOptions.InCStyleCasts = SpacesInCStyleCastParentheses; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 03082cd2742c8c..0fd0214d16615c 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -4361,6 +4361,15 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, Right.is(tok::r_brace) && Right.isNot(BK_Block))) { return Style.SpacesInParensOptions.InEmptyParentheses; } + if (Style.SpacesInParens == FormatStyle::SIPO_Custom && + Style.SpacesInParensOptions.ExceptDoubleParentheses && + Left.is(tok::r_paren) && Right.is(tok::r_paren)) { + auto *InnerLParen = Left.MatchingParen; + if (InnerLParen && InnerLParen->Previous == Right.MatchingParen) { + InnerLParen->SpacesRequiredBefore = 0; + return false; + } + } if (Style.SpacesInParensOptions.InConditionalStatements) { const FormatToken *LeftParen = nullptr; if (Left.is(tok::l_paren)) diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index 2466677a3740d3..cc044153b7c9be 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -240,6 +240,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) { CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterOverloadedOperator); CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterPlacementOperator); CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, BeforeNonEmptyParentheses); + CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, ExceptDoubleParentheses); CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, InCStyleCasts); CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, InConditionalStatements); CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, InEmptyParentheses); @@ -626,20 +627,24 @@ TEST(ConfigParseTest, ParsesConfiguration) { FormatStyle::SIPO_Custom); Style.SpacesInParens = FormatStyle::SIPO_Never; Style.SpacesInParensOptions = {}; - CHECK_PARSE("SpacesInParentheses: true", SpacesInParensOptions, - FormatStyle::SpacesInParensCustom(true, false, false, true)); + CHECK_PARSE( + "SpacesInParentheses: true", SpacesInParensOptions, + FormatStyle::SpacesInParensCustom(false, true, false, false, true)); Style.SpacesInParens = FormatStyle::SIPO_Never; Style.SpacesInParensOptions = {}; - CHECK_PARSE("SpacesInConditionalStatement: true", SpacesInParensOptions, - FormatStyle::SpacesInParensCustom(true, false, false, false)); + CHECK_PARSE( + "SpacesInConditionalStatement: true", SpacesInParensOptions, + 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)); + CHECK_PARSE( + "SpacesInCStyleCastParentheses: true", SpacesInParensOptions, + 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)); + CHECK_PARSE( + "SpaceInEmptyParentheses: true", SpacesInParensOptions, + 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 5276e79d759812..283843ad7ab472 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -17129,6 +17129,23 @@ TEST_F(FormatTest, ConfigurableSpacesInParens) { verifyFormat("SomeType *__attribute__((attr)) *a = NULL;", Spaces); verifyFormat("void __attribute__((naked)) foo(int bar)", Spaces); verifyFormat("void f() __attribute__((asdf));", Spaces); + verifyFormat("x = (int32)y;", Spaces); + verifyFormat("y = ((int (*)(int))foo)(x);", Spaces); + verifyFormat("decltype(x) y = 42;", Spaces); + verifyFormat("decltype((x)) y = z;", Spaces); + verifyFormat("decltype((foo())) a = foo();", Spaces); + verifyFormat("decltype((bar(10))) a = bar(11);", Spaces); + verifyFormat("if ((x - y) && (a ^ b))\n" + " f();", + Spaces); + verifyFormat("for (int i = 0; i < 10; i = (i + 1))\n" + " foo(i);", + Spaces); + verifyFormat("switch (x / (y + z)) {\n" + "default:\n" + " break;\n" + "}", + Spaces); Spaces.SpacesInParens = FormatStyle::SIPO_Custom; Spaces.SpacesInParensOptions = {}; @@ -17163,6 +17180,23 @@ TEST_F(FormatTest, ConfigurableSpacesInParens) { verifyFormat("SomeType *__attribute__( ( attr ) ) *a = NULL;", Spaces); verifyFormat("void __attribute__( ( naked ) ) foo( int bar )", Spaces); verifyFormat("void f() __attribute__( ( asdf ) );", Spaces); + verifyFormat("x = (int32)y;", Spaces); + verifyFormat("y = ( (int ( * )( int ))foo )( x );", Spaces); + verifyFormat("decltype( x ) y = 42;", Spaces); + verifyFormat("decltype( ( x ) ) y = z;", Spaces); + verifyFormat("decltype( ( foo() ) ) a = foo();", Spaces); + verifyFormat("decltype( ( bar( 10 ) ) ) a = bar( 11 );", Spaces); + verifyFormat("if ( ( x - y ) && ( a ^ b ) )\n" + " f();", + Spaces); + verifyFormat("for ( int i = 0; i < 10; i = ( i + 1 ) )\n" + " foo( i );", + Spaces); + verifyFormat("switch ( x / ( y + z ) ) {\n" + "default:\n" + " break;\n" + "}", + Spaces); Spaces.SpacesInParens = FormatStyle::SIPO_Custom; Spaces.SpacesInParensOptions = {}; @@ -17175,6 +17209,7 @@ TEST_F(FormatTest, ConfigurableSpacesInParens) { verifyFormat("#define AA(X) sizeof((( X * )NULL)->a)", Spaces); verifyFormat("my_int a = ( my_int )sizeof(int);", Spaces); verifyFormat("#define x (( int )-1)", Spaces); + verifyFormat("y = (( int (*)(int) )foo)(x);", Spaces); // Run the first set of tests again with: Spaces.SpacesInParens = FormatStyle::SIPO_Custom; @@ -17207,6 +17242,23 @@ TEST_F(FormatTest, ConfigurableSpacesInParens) { verifyFormat("SomeType *__attribute__((attr)) *a = NULL;", Spaces); verifyFormat("void __attribute__((naked)) foo(int bar)", Spaces); verifyFormat("void f( ) __attribute__((asdf));", Spaces); + verifyFormat("x = ( int32 )y;", Spaces); + verifyFormat("y = (( int (*)(int) )foo)(x);", Spaces); + verifyFormat("decltype(x) y = 42;", Spaces); + verifyFormat("decltype((x)) y = z;", Spaces); + verifyFormat("decltype((foo( ))) a = foo( );", Spaces); + verifyFormat("decltype((bar(10))) a = bar(11);", Spaces); + verifyFormat("if ((x - y) && (a ^ b))\n" + " f( );", + Spaces); + verifyFormat("for (int i = 0; i < 10; i = (i + 1))\n" + " foo(i);", + Spaces); + verifyFormat("switch (x / (y + z)) {\n" + "default:\n" + " break;\n" + "}", + Spaces); // Run the first set of tests again with: Spaces.SpaceAfterCStyleCast = true; @@ -17314,6 +17366,63 @@ TEST_F(FormatTest, ConfigurableSpacesInParens) { verifyFormat("size_t idx = (a->foo)(a - 1);", Spaces); verifyFormat("size_t idx = (*foo)(a - 1);", Spaces); verifyFormat("size_t idx = (*(foo))(a - 1);", Spaces); + + // Check ExceptDoubleParentheses spaces + Spaces.IndentWidth = 2; + Spaces.SpacesInParens = FormatStyle::SIPO_Custom; + Spaces.SpacesInParensOptions = {}; + Spaces.SpacesInParensOptions.Other = true; + Spaces.SpacesInParensOptions.ExceptDoubleParentheses = true; + verifyFormat("SomeType *__attribute__(( attr )) *a = NULL;", Spaces); + verifyFormat("void __attribute__(( naked )) foo( int bar )", Spaces); + verifyFormat("void f() __attribute__(( asdf ));", Spaces); + verifyFormat("__attribute__(( __aligned__( x ) )) z;", Spaces); + verifyFormat("int x __attribute__(( aligned( 16 ) )) = 0;", Spaces); + verifyFormat("class __declspec( dllimport ) X {};", Spaces); + verifyFormat("class __declspec(( dllimport )) X {};", Spaces); + verifyFormat("int x = ( ( a - 1 ) * 3 );", Spaces); + verifyFormat("int x = ( 3 * ( a - 1 ) );", Spaces); + verifyFormat("decltype( x ) y = 42;", Spaces); + verifyFormat("decltype(( bar( 10 ) )) a = bar( 11 );", Spaces); + verifyFormat("if (( i = j ))\n" + " do_something( i );", + Spaces); + + Spaces.SpacesInParens = FormatStyle::SIPO_Custom; + Spaces.SpacesInParensOptions = {}; + Spaces.SpacesInParensOptions.InConditionalStatements = true; + Spaces.SpacesInParensOptions.ExceptDoubleParentheses = true; + verifyFormat("while ( (bool)1 )\n" + " continue;", + Spaces); + verifyFormat("while ((i = j))\n" + " continue;", + Spaces); + verifyFormat("do {\n" + " do_something((int)i);\n" + "} while ( something() );", + Spaces); + verifyFormat("do {\n" + " do_something((int)i);\n" + "} while ((i = i + 1));", + Spaces); + verifyFormat("if ( (x - y) && (a ^ b) )\n" + " f();", + Spaces); + verifyFormat("if ((i = j))\n" + " do_something(i);", + Spaces); + verifyFormat("for ( int i = 0; i < 10; i = (i + 1) )\n" + " foo(i);", + Spaces); + verifyFormat("switch ( x / (y + z) ) {\n" + "default:\n" + " break;\n" + "}", + Spaces); + verifyFormat("if constexpr ((a = b))\n" + " c;", + Spaces); } TEST_F(FormatTest, ConfigurableSpacesInSquareBrackets) {