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 @@ -299,6 +299,18 @@ /// which should not be split into lines or otherwise changed. std::string CommentPragmas; + /// \brief A list of macros that should be interpreted as foreach loops + /// instead of as function calls. + /// + /// These are 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 == @@ -353,7 +365,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 { @@ -198,6 +200,7 @@ Style.SpaceBeforeAssignmentOperators); IO.mapOptional("ContinuationIndentWidth", Style.ContinuationIndentWidth); IO.mapOptional("CommentPragmas", Style.CommentPragmas); + IO.mapOptional("ForEachMacros", Style.ForEachMacros); // For backward compatibility. if (!IO.outputting()) { Index: lib/Format/FormatToken.h =================================================================== --- lib/Format/FormatToken.h +++ lib/Format/FormatToken.h @@ -333,6 +333,8 @@ return is(tok::comment) && (!Next || Next->NewlinesBefore > 0); } + bool isForEachMacro(const FormatStyle &Style) const; + prec::Level getPrecedence() const { return getBinOpPrecedence(Tok.getKind(), true, true); } Index: lib/Format/FormatToken.cpp =================================================================== --- lib/Format/FormatToken.cpp +++ lib/Format/FormatToken.cpp @@ -15,6 +15,7 @@ #include "FormatToken.h" #include "ContinuationIndenter.h" +#include "clang/Basic/IdentifierTable.h" #include "clang/Format/Format.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Debug.h" @@ -52,6 +53,23 @@ } } +bool FormatToken::isForEachMacro(const FormatStyle &Style) const { + IdentifierInfo *Info = Tok.getIdentifierInfo(); + if (!Info) + return false; + + StringRef Name = Info->getName(); + + for (std::vector::const_iterator I = Style.ForEachMacros.begin(), + E = Style.ForEachMacros.end(); + I != E; ++I) { + if (Name == *I) + return true; + } + + return false; +} + TokenRole::~TokenRole() {} void TokenRole::precomputeFormattingInfos(const FormatToken *Token) {} Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -111,6 +111,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(Style)) { + // The first argument to a foreach macro is a declaration. + Contexts.back().IsForEachMacro = true; + Contexts.back().IsExpression = false; } if (StartsObjCMethodExpr) { @@ -447,6 +451,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; @@ -602,7 +608,8 @@ ColonIsForRangeExpr(false), ColonIsDictLiteral(false), ColonIsObjCMethodExpr(false), FirstObjCSelectorName(NULL), FirstStartOfName(NULL), IsExpression(IsExpression), - CanBeExpression(true), InCtorInitializer(false), CaretFound(false) {} + CanBeExpression(true), InCtorInitializer(false), CaretFound(false), + IsForEachMacro(false) {} tok::TokenKind ContextKind; unsigned BindingStrength; @@ -617,6 +624,7 @@ bool CanBeExpression; bool InCtorInitializer; bool CaretFound; + bool IsForEachMacro; }; /// \brief Puts a new \c Context onto the stack \c Contexts for the lifetime @@ -1352,8 +1360,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))) || (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 @@ -628,7 +628,7 @@ return; case tok::kw_for: case tok::kw_while: - parseForOrWhileLoop(); + parseForOrWhileOrForEachLoop(); return; case tok::kw_do: parseDoWhile(); @@ -653,6 +653,12 @@ return; } } + case tok::identifier: + if (FormatTok->isForEachMacro(Style)) { + parseForOrWhileOrForEachLoop(); + return; + } + // In all other cases, parse the declaration. break; default: @@ -1031,9 +1037,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(Style)) && + "'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 @@ -7476,6 +7476,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) {