Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -971,6 +971,16 @@ /// For example: BOOST_FOREACH. std::vector ForEachMacros; + /// \brief A vector of macros that should be interpreted as complete + /// statements. + /// + /// Typical macros are expressions, and require a semi-colon to be + /// added; sometimes this is not the case, and this allows to make + /// clang-format aware of such cases. + /// + /// For example: Q_UNUSED + std::vector StatementMacros; + /// \brief See documentation of ``IncludeCategories``. struct IncludeCategory { /// \brief The regular expression that this category matches. @@ -1568,8 +1578,7 @@ ObjCBlockIndentWidth == R.ObjCBlockIndentWidth && ObjCSpaceAfterProperty == R.ObjCSpaceAfterProperty && ObjCSpaceBeforeProtocolList == R.ObjCSpaceBeforeProtocolList && - PenaltyBreakAssignment == - R.PenaltyBreakAssignment && + PenaltyBreakAssignment == R.PenaltyBreakAssignment && PenaltyBreakBeforeFirstCallParameter == R.PenaltyBreakBeforeFirstCallParameter && PenaltyBreakComment == R.PenaltyBreakComment && @@ -1590,7 +1599,7 @@ SpacesInParentheses == R.SpacesInParentheses && SpacesInSquareBrackets == R.SpacesInSquareBrackets && Standard == R.Standard && TabWidth == R.TabWidth && - UseTab == R.UseTab; + StatementMacros == R.StatementMacros && UseTab == R.UseTab; } }; Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -405,6 +405,7 @@ IO.mapOptional("SpacesInParentheses", Style.SpacesInParentheses); IO.mapOptional("SpacesInSquareBrackets", Style.SpacesInSquareBrackets); IO.mapOptional("Standard", Style.Standard); + IO.mapOptional("StatementMacros", Style.StatementMacros); IO.mapOptional("TabWidth", Style.TabWidth); IO.mapOptional("UseTab", Style.UseTab); } @@ -637,6 +638,8 @@ LLVMStyle.DisableFormat = false; LLVMStyle.SortIncludes = true; LLVMStyle.SortUsingDeclarations = true; + LLVMStyle.StatementMacros.push_back("Q_UNUSED"); + LLVMStyle.StatementMacros.push_back("QT_REQUIRE_VERSION"); return LLVMStyle; } Index: lib/Format/FormatToken.h =================================================================== --- lib/Format/FormatToken.h +++ lib/Format/FormatToken.h @@ -84,6 +84,7 @@ TYPE(RegexLiteral) \ TYPE(SelectorName) \ TYPE(StartOfName) \ + TYPE(StatementMacro) \ TYPE(StructuredBindingLSquare) \ TYPE(TemplateCloser) \ TYPE(TemplateOpener) \ Index: lib/Format/FormatTokenLexer.h =================================================================== --- lib/Format/FormatTokenLexer.h +++ lib/Format/FormatTokenLexer.h @@ -22,6 +22,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Format/Format.h" #include "llvm/Support/Regex.h" +#include #include @@ -97,7 +98,8 @@ // Index (in 'Tokens') of the last token that starts a new line. unsigned FirstInLineIndex; SmallVector Tokens; - SmallVector ForEachMacros; + + llvm::SmallMapVector Macros; bool FormattingDisabled; Index: lib/Format/FormatTokenLexer.cpp =================================================================== --- lib/Format/FormatTokenLexer.cpp +++ lib/Format/FormatTokenLexer.cpp @@ -37,8 +37,9 @@ Lex->SetKeepWhitespaceMode(true); for (const std::string &ForEachMacro : Style.ForEachMacros) - ForEachMacros.push_back(&IdentTable.get(ForEachMacro)); - std::sort(ForEachMacros.begin(), ForEachMacros.end()); + Macros.insert({&IdentTable.get(ForEachMacro), TT_ForEachMacro}); + for (const std::string &StatementMacro : Style.StatementMacros) + Macros.insert({&IdentTable.get(StatementMacro), TT_StatementMacro}); } ArrayRef FormatTokenLexer::lex() { @@ -633,12 +634,13 @@ } if (Style.isCpp()) { + decltype(Macros)::iterator it; if (!(Tokens.size() > 0 && Tokens.back()->Tok.getIdentifierInfo() && Tokens.back()->Tok.getIdentifierInfo()->getPPKeywordID() == tok::pp_define) && - std::find(ForEachMacros.begin(), ForEachMacros.end(), - FormatTok->Tok.getIdentifierInfo()) != ForEachMacros.end()) { - FormatTok->Type = TT_ForEachMacro; + (it = Macros.find(FormatTok->Tok.getIdentifierInfo())) != + Macros.end()) { + FormatTok->Type = it->second; } else if (FormatTok->is(tok::identifier)) { if (MacroBlockBeginRegex.match(Text)) { FormatTok->Type = TT_MacroBlockBegin; Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -1089,6 +1089,15 @@ return; } } + if (Style.isCpp() && FormatTok->is(TT_StatementMacro)) { + nextToken(); + if (FormatTok->is(tok::l_paren)) + parseParens(); + if (FormatTok->is(tok::semi)) + nextToken(); + addUnwrappedLine(); + return; + } // In all other cases, parse the declaration. break; default: @@ -1238,6 +1247,16 @@ return; } + if (Style.isCpp() && FormatTok->is(TT_StatementMacro)) { + nextToken(); + if (FormatTok->is(tok::l_paren)) + parseParens(); + if (FormatTok->is(tok::semi)) + nextToken(); + addUnwrappedLine(); + return; + } + // See if the following token should start a new unwrapped line. StringRef Text = FormatTok->TokenText; nextToken(); Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -2385,6 +2385,42 @@ getLLVMStyleWithColumns(40))); verifyFormat("MACRO(>)"); + + // Some macros contain an implicit semicolon + FormatStyle Style = getLLVMStyle(); + Style.StatementMacros.push_back("FOO"); + verifyFormat("FOO(a) int b = 0;"); + verifyFormat("FOO(a)\n" + "int b = 0;", + Style); + verifyFormat("FOO(a);\n" + "int b = 0;", + Style); + verifyFormat("FOO(argc, argv, \"4.0.2\")\n" + "int b = 0;", + Style); + verifyFormat("FOO()\n" + "int b = 0;", + Style); + verifyFormat("FOO\n" + "int b = 0;", + Style); + verifyFormat("void f() {\n" + " FOO(a)\n" + " return a;\n" + "}", + Style); + verifyFormat("FOO(a)\n" + "FOO(b)", + Style); + verifyFormat("int a = 0;\n" + "FOO(b)\n" + "int c = 0;", + Style); + verifyFormat("int a = 0;\n" + "int x = FOO(a)\n" + "int b = 0;", + Style); } TEST_F(FormatTest, LayoutMacroDefinitionsStatementsSpanningBlocks) { @@ -10185,6 +10221,12 @@ CHECK_PARSE("ForEachMacros: [BOOST_FOREACH, Q_FOREACH]", ForEachMacros, BoostAndQForeach); + Style.StatementMacros.clear(); + CHECK_PARSE("StatementMacros: [QUNUSED]", StatementMacros, + std::vector{"QUNUSED"}); + CHECK_PARSE("StatementMacros: [QUNUSED, QT_REQUIRE_VERSION]", StatementMacros, + std::vector({"QUNUSED", "QT_REQUIRE_VERSION"})); + Style.IncludeCategories.clear(); std::vector ExpectedCategories = {{"abc/.*", 2}, {".*", 1}};