Index: docs/ClangFormatStyleOptions.rst =================================================================== --- docs/ClangFormatStyleOptions.rst +++ docs/ClangFormatStyleOptions.rst @@ -212,6 +212,17 @@ 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::string``) + A regular expression that describes macros that should be interpreted as + foreach loops instead of as function calls. + + These are expected to be macros of the form: + + .. code-block:: c++ + + FOREACH(, ...) + + **IndentCaseLabels** (``bool``) Indent case labels one level from the switch statement. @@ -300,13 +311,16 @@ * ``SBPO_ControlStatements`` (in configuration: ``ControlStatements``) Put a space before opening parentheses only after control statement keywords (``for/if/while...``). + * ``SBPO_ControlStatementsAndForEachMacros`` (in configuration: + ``ControlStatementsAndForEachMacros``) + The same as ``ControlStatements``, but spaces are also added after + identifiers matched by ``ForEachMacros``. * ``SBPO_Always`` (in configuration: ``Always``) Always put a space before opening parentheses, except when it's prohibited by the syntax rules (in function-like macro definitions) or when determined by other style rules (after unary operators, opening parentheses, etc.) - **SpaceInEmptyParentheses** (``bool``) If ``true``, spaces may be inserted into '()'. Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -279,6 +279,9 @@ /// Put a space before opening parentheses only after control statement /// keywords (for/if/while...). SBPO_ControlStatements, + /// The same as SBPO_ControlStatements, but spaces are also added after + /// foreach macros. + SBPO_ControlStatementsAndForEachMacros, /// Always put a space before opening parentheses, except when it's /// prohibited by the syntax rules (in function-like macro definitions) or /// when determined by other style rules (after unary operators, opening @@ -299,6 +302,18 @@ /// which should not be split into lines or otherwise changed. std::string CommentPragmas; + /// \brief A regular expression that describes 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::string ForEachMacros; + bool operator==(const FormatStyle &R) const { return AccessModifierOffset == R.AccessModifierOffset && ConstructorInitializerIndentWidth == @@ -353,7 +368,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 @@ -27,6 +27,7 @@ #include "llvm/Support/Allocator.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Regex.h" #include "llvm/Support/YAMLTraits.h" #include #include @@ -90,6 +91,8 @@ IO.enumCase(Value, "Never", FormatStyle::SBPO_Never); IO.enumCase(Value, "ControlStatements", FormatStyle::SBPO_ControlStatements); + IO.enumCase(Value, "ControlStatementsAndForEachMacros", + FormatStyle::SBPO_ControlStatementsAndForEachMacros); IO.enumCase(Value, "Always", FormatStyle::SBPO_Always); // For backward compatibility. @@ -198,6 +201,7 @@ Style.SpaceBeforeAssignmentOperators); IO.mapOptional("ContinuationIndentWidth", Style.ContinuationIndentWidth); IO.mapOptional("CommentPragmas", Style.CommentPragmas); + IO.mapOptional("ForEachMacros", Style.ForEachMacros); // For backward compatibility. if (!IO.outputting()) { @@ -1390,7 +1394,8 @@ : Style(Style), Lex(Lex), SourceMgr(SourceMgr), Whitespaces(SourceMgr, Style, inputUsesCRLF(Lex.getBuffer())), Ranges(Ranges.begin(), Ranges.end()), UnwrappedLines(1), - Encoding(encoding::detectEncoding(Lex.getBuffer())) { + Encoding(encoding::detectEncoding(Lex.getBuffer())), + ForEachMacrosRegex(Style.ForEachMacros) { DEBUG(llvm::dbgs() << "File encoding: " << (Encoding == encoding::Encoding_UTF8 ? "UTF8" : "unknown") @@ -1403,7 +1408,7 @@ tooling::Replacements Result; FormatTokenLexer Tokens(Lex, SourceMgr, Style, Encoding); - UnwrappedLineParser Parser(Style, Tokens.lex(), *this); + UnwrappedLineParser Parser(Style, Tokens.lex(), ForEachMacrosRegex, *this); bool StructuralError = Parser.parse(); assert(UnwrappedLines.rbegin()->empty()); for (unsigned Run = 0, RunE = UnwrappedLines.size(); Run + 1 != RunE; @@ -1434,7 +1439,8 @@ tooling::Replacements format(SmallVectorImpl &AnnotatedLines, bool StructuralError, FormatTokenLexer &Tokens) { - TokenAnnotator Annotator(Style, Tokens.getIdentTable().get("in")); + TokenAnnotator Annotator(Style, Tokens.getIdentTable().get("in"), + ForEachMacrosRegex); for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) { Annotator.annotate(*AnnotatedLines[i]); } @@ -1664,6 +1670,7 @@ encoding::Encoding Encoding; bool BinPackInconclusiveFunctions; + llvm::Regex ForEachMacrosRegex; }; } // end anonymous namespace Index: lib/Format/FormatToken.h =================================================================== --- lib/Format/FormatToken.h +++ lib/Format/FormatToken.h @@ -21,6 +21,10 @@ #include "clang/Lex/Lexer.h" #include "llvm/ADT/OwningPtr.h" +namespace llvm { +class Regex; +} + namespace clang { namespace format { @@ -333,6 +337,8 @@ return is(tok::comment) && (!Next || Next->NewlinesBefore > 0); } + bool isForEachMacro(llvm::Regex &ForEachMacrosRegex) const; + prec::Level getPrecedence() const { return getBinOpPrecedence(Tok.getKind(), true, true); } Index: lib/Format/FormatToken.cpp =================================================================== --- lib/Format/FormatToken.cpp +++ lib/Format/FormatToken.cpp @@ -18,6 +18,7 @@ #include "clang/Format/Format.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Regex.h" namespace clang { namespace format { @@ -52,6 +53,10 @@ } } +bool FormatToken::isForEachMacro(llvm::Regex &ForEachMacrosRegex) const { + return is(tok::identifier) && ForEachMacrosRegex.match(TokenText); +} + TokenRole::~TokenRole() {} void TokenRole::precomputeFormattingInfos(const FormatToken *Token) {} Index: lib/Format/TokenAnnotator.h =================================================================== --- lib/Format/TokenAnnotator.h +++ lib/Format/TokenAnnotator.h @@ -109,8 +109,10 @@ /// \c UnwrappedLine. class TokenAnnotator { public: - TokenAnnotator(const FormatStyle &Style, IdentifierInfo &Ident_in) - : Style(Style), Ident_in(Ident_in) {} + TokenAnnotator(const FormatStyle &Style, IdentifierInfo &Ident_in, + llvm::Regex &ForEachMacrosRegex) + : Style(Style), Ident_in(Ident_in), + ForEachMacrosRegex(ForEachMacrosRegex) {} /// \brief Adapts the indent levels of comment lines to the indent of the /// subsequent line. @@ -142,6 +144,7 @@ // Contextual keywords: IdentifierInfo &Ident_in; + llvm::Regex &ForEachMacrosRegex; }; } // end namespace format Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -30,9 +30,10 @@ class AnnotatingParser { public: AnnotatingParser(const FormatStyle &Style, AnnotatedLine &Line, - IdentifierInfo &Ident_in) + IdentifierInfo &Ident_in, llvm::Regex &ForEachMacrosRegex) : Style(Style), Line(Line), CurrentToken(Line.First), - KeywordVirtualFound(false), AutoFound(false), Ident_in(Ident_in) { + KeywordVirtualFound(false), AutoFound(false), Ident_in(Ident_in), + ForEachMacrosRegex(ForEachMacrosRegex) { Contexts.push_back(Context(tok::unknown, 1, /*IsExpression=*/false)); } @@ -111,6 +112,11 @@ 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(ForEachMacrosRegex)) { + // The first argument to a foreach macro is a declaration. + Contexts.back().IsForEachMacro = true; + Contexts.back().IsExpression = false; } if (StartsObjCMethodExpr) { @@ -447,6 +453,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 +610,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 +626,7 @@ bool CanBeExpression; bool InCtorInitializer; bool CaretFound; + bool IsForEachMacro; }; /// \brief Puts a new \c Context onto the stack \c Contexts for the lifetime @@ -887,6 +897,7 @@ bool KeywordVirtualFound; bool AutoFound; IdentifierInfo &Ident_in; + llvm::Regex &ForEachMacrosRegex; }; static int PrecedenceUnaryOperator = prec::PointerToMember + 1; @@ -1071,7 +1082,7 @@ I != E; ++I) { annotate(**I); } - AnnotatingParser Parser(Style, Line, Ident_in); + AnnotatingParser Parser(Style, Line, Ident_in, ForEachMacrosRegex); Line.Type = Parser.parseLine(); if (Line.Type == LT_Invalid) return; @@ -1354,6 +1365,9 @@ (Style.SpaceBeforeParens != FormatStyle::SBPO_Never && Left.isOneOf(tok::kw_if, tok::kw_for, tok::kw_while, tok::kw_switch, tok::kw_catch)) || + (Style.SpaceBeforeParens == + FormatStyle::SBPO_ControlStatementsAndForEachMacros && + Left.isForEachMacro(ForEachMacrosRegex)) || (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 @@ -60,6 +60,7 @@ class UnwrappedLineParser { public: UnwrappedLineParser(const FormatStyle &Style, ArrayRef Tokens, + llvm::Regex &ForEachMacrosRegex, UnwrappedLineConsumer &Callback); /// Returns true in case of a structural error. @@ -85,7 +86,7 @@ void parseParens(); void parseSquare(); void parseIfThenElse(); - void parseForOrWhileLoop(); + void parseForOrWhileOrForEachLoop(); void parseDoWhile(); void parseLabel(); void parseCaseLabel(); @@ -146,6 +147,7 @@ const FormatStyle &Style; FormatTokenSource *Tokens; + llvm::Regex &ForEachMacrosRegex; UnwrappedLineConsumer &Callback; // FIXME: This is a temporary measure until we have reworked the ownership Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -206,10 +206,12 @@ UnwrappedLineParser::UnwrappedLineParser(const FormatStyle &Style, ArrayRef Tokens, + llvm::Regex &ForEachMacrosRegex, UnwrappedLineConsumer &Callback) : Line(new UnwrappedLine), MustBreakBeforeNextToken(false), CurrentLines(&Lines), StructuralError(false), Style(Style), Tokens(NULL), - Callback(Callback), AllTokens(Tokens), PPBranchLevel(-1) {} + ForEachMacrosRegex(ForEachMacrosRegex), Callback(Callback), + AllTokens(Tokens), PPBranchLevel(-1) {} void UnwrappedLineParser::reset() { PPBranchLevel = -1; @@ -628,7 +630,7 @@ return; case tok::kw_for: case tok::kw_while: - parseForOrWhileLoop(); + parseForOrWhileOrForEachLoop(); return; case tok::kw_do: parseDoWhile(); @@ -653,6 +655,12 @@ return; } } + case tok::identifier: + if (FormatTok->isForEachMacro(ForEachMacrosRegex)) { + parseForOrWhileOrForEachLoop(); + return; + } + // In all other cases, parse the declaration. break; default: @@ -1031,9 +1039,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(ForEachMacrosRegex)) && + "'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 @@ -7447,6 +7447,9 @@ FormatStyle::SBPO_Always); CHECK_PARSE("SpaceBeforeParens: ControlStatements", SpaceBeforeParens, FormatStyle::SBPO_ControlStatements); + CHECK_PARSE("SpaceBeforeParens: ControlStatementsAndForEachMacros", + SpaceBeforeParens, + FormatStyle::SBPO_ControlStatementsAndForEachMacros); // For backward compatibility: CHECK_PARSE("SpaceAfterControlStatementKeyword: false", SpaceBeforeParens, FormatStyle::SBPO_Never); @@ -7476,6 +7479,9 @@ FormatStyle::NI_Inner); CHECK_PARSE("NamespaceIndentation: All", NamespaceIndentation, FormatStyle::NI_All); + + CHECK_PARSE("ForEachMacros: \".*for_?each.*\"", ForEachMacros, + ".*for_?each.*"); } TEST_F(FormatTest, ParsesConfigurationWithLanguages) {