From 33df7ef5ffbc4a6a0ef0dd0818d49c680d73102d Mon Sep 17 00:00:00 2001 From: Daniel Jasper Date: Tue, 1 Apr 2014 12:55:11 +0000 Subject: [PATCH] clang-format: Support configurable list of foreach-macros. This fixes llvm.org/PR17242. Patch by Brian Green, thank you! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@205307 91177308-0d34-0410-b5e6-96231b3b80d8 --- docs/ClangFormatStyleOptions.rst | 8 ++++++++ include/clang/Format/Format.h | 15 ++++++++++++++- lib/Format/Format.cpp | 19 +++++++++++++++++-- lib/Format/FormatToken.h | 5 ++++- lib/Format/TokenAnnotator.cpp | 14 +++++++++++--- lib/Format/UnwrappedLineParser.cpp | 11 +++++++++-- unittests/Format/FormatTest.cpp | 16 ++++++++++++++++ 7 files changed, 79 insertions(+), 9 deletions(-) diff --git a/docs/ClangFormatStyleOptions.rst b/docs/ClangFormatStyleOptions.rst index 662f49d994..db77372724 100644 --- a/docs/ClangFormatStyleOptions.rst +++ b/docs/ClangFormatStyleOptions.rst @@ -212,6 +212,14 @@ the configuration (without a prefix: ``Auto``). 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. diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h index fd180f4671..cded911edb 100644 --- a/include/clang/Format/Format.h +++ b/include/clang/Format/Format.h @@ -302,6 +302,18 @@ struct FormatStyle { /// which should not be split into lines or otherwise changed. std::string CommentPragmas; + /// \brief A vector 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 @@ struct FormatStyle { SpaceBeforeParens == R.SpaceBeforeParens && SpaceBeforeAssignmentOperators == R.SpaceBeforeAssignmentOperators && ContinuationIndentWidth == R.ContinuationIndentWidth && - CommentPragmas == R.CommentPragmas; + CommentPragmas == R.CommentPragmas && + ForEachMacros == R.ForEachMacros; } }; diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index b810f4492e..cb07cb56a5 100644 --- a/lib/Format/Format.cpp +++ b/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 @@ template <> struct MappingTraits { Style.SpaceBeforeAssignmentOperators); IO.mapOptional("ContinuationIndentWidth", Style.ContinuationIndentWidth); IO.mapOptional("CommentPragmas", Style.CommentPragmas); + IO.mapOptional("ForEachMacros", Style.ForEachMacros); // For backward compatibility. if (!IO.outputting()) { @@ -259,11 +262,16 @@ FormatStyle getLLVMStyle() { LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach; LLVMStyle.BreakConstructorInitializersBeforeComma = false; LLVMStyle.ColumnLimit = 80; + LLVMStyle.CommentPragmas = "^ IWYU pragma:"; LLVMStyle.ConstructorInitializerAllOnOneLineOrOnePerLine = false; LLVMStyle.ConstructorInitializerIndentWidth = 4; + LLVMStyle.ContinuationIndentWidth = 4; LLVMStyle.Cpp11BracedListStyle = true; LLVMStyle.DerivePointerBinding = false; LLVMStyle.ExperimentalAutoDetectBinPacking = false; + LLVMStyle.ForEachMacros.push_back("foreach"); + LLVMStyle.ForEachMacros.push_back("Q_FOREACH"); + LLVMStyle.ForEachMacros.push_back("BOOST_FOREACH"); LLVMStyle.IndentCaseLabels = false; LLVMStyle.IndentFunctionDeclarationAfterType = false; LLVMStyle.IndentWidth = 2; @@ -283,9 +291,7 @@ FormatStyle getLLVMStyle() { LLVMStyle.SpacesInCStyleCastParentheses = false; LLVMStyle.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; LLVMStyle.SpaceBeforeAssignmentOperators = true; - LLVMStyle.ContinuationIndentWidth = 4; LLVMStyle.SpacesInAngles = false; - LLVMStyle.CommentPragmas = "^ IWYU pragma:"; LLVMStyle.PenaltyBreakComment = 300; LLVMStyle.PenaltyBreakFirstLessLess = 120; @@ -1131,6 +1137,10 @@ public: TrailingWhitespace(0), Lex(Lex), SourceMgr(SourceMgr), Style(Style), IdentTable(getFormattingLangOpts()), Encoding(Encoding) { Lex.SetKeepWhitespaceMode(true); + + for (const std::string& ForEachMacro : Style.ForEachMacros) + ForEachMacros.push_back(&IdentTable.get(ForEachMacro)); + std::sort(ForEachMacros.begin(), ForEachMacros.end()); } ArrayRef lex() { @@ -1351,6 +1361,10 @@ private: Column = FormatTok->LastLineColumnWidth; } + FormatTok->IsForEachMacro = + std::binary_search(ForEachMacros.begin(), ForEachMacros.end(), + FormatTok->Tok.getIdentifierInfo()); + return FormatTok; } @@ -1366,6 +1380,7 @@ private: encoding::Encoding Encoding; llvm::SpecificBumpPtrAllocator Allocator; SmallVector Tokens; + SmallVector ForEachMacros; void readRawToken(FormatToken &Tok) { Lex.LexFromRawLexer(Tok.Tok); diff --git a/lib/Format/FormatToken.h b/lib/Format/FormatToken.h index 42d4a8dc24..a5aaa6f6de 100644 --- a/lib/Format/FormatToken.h +++ b/lib/Format/FormatToken.h @@ -104,7 +104,7 @@ struct FormatToken { 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 @@ struct FormatToken { /// Only set if \c Type == \c TT_StartOfName. bool PartOfMultiVariableDeclStmt; + /// \brief Is this a foreach macro? + bool IsForEachMacro; + bool is(tok::TokenKind Kind) const { return Tok.is(Kind); } bool isOneOf(tok::TokenKind K1, tok::TokenKind K2) const { diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 6a0e03461e..709e0b856d 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -120,6 +120,10 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, 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); diff --git a/lib/Format/UnwrappedLineParser.cpp b/lib/Format/UnwrappedLineParser.cpp index 58a581e97e..96d815572f 100644 --- a/lib/Format/UnwrappedLineParser.cpp +++ b/lib/Format/UnwrappedLineParser.cpp @@ -654,6 +654,12 @@ void UnwrappedLineParser::parseStructuralElement() { return; } } + break; + case tok::identifier: + if (FormatTok->IsForEachMacro) { + parseForOrWhileLoop(); + return; + } // In all other cases, parse the declaration. break; default: @@ -1041,8 +1047,9 @@ void UnwrappedLineParser::parseNamespace() { } void UnwrappedLineParser::parseForOrWhileLoop() { - assert((FormatTok->Tok.is(tok::kw_for) || FormatTok->Tok.is(tok::kw_while)) && - "'for' or 'while' expected"); + assert((FormatTok->Tok.is(tok::kw_for) || FormatTok->Tok.is(tok::kw_while) || + FormatTok->IsForEachMacro) && + "'for', 'while' or foreach macro expected"); nextToken(); if (FormatTok->Tok.is(tok::l_paren)) parseParens(); diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 7332fd04e0..8c066e0428 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -465,6 +465,15 @@ TEST_F(FormatTest, RangeBasedForLoops) { " aaaaaaaaaaaa.aaaaaaaaaaaa().aaaaaaaaa().a()) {\n}"); } +TEST_F(FormatTest, ForEachLoops) { + verifyFormat("void f() {\n" + " foreach (Item *item, itemlist) {}\n" + " Q_FOREACH (Item *item, itemlist) {}\n" + " BOOST_FOREACH (Item *item, itemlist) {}\n" + " UNKNOWN_FORACH(Item * item, itemlist) {}\n" + "}"); +} + TEST_F(FormatTest, FormatsWhileLoop) { verifyFormat("while (true) {\n}"); verifyFormat("while (true)\n" @@ -7607,6 +7616,13 @@ TEST_F(FormatTest, ParsesConfiguration) { FormatStyle::NI_Inner); CHECK_PARSE("NamespaceIndentation: All", NamespaceIndentation, FormatStyle::NI_All); + + Style.ForEachMacros.clear(); + 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) { -- 2.50.1