diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2287,6 +2287,9 @@ int a[ 5 ]; vs. int a[5]; std::unique_ptr foo() {} // Won't be affected +**SpacesInConditionalStatement** (``bool``) + If ``true``, spaces will be inserted around if/for/while (and similar) conditions. + **Standard** (``LanguageStandard``) Format compatible with this standard, e.g. use ``A >`` instead of ``A>`` for ``LS_Cpp03``. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1945,6 +1945,15 @@ /// \endcode bool SpacesInSquareBrackets; + /// If ``true``, spaces will be inserted around if/for/switch/while + /// conditions. + /// \code + /// true: false: + /// if ( a ) { ... } vs. if (a) { ... } + /// while ( i < 5 ) { ... } while (i < 5) { ... } + /// \endcode + bool SpacesInConditionalStatement; + /// Supported language standards. enum LanguageStandard { /// Use C++03-compatible syntax. @@ -2081,6 +2090,7 @@ SpacesInCStyleCastParentheses == R.SpacesInCStyleCastParentheses && SpacesInParentheses == R.SpacesInParentheses && SpacesInSquareBrackets == R.SpacesInSquareBrackets && + SpacesInConditionalStatement == R.SpacesInConditionalStatement && Standard == R.Standard && TabWidth == R.TabWidth && StatementMacros == R.StatementMacros && UseTab == R.UseTab && TypenameMacros == R.TypenameMacros; diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -511,6 +511,7 @@ IO.mapOptional("SpacesBeforeTrailingComments", Style.SpacesBeforeTrailingComments); IO.mapOptional("SpacesInAngles", Style.SpacesInAngles); + IO.mapOptional("SpacesInConditionalStatement", Style.SpacesInConditionalStatement); IO.mapOptional("SpacesInContainerLiterals", Style.SpacesInContainerLiterals); IO.mapOptional("SpacesInCStyleCastParentheses", @@ -775,6 +776,7 @@ LLVMStyle.SpaceBeforeAssignmentOperators = true; LLVMStyle.SpaceBeforeCpp11BracedList = false; LLVMStyle.SpacesInAngles = false; + LLVMStyle.SpacesInConditionalStatement = false; LLVMStyle.PenaltyBreakAssignment = prec::Assignment; LLVMStyle.PenaltyBreakComment = 300; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -2489,6 +2489,13 @@ Right.ParameterCount > 0); } +/// Returns \c true if the token is followed by a boolean condition, \c false +/// otherwise. +static bool isKeywordWithCondition(const FormatToken &Tok) { + return Tok.isOneOf(tok::kw_if, tok::kw_for, tok::kw_while, tok::kw_switch, + tok::kw_constexpr); +}; + bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, const FormatToken &Left, const FormatToken &Right) { @@ -2505,6 +2512,16 @@ return Right.is(tok::hash); if (Left.is(tok::l_paren) && Right.is(tok::r_paren)) return Style.SpaceInEmptyParentheses; + if (Style.SpacesInConditionalStatement) { + if (Left.is(tok::l_paren) && Left.Previous && + isKeywordWithCondition(*Left.Previous) ) + return true; + if (Right.is(tok::r_paren) && + Right.MatchingParen && + Right.MatchingParen->Previous && + isKeywordWithCondition(*Right.MatchingParen->Previous)) + return true; + } if (Left.is(tok::l_paren) || Right.is(tok::r_paren)) return (Right.is(TT_CastRParen) || (Left.MatchingParen && Left.MatchingParen->is(TT_CastRParen))) @@ -2903,7 +2920,8 @@ // The identifier might actually be a macro name such as ALWAYS_INLINE. If // this turns out to be too lenient, add analysis of the identifier itself. return Right.WhitespaceRange.getBegin() != Right.WhitespaceRange.getEnd(); - if (Right.is(tok::coloncolon) && !Left.isOneOf(tok::l_brace, tok::comment)) + if (Right.is(tok::coloncolon) && + !Left.isOneOf(tok::l_brace, tok::comment, tok::l_paren)) return (Left.is(TT_TemplateOpener) && Style.Standard < FormatStyle::LS_Cpp11) || !(Left.isOneOf(tok::l_paren, tok::r_paren, tok::l_square, diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -12165,6 +12165,7 @@ CHECK_PARSE_BOOL(SpacesInParentheses); CHECK_PARSE_BOOL(SpacesInSquareBrackets); CHECK_PARSE_BOOL(SpacesInAngles); + CHECK_PARSE_BOOL(SpacesInConditionalStatement); CHECK_PARSE_BOOL(SpaceInEmptyBlock); CHECK_PARSE_BOOL(SpaceInEmptyParentheses); CHECK_PARSE_BOOL(SpacesInContainerLiterals); @@ -14373,6 +14374,23 @@ verifyFormat("auto lambda = [&a = a]() { a = 2; };", AlignStyle); } +TEST_F(FormatTest, SpacesInConditionalStatement) { + FormatStyle Spaces = getLLVMStyle(); + Spaces.SpacesInConditionalStatement = true; + verifyFormat("for ( int i = 0; i; i++ )\n continue;", Spaces); + verifyFormat("if ( !a )\n return;", Spaces); + verifyFormat("if ( a )\n return;", Spaces); + verifyFormat("if constexpr ( a )\n return;", Spaces); + verifyFormat("switch ( a )\ncase 1:\n return;", Spaces); + verifyFormat("while ( a )\n return;", Spaces); + verifyFormat("while ( (a && b) )\n return;", Spaces); + verifyFormat("do {\n} while ( 1 != 0 );", Spaces); + + // Check that space on the left of "::" is inserted as expected at beginning + // of condition. + verifyFormat("while ( ::func() )\n return;", Spaces); +} + } // end namespace } // end namespace format } // end namespace clang