Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -3688,6 +3688,85 @@ } } + * ``SBPO_Custom`` (in configuration: ``Custom``) + Configure each individual space before parentheses in + `SpaceBeforeParensFlags`. + + + +**SpaceBeforeParensFlags** (``SpaceBeforeParensCustom``) :versionbadge:`clang-format 14` + Control of individual space before parentheses.. + + If ``SpaceBeforeParens`` is set to ``SBPO_Custom``, use this to specify + how each individual space before parentheses case should be handled. + Otherwise, this is ignored. + + .. code-block:: yaml + + # Example of usage: + SpaceBeforeParens: Custom + SpaceBeforeParensFlags: + AfterFunctionDefinitionName: true + AfterControlStatements: true + + Nested configuration flags: + + + * ``bool AfterFunctionDeclarationName`` If ``true``, put a space between function declaration name and opening + parentheses. + + .. code-block:: c++ + + true: false: + void f (); vs. void f(); + + * ``bool AfterFunctionDefinitionName`` If ``true``, put a space between function definition name and opening + parentheses. + + .. code-block:: c++ + + true: false: + void f () {} vs. void f() {} + + * ``bool AfterControlStatements`` If ``true``, put space betwee control statement keywords + (for/if/while...) and opening parentheses. + + .. code-block:: c++ + + true: false: + if (...) {} vs. if(...) {} + + * ``bool AfterOperators`` If ``true``, put space between operators and opening parentheses. + + .. code-block:: c++ + + true: false: + A *a = new (...) A; vs. A *a = new(...) A; + + * ``bool AfterIfMacros`` If ``true``, put space between if macros and opening parentheses. + + .. code-block:: c++ + + true: false: + IF (...) vs. IF(...) + + + * ``bool AfterForeachMacros`` If ``true``, put space between foreach macros and opening parentheses. + + .. code-block:: c++ + + true: false: + FOREACH (...) vs. FOREACH(...) + + + * ``bool BeforeNonEmptyParentheses`` If ``true``, put a space before opening parentheses only if the + parentheses are not empty. + + .. code-block:: c++ + + true: false: + void f (int a); vs. void f(); + f (a); f(); **SpaceBeforeRangeBasedForLoopColon** (``Boolean``) :versionbadge:`clang-format 7` @@ -3697,7 +3776,7 @@ .. code-block:: c++ true: false: - for (auto v : values) {} vs. for(auto v: values) {} + for (auto v : values) {} vs. for (auto v: values) {} **SpaceBeforeSquareBrackets** (``Boolean``) :versionbadge:`clang-format 11` If ``true``, spaces will be before ``[``. Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -3313,18 +3313,113 @@ /// } /// } /// \endcode - SBPO_Always + SBPO_Always, + /// Configure each individual space before parentheses in + /// `SpaceBeforeParensFlags`. + SBPO_Custom }; /// Defines in which cases to put a space before opening parentheses. /// \version 3.5 SpaceBeforeParensOptions SpaceBeforeParens; + /// Precise control over the spacing before parentheses. + /// \code + /// # Should be declared this way: + /// SpaceBeforeParens: Custom + /// SpaceBeforeParensFlags: + /// AfterFunctionDefinitionName: true + /// AfterControlStatements: true + /// \endcode + struct SpaceBeforeParensCustom { + /// If ``true``, put a space between function declaration name and opening + /// parentheses. + /// \code + /// true: false: + /// void f (); vs. void f(); + /// \endcode + bool AfterFunctionDeclarationName; + /// If ``true``, put a space between function definition name and opening + /// parentheses. + /// \code + /// true: false: + /// void f () {} vs. void f() {} + /// \endcode + bool AfterFunctionDefinitionName; + /// If ``true``, put space betwee control statement keywords + /// (for/if/while...) and opening parentheses. + /// \code + /// true: false: + /// if (...) {} vs. if(...) {} + /// \endcode + bool AfterControlStatements; + /// If ``true``, put space between operators and opening parentheses. + /// \code + /// true: false: + /// A *a = new (...) A; vs. A *a = new(...) A; + /// \endcode + bool AfterOperators; + /// If ``true``, put space between if macros and opening parentheses. + /// \code + /// true: false: + /// IF (...) vs. IF(...) + /// + /// \endcode + bool AfterIfMacros; + /// If ``true``, put space between foreach macros and opening parentheses. + /// \code + /// true: false: + /// FOREACH (...) vs. FOREACH(...) + /// + /// \endcode + bool AfterForeachMacros; + /// If ``true``, put a space before opening parentheses only if the + /// parentheses are not empty. + /// \code + /// true: false: + /// void f (int a); vs. void f(); + /// f (a); f(); + /// \endcode + bool BeforeNonEmptyParentheses; + + SpaceBeforeParensCustom() + : AfterFunctionDeclarationName(false), + AfterFunctionDefinitionName(false), AfterControlStatements(false), + AfterOperators(false), AfterIfMacros(false), + AfterForeachMacros(false), BeforeNonEmptyParentheses(false) {} + + bool operator==(const SpaceBeforeParensCustom &Other) const { + return AfterFunctionDeclarationName == + Other.AfterFunctionDeclarationName && + AfterFunctionDefinitionName == Other.AfterFunctionDefinitionName && + AfterControlStatements == Other.AfterControlStatements && + AfterOperators == Other.AfterOperators && + AfterIfMacros == Other.AfterIfMacros && + AfterForeachMacros == Other.AfterForeachMacros && + BeforeNonEmptyParentheses == Other.BeforeNonEmptyParentheses; + } + }; + + /// Control of individual space before parentheses.. + /// + /// If ``SpaceBeforeParens`` is set to ``SBPO_Custom``, use this to specify + /// how each individual space before parentheses case should be handled. + /// Otherwise, this is ignored. + /// \code{.yaml} + /// # Example of usage: + /// SpaceBeforeParens: Custom + /// SpaceBeforeParensFlags: + /// AfterFunctionDefinitionName: true + /// AfterControlStatements: true + /// \endcode + /// \version 14 + SpaceBeforeParensCustom SpaceBeforeParensFlags; + /// If ``false``, spaces will be removed before range-based for loop /// colon. /// \code /// true: false: - /// for (auto v : values) {} vs. for(auto v: values) {} + /// for (auto v : values) {} vs. for (auto v: values) {} /// \endcode /// \version 7 bool SpaceBeforeRangeBasedForLoopColon; @@ -3713,6 +3808,7 @@ R.SpaceBeforeCtorInitializerColon && SpaceBeforeInheritanceColon == R.SpaceBeforeInheritanceColon && SpaceBeforeParens == R.SpaceBeforeParens && + SpaceBeforeParensFlags == R.SpaceBeforeParensFlags && SpaceAroundPointerQualifiers == R.SpaceAroundPointerQualifiers && SpaceBeforeRangeBasedForLoopColon == R.SpaceBeforeRangeBasedForLoopColon && Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -462,6 +462,7 @@ IO.enumCase(Value, "NonEmptyParentheses", FormatStyle::SBPO_NonEmptyParentheses); IO.enumCase(Value, "Always", FormatStyle::SBPO_Always); + IO.enumCase(Value, "Custom", FormatStyle::SBPO_Custom); // For backward compatibility. IO.enumCase(Value, "false", FormatStyle::SBPO_Never); @@ -787,6 +788,7 @@ IO.mapOptional("SpaceBeforeInheritanceColon", Style.SpaceBeforeInheritanceColon); IO.mapOptional("SpaceBeforeParens", Style.SpaceBeforeParens); + IO.mapOptional("SpaceBeforeParensFlags", Style.SpaceBeforeParensFlags); IO.mapOptional("SpaceAroundPointerQualifiers", Style.SpaceAroundPointerQualifiers); IO.mapOptional("SpaceBeforeRangeBasedForLoopColon", @@ -845,6 +847,21 @@ } }; +template <> struct MappingTraits { + static void mapping(IO &IO, FormatStyle::SpaceBeforeParensCustom &Spacing) { + IO.mapOptional("AfterFunctionDefinitionName", + Spacing.AfterFunctionDefinitionName); + IO.mapOptional("AfterFunctionDeclarationName", + Spacing.AfterFunctionDeclarationName); + IO.mapOptional("AfterControlStatements", Spacing.AfterControlStatements); + IO.mapOptional("AfterOperators", Spacing.AfterOperators); + IO.mapOptional("AfterIfMacros", Spacing.AfterIfMacros); + IO.mapOptional("AfterForeachMacros", Spacing.AfterForeachMacros); + IO.mapOptional("BeforeNonEmptyParentheses", + Spacing.BeforeNonEmptyParentheses); + } +}; + template <> struct MappingTraits { static void mapping(IO &IO, FormatStyle::RawStringFormat &Format) { IO.mapOptional("Language", Format.Language); @@ -939,10 +956,9 @@ llvm_unreachable("unexpected parse error"); } -static FormatStyle expandPresets(const FormatStyle &Style) { - if (Style.BreakBeforeBraces == FormatStyle::BS_Custom) - return Style; - FormatStyle Expanded = Style; +static void expandPresetsBraceWrapping(FormatStyle &Expanded) { + if (Expanded.BreakBeforeBraces == FormatStyle::BS_Custom) + return; Expanded.BraceWrapping = {/*AfterCaseLabel=*/false, /*AfterClass=*/false, /*AfterControlStatement=*/FormatStyle::BWACS_Never, @@ -961,7 +977,7 @@ /*SplitEmptyFunction=*/true, /*SplitEmptyRecord=*/true, /*SplitEmptyNamespace=*/true}; - switch (Style.BreakBeforeBraces) { + switch (Expanded.BreakBeforeBraces) { case FormatStyle::BS_Linux: Expanded.BraceWrapping.AfterClass = true; Expanded.BraceWrapping.AfterFunction = true; @@ -1042,7 +1058,35 @@ default: break; } - return Expanded; +} + +static void expandPresetsSpaceBeforeParens(FormatStyle &Expanded) { + if (Expanded.SpaceBeforeParens == FormatStyle::SBPO_Custom) + return; + // Reset all flags + Expanded.SpaceBeforeParensFlags = {}; + + switch (Expanded.SpaceBeforeParens) { + case FormatStyle::SBPO_Never: + break; + case FormatStyle::SBPO_ControlStatements: + Expanded.SpaceBeforeParensFlags.AfterControlStatements = true; + Expanded.SpaceBeforeParensFlags.AfterOperators = true; + Expanded.SpaceBeforeParensFlags.AfterIfMacros = true; + Expanded.SpaceBeforeParensFlags.AfterForeachMacros = true; + break; + case FormatStyle::SBPO_ControlStatementsExceptControlMacros: + Expanded.SpaceBeforeParensFlags.AfterControlStatements = true; + Expanded.SpaceBeforeParensFlags.AfterOperators = true; + break; + case FormatStyle::SBPO_NonEmptyParentheses: + Expanded.SpaceBeforeParensFlags.BeforeNonEmptyParentheses = true; + break; + case FormatStyle::SBPO_Always: + break; + default: + break; + } } FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { @@ -1174,6 +1218,10 @@ LLVMStyle.SpaceBeforeCtorInitializerColon = true; LLVMStyle.SpaceBeforeInheritanceColon = true; LLVMStyle.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; + LLVMStyle.SpaceBeforeParensFlags.AfterControlStatements = true; + LLVMStyle.SpaceBeforeParensFlags.AfterOperators = true; + LLVMStyle.SpaceBeforeParensFlags.AfterIfMacros = true; + LLVMStyle.SpaceBeforeParensFlags.AfterForeachMacros = true; LLVMStyle.SpaceBeforeRangeBasedForLoopColon = true; LLVMStyle.SpaceBeforeAssignmentOperators = true; LLVMStyle.SpaceBeforeCpp11BracedList = false; @@ -1649,8 +1697,11 @@ llvm::yaml::Output Output(Stream); // We use the same mapping method for input and output, so we need a non-const // reference here. - FormatStyle NonConstStyle = expandPresets(Style); + FormatStyle NonConstStyle = Style; + expandPresetsBraceWrapping(NonConstStyle); + expandPresetsSpaceBeforeParens(NonConstStyle); Output << NonConstStyle; + return Stream.str(); } @@ -2929,7 +2980,9 @@ ArrayRef Ranges, unsigned FirstStartColumn, unsigned NextStartColumn, unsigned LastStartColumn, StringRef FileName, FormattingAttemptStatus *Status) { - FormatStyle Expanded = expandPresets(Style); + FormatStyle Expanded = Style; + expandPresetsBraceWrapping(Expanded); + expandPresetsSpaceBeforeParens(Expanded); if (Expanded.DisableFormat) return {tooling::Replacements(), 0}; if (isLikelyXml(Code)) Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -2906,7 +2906,7 @@ bool TokenAnnotator::spaceRequiredBeforeParens(const FormatToken &Right) const { return Style.SpaceBeforeParens == FormatStyle::SBPO_Always || - (Style.SpaceBeforeParens == FormatStyle::SBPO_NonEmptyParentheses && + (Style.SpaceBeforeParensFlags.BeforeNonEmptyParentheses && Right.ParameterCount > 0); } @@ -3134,33 +3134,64 @@ // e.g. template [[nodiscard]] ... if (Left.is(TT_TemplateCloser) && Right.is(TT_AttributeSquare)) return true; + // Space before parentheses common for all languages if (Right.is(tok::l_paren)) { if ((Left.is(tok::r_paren) && Left.is(TT_AttributeParen)) || (Left.is(tok::r_square) && Left.is(TT_AttributeSquare))) return true; - if (Style.SpaceBeforeParens == - FormatStyle::SBPO_ControlStatementsExceptControlMacros && - Left.is(TT_ForEachMacro)) - return false; - if (Style.SpaceBeforeParens == - FormatStyle::SBPO_ControlStatementsExceptControlMacros && - Left.is(TT_IfMacro)) - return false; - return Line.Type == LT_ObjCDecl || Left.is(tok::semi) || - (Style.SpaceBeforeParens != FormatStyle::SBPO_Never && - (Left.isOneOf(tok::pp_elif, tok::kw_for, tok::kw_while, - tok::kw_switch, tok::kw_case, TT_ForEachMacro, - TT_ObjCForIn) || - Left.isIf(Line.Type != LT_PreprocessorDirective) || - (Left.isOneOf(tok::kw_try, Keywords.kw___except, tok::kw_catch, - tok::kw_new, tok::kw_delete) && - (!Left.Previous || Left.Previous->isNot(tok::period))))) || - (spaceRequiredBeforeParens(Right) && - (Left.is(tok::identifier) || Left.isFunctionLikeKeyword() || - Left.is(tok::r_paren) || Left.isSimpleTypeSpecifier() || - (Left.is(tok::r_square) && Left.MatchingParen && - Left.MatchingParen->is(TT_LambdaLSquare))) && - Line.Type != LT_PreprocessorDirective); + if (Left.is(TT_ForEachMacro)) + return (Style.SpaceBeforeParensFlags.AfterForeachMacros || + spaceRequiredBeforeParens(Right)); + if (Left.is(TT_IfMacro)) + return (Style.SpaceBeforeParensFlags.AfterIfMacros || + spaceRequiredBeforeParens(Right)); + if (Line.Type == LT_ObjCDecl) + return true; + if (Left.is(tok::semi)) + return true; + if (Left.isOneOf(tok::pp_elif, tok::kw_for, tok::kw_while, tok::kw_switch, + tok::kw_case, TT_ForEachMacro, TT_ObjCForIn)) + return Style.SpaceBeforeParensFlags.AfterControlStatements || + spaceRequiredBeforeParens(Right); + if ((Left.isOneOf(tok::kw_try, Keywords.kw___except, tok::kw_catch) && + (!Left.Previous || Left.Previous->isNot(tok::period)))) + return Style.SpaceBeforeParensFlags.AfterControlStatements || + spaceRequiredBeforeParens(Right); + if (Left.isOneOf(tok::kw_new, tok::kw_delete) && + (!Left.Previous || Left.Previous->isNot(tok::period))) + return Style.SpaceBeforeParensFlags.AfterOperators || + spaceRequiredBeforeParens(Right); + if (Left.isIf(Line.Type != LT_PreprocessorDirective)) + return Style.SpaceBeforeParensFlags.AfterControlStatements || + spaceRequiredBeforeParens(Right); + if (Line.MightBeFunctionDecl) { + // Find in the line if there was another set of parentheses, + // to add a space only before the first one + bool IsFirstLParen = true; + const FormatToken *Tok = Right.Previous; + while (Tok) { + if (Tok->is(tok::r_paren)) { + IsFirstLParen = false; + break; + } + Tok = Tok->Previous; + } + if (IsFirstLParen) { + if (Line.mightBeFunctionDefinition()) + return Style.SpaceBeforeParensFlags.AfterFunctionDefinitionName || + spaceRequiredBeforeParens(Right); + else + return Style.SpaceBeforeParensFlags.AfterFunctionDeclarationName || + spaceRequiredBeforeParens(Right); + } + } + if (Line.Type != LT_PreprocessorDirective && + (Left.is(tok::identifier) || Left.isFunctionLikeKeyword() || + Left.is(tok::r_paren) || Left.isSimpleTypeSpecifier() || + (Left.is(tok::r_square) && Left.MatchingParen && + Left.MatchingParen->is(TT_LambdaLSquare)))) + return spaceRequiredBeforeParens(Right); + return false; } if (Left.is(tok::at) && Right.Tok.getObjCKeywordID() != tok::objc_not_keyword) return false; @@ -3307,7 +3338,7 @@ if (Right.is(tok::l_paren)) if (Left.isOneOf(tok::kw_using, Keywords.kw_async, Keywords.kw_when, Keywords.kw_lock)) - return Style.SpaceBeforeParens == FormatStyle::SBPO_ControlStatements || + return Style.SpaceBeforeParensFlags.AfterControlStatements || spaceRequiredBeforeParens(Right); // space between method modifier and opening parenthesis of a tuple return @@ -3414,7 +3445,8 @@ if (Left.is(tok::r_square) && Right.is(tok::l_brace)) return true; if (Left.is(Keywords.kw_synchronized) && Right.is(tok::l_paren)) - return Style.SpaceBeforeParens != FormatStyle::SBPO_Never; + return Style.SpaceBeforeParensFlags.AfterControlStatements || + spaceRequiredBeforeParens(Right); if ((Left.isOneOf(tok::kw_static, tok::kw_public, tok::kw_private, tok::kw_protected) || Left.isOneOf(Keywords.kw_final, Keywords.kw_abstract, Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -14133,6 +14133,64 @@ verifyFormat("X A::operator++ (T);", SomeSpace); verifyFormat("int x = int (y);", SomeSpace); verifyFormat("auto lambda = []() { return 0; };", SomeSpace); + + FormatStyle SomeSpace2 = getLLVMStyle(); + SomeSpace2.SpaceBeforeParens = FormatStyle::SBPO_Custom; + SomeSpace2.SpaceBeforeParensFlags = {}; // Reset all flags to false + SomeSpace2.SpaceBeforeParensFlags.AfterControlStatements = true; + SomeSpace2.SpaceBeforeParensFlags.AfterFunctionDefinitionName = true; + + verifyFormat("int f();", SomeSpace2); + verifyFormat("void f (int a, T b) {\n" + " while (true)\n" + " continue;\n" + "}", + SomeSpace2); + verifyFormat("if (true)\n" + " f();\n" + "else if (true)\n" + " f();", + SomeSpace2); + verifyFormat("do {\n" + " do_something();\n" + "} while (something());", + SomeSpace2); + verifyFormat("switch (x) {\n" + "default:\n" + " break;\n" + "}", + SomeSpace2); + verifyFormat("A::A () : a(1) {}", SomeSpace2); + verifyFormat("void f() __attribute__((asdf));", SomeSpace2); + verifyFormat("*(&a + 1);\n" + "&((&a)[1]);\n" + "a[(b + c) * d];\n" + "(((a + 1) * 2) + 3) * 4;", + SomeSpace2); + verifyFormat("#define A(x) x", SomeSpace2); + verifyFormat("#define A (x) x", SomeSpace2); + verifyFormat("#if defined(x)\n" + "#endif", + SomeSpace2); + verifyFormat("auto i = std::make_unique(5);", SomeSpace2); + verifyFormat("size_t x = sizeof(x);", SomeSpace2); + verifyFormat("auto f(int x) -> decltype(x);", SomeSpace2); + verifyFormat("auto f(int x) -> typeof(x);", SomeSpace2); + verifyFormat("auto f(int x) -> _Atomic(x);", SomeSpace2); + verifyFormat("auto f(int x) -> __underlying_type(x);", SomeSpace2); + verifyFormat("int f(T x) noexcept(x.create());", SomeSpace2); + verifyFormat("alignas(128) char a[128];", SomeSpace2); + verifyFormat("size_t x = alignof(MyType);", SomeSpace2); + verifyFormat("static_assert(sizeof(char) == 1, \"Impossible!\");", + SomeSpace2); + verifyFormat("int f() throw(Deprecated);", SomeSpace2); + verifyFormat("typedef void (*cb)(int);", SomeSpace2); + verifyFormat("T A::operator()();", SomeSpace2); + verifyFormat("X A::operator++(T);", SomeSpace2); + verifyFormat("auto lambda = []() { return 0; };", SomeSpace2); + verifyFormat("int x = int(y);", SomeSpace2); + verifyFormat("M (std::size_t R, std::size_t C) : C(C), data(R) {}", + SomeSpace2); } TEST_F(FormatTest, SpaceAfterLogicalNot) { @@ -18631,6 +18689,8 @@ FormatStyle::SBPO_ControlStatementsExceptControlMacros); CHECK_PARSE("SpaceBeforeParens: NonEmptyParentheses", SpaceBeforeParens, FormatStyle::SBPO_NonEmptyParentheses); + CHECK_PARSE("SpaceBeforeParens: Custom", SpaceBeforeParens, + FormatStyle::SBPO_Custom); // For backward compatibility: CHECK_PARSE("SpaceAfterControlStatementKeyword: false", SpaceBeforeParens, FormatStyle::SBPO_Never);