Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -212,6 +212,14 @@ NOTE: This is an experimental flag, that might go away or be renamed. Do not use this in config files, etc. Use at your own risk. +**ForEachMacros** (``std::vector``) + A list of macros that should be interpreted as foreach loops instead of as + function calls. + + For example, ``ForEachMacros: [BOOST_FOREACH, Q_FOREACH]`` tells + clang-format to treat ``BOOST_FOREACH`` and ``Q_FOREACH`` as loop control + statements. + **IndentCaseLabels** (``bool``) Indent case labels one level from the switch statement. Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -302,6 +302,18 @@ /// which should not be split into lines or otherwise changed. std::string CommentPragmas; + /// \brief An array of macros that should be interpreted as foreach loops + /// instead of as function calls. + /// + /// These are expected to be macros of the form: + /// \code + /// FOREACH(, ...) + /// + /// \endcode + /// + /// For example: BOOST_FOREACH. + std::vector ForEachMacros; + bool operator==(const FormatStyle &R) const { return AccessModifierOffset == R.AccessModifierOffset && ConstructorInitializerIndentWidth == @@ -358,7 +370,8 @@ SpaceBeforeParens == R.SpaceBeforeParens && SpaceBeforeAssignmentOperators == R.SpaceBeforeAssignmentOperators && ContinuationIndentWidth == R.ContinuationIndentWidth && - CommentPragmas == R.CommentPragmas; + CommentPragmas == R.CommentPragmas && + ForEachMacros == R.ForEachMacros; } }; Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -33,6 +33,8 @@ using clang::format::FormatStyle; +LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(std::string) + namespace llvm { namespace yaml { template <> struct ScalarEnumerationTraits { @@ -200,6 +202,7 @@ Style.SpaceBeforeAssignmentOperators); IO.mapOptional("ContinuationIndentWidth", Style.ContinuationIndentWidth); IO.mapOptional("CommentPragmas", Style.CommentPragmas); + IO.mapOptional("ForEachMacros", Style.ForEachMacros); // For backward compatibility. if (!IO.outputting()) { @@ -1131,6 +1134,10 @@ TrailingWhitespace(0), Lex(Lex), SourceMgr(SourceMgr), Style(Style), IdentTable(getFormattingLangOpts()), Encoding(Encoding) { Lex.SetKeepWhitespaceMode(true); + + for (auto I : Style.ForEachMacros) + ForEachMacros.push_back(&IdentTable.get(I)); + std::sort(ForEachMacros.begin(), ForEachMacros.end()); } ArrayRef lex() { @@ -1351,6 +1358,10 @@ Column = FormatTok->LastLineColumnWidth; } + FormatTok->IsForEachMacro = + std::binary_search(ForEachMacros.begin(), ForEachMacros.end(), + FormatTok->Tok.getIdentifierInfo()); + return FormatTok; } @@ -1366,6 +1377,7 @@ encoding::Encoding Encoding; llvm::SpecificBumpPtrAllocator Allocator; SmallVector Tokens; + SmallVector ForEachMacros; void readRawToken(FormatToken &Tok) { Lex.LexFromRawLexer(Tok.Tok); Index: lib/Format/FormatToken.h =================================================================== --- lib/Format/FormatToken.h +++ lib/Format/FormatToken.h @@ -104,7 +104,7 @@ SplitPenalty(0), LongestObjCSelectorName(0), FakeRParens(0), StartsBinaryExpression(false), EndsBinaryExpression(false), LastInChainOfCalls(false), PartOfMultiVariableDeclStmt(false), - MatchingParen(NULL), Previous(NULL), Next(NULL), + IsForEachMacro(false), MatchingParen(NULL), Previous(NULL), Next(NULL), Decision(FD_Unformatted), Finalized(false) {} /// \brief The \c Token. @@ -247,6 +247,9 @@ /// Only set if \c Type == \c TT_StartOfName. bool PartOfMultiVariableDeclStmt; + /// \brief Is this a for each macro? + bool IsForEachMacro; + bool is(tok::TokenKind Kind) const { return Tok.is(Kind); } bool isOneOf(tok::TokenKind K1, tok::TokenKind K2) const { Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -120,6 +120,10 @@ Contexts.back().IsExpression = false; } else if (Left->Previous && Left->Previous->is(tok::kw___attribute)) { Left->Type = TT_AttributeParen; + } else if (Left->Previous && Left->Previous->IsForEachMacro) { + // The first argument to a foreach macro is a declaration. + Contexts.back().IsForEachMacro = true; + Contexts.back().IsExpression = false; } if (StartsObjCMethodExpr) { @@ -464,6 +468,8 @@ Contexts.back().FirstStartOfName->PartOfMultiVariableDeclStmt = true; if (Contexts.back().InCtorInitializer) Tok->Type = TT_CtorInitializerComma; + if (Contexts.back().IsForEachMacro) + Contexts.back().IsExpression = true; break; default: break; @@ -625,7 +631,7 @@ ColonIsObjCMethodExpr(false), FirstObjCSelectorName(NULL), FirstStartOfName(NULL), IsExpression(IsExpression), CanBeExpression(true), InTemplateArgument(false), - InCtorInitializer(false), CaretFound(false) {} + InCtorInitializer(false), CaretFound(false), IsForEachMacro(false) {} tok::TokenKind ContextKind; unsigned BindingStrength; @@ -641,6 +647,7 @@ bool InTemplateArgument; bool InCtorInitializer; bool CaretFound; + bool IsForEachMacro; }; /// \brief Puts a new \c Context onto the stack \c Contexts for the lifetime @@ -1408,8 +1415,9 @@ Left.isOneOf(tok::kw_return, tok::kw_new, tok::kw_delete, tok::semi) || (Style.SpaceBeforeParens != FormatStyle::SBPO_Never && - Left.isOneOf(tok::kw_if, tok::kw_for, tok::kw_while, tok::kw_switch, - tok::kw_catch)) || + (Left.isOneOf(tok::kw_if, tok::kw_for, tok::kw_while, + tok::kw_switch, tok::kw_catch) || + Left.IsForEachMacro)) || (Style.SpaceBeforeParens == FormatStyle::SBPO_Always && Left.isOneOf(tok::identifier, tok::kw___attribute) && Line.Type != LT_PreprocessorDirective); Index: lib/Format/UnwrappedLineParser.h =================================================================== --- lib/Format/UnwrappedLineParser.h +++ lib/Format/UnwrappedLineParser.h @@ -85,7 +85,7 @@ void parseParens(); void parseSquare(); void parseIfThenElse(); - void parseForOrWhileLoop(); + void parseForOrWhileOrForEachLoop(); void parseDoWhile(); void parseLabel(); void parseCaseLabel(); Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -629,7 +629,7 @@ return; case tok::kw_for: case tok::kw_while: - parseForOrWhileLoop(); + parseForOrWhileOrForEachLoop(); return; case tok::kw_do: parseDoWhile(); @@ -656,6 +656,11 @@ } // In all other cases, parse the declaration. break; + case tok::identifier: + if (FormatTok->IsForEachMacro) { + parseForOrWhileOrForEachLoop(); + return; + } default: break; } @@ -1040,9 +1045,10 @@ // FIXME: Add error handling. } -void UnwrappedLineParser::parseForOrWhileLoop() { - assert((FormatTok->Tok.is(tok::kw_for) || FormatTok->Tok.is(tok::kw_while)) && - "'for' or 'while' expected"); +void UnwrappedLineParser::parseForOrWhileOrForEachLoop() { + assert((FormatTok->Tok.is(tok::kw_for) || FormatTok->Tok.is(tok::kw_while) || + FormatTok->IsForEachMacro) && + "'for' or 'while' or foreach macro expected"); nextToken(); if (FormatTok->Tok.is(tok::l_paren)) parseParens(); Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -7607,6 +7607,12 @@ FormatStyle::NI_Inner); CHECK_PARSE("NamespaceIndentation: All", NamespaceIndentation, FormatStyle::NI_All); + + std::vector BoostForeach = { "BOOST_FOREACH" }; + CHECK_PARSE("ForEachMacros: [BOOST_FOREACH]", ForEachMacros, BoostForeach); + std::vector BoostAndQForeach = { "BOOST_FOREACH", "Q_FOREACH" }; + CHECK_PARSE("ForEachMacros: [BOOST_FOREACH, Q_FOREACH]", ForEachMacros, + BoostAndQForeach); } TEST_F(FormatTest, ParsesConfigurationWithLanguages) {