Author: typz Date: Tue Oct 2 09:37:51 2018 New Revision: 343602 URL: http://llvm.org/viewvc/llvm-project?rev=343602&view=rev Log: clang-format: better handle statement macros
Summary: Some macros are used in the body of function, and actually contain the trailing semicolon: they should thus be automatically followed by a new line, and not get merged with the next line. This is for example the case with Qt's Q_UNUSED macro: void foo(int a, int b) { Q_UNUSED(a) return b; } This patch deals with these cases by introducing a new option to specify list of statement macros. This re-uses the system already in place for foreach macros, to ensure there is no impact on performance. Reviewers: krasimir, djasper, klimek Reviewed By: krasimir Subscribers: acoomans, mgrang, alexfh, klimek, cfe-commits Differential Revision: https://reviews.llvm.org/D33440 Modified: cfe/trunk/docs/ClangFormatStyleOptions.rst cfe/trunk/include/clang/Format/Format.h cfe/trunk/lib/Format/Format.cpp cfe/trunk/lib/Format/FormatToken.h cfe/trunk/lib/Format/FormatTokenLexer.cpp cfe/trunk/lib/Format/FormatTokenLexer.h cfe/trunk/lib/Format/UnwrappedLineParser.cpp cfe/trunk/lib/Format/UnwrappedLineParser.h cfe/trunk/unittests/Format/FormatTest.cpp Modified: cfe/trunk/docs/ClangFormatStyleOptions.rst URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/ClangFormatStyleOptions.rst?rev=343602&r1=343601&r2=343602&view=diff ============================================================================== --- cfe/trunk/docs/ClangFormatStyleOptions.rst (original) +++ cfe/trunk/docs/ClangFormatStyleOptions.rst Tue Oct 2 09:37:51 2018 @@ -1979,6 +1979,15 @@ the configuration (without a prefix: ``A +**StatementMacros** (``std::vector<std::string>``) + 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 + **TabWidth** (``unsigned``) The number of columns used for tab stops. Modified: cfe/trunk/include/clang/Format/Format.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Format/Format.h?rev=343602&r1=343601&r2=343602&view=diff ============================================================================== --- cfe/trunk/include/clang/Format/Format.h (original) +++ cfe/trunk/include/clang/Format/Format.h Tue Oct 2 09:37:51 2018 @@ -1051,6 +1051,16 @@ struct FormatStyle { /// For example: BOOST_FOREACH. std::vector<std::string> ForEachMacros; + /// 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<std::string> StatementMacros; + tooling::IncludeStyle IncludeStyle; /// Indent case labels one level from the switch statement. @@ -1766,7 +1776,7 @@ struct FormatStyle { SpacesInParentheses == R.SpacesInParentheses && SpacesInSquareBrackets == R.SpacesInSquareBrackets && Standard == R.Standard && TabWidth == R.TabWidth && - UseTab == R.UseTab; + StatementMacros == R.StatementMacros && UseTab == R.UseTab; } llvm::Optional<FormatStyle> GetLanguageStyle(LanguageKind Language) const; Modified: cfe/trunk/lib/Format/Format.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/Format.cpp?rev=343602&r1=343601&r2=343602&view=diff ============================================================================== --- cfe/trunk/lib/Format/Format.cpp (original) +++ cfe/trunk/lib/Format/Format.cpp Tue Oct 2 09:37:51 2018 @@ -469,6 +469,7 @@ template <> struct MappingTraits<FormatS 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); } @@ -714,6 +715,8 @@ FormatStyle getLLVMStyle() { LLVMStyle.DisableFormat = false; LLVMStyle.SortIncludes = true; LLVMStyle.SortUsingDeclarations = true; + LLVMStyle.StatementMacros.push_back("Q_UNUSED"); + LLVMStyle.StatementMacros.push_back("QT_REQUIRE_VERSION"); return LLVMStyle; } Modified: cfe/trunk/lib/Format/FormatToken.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/FormatToken.h?rev=343602&r1=343601&r2=343602&view=diff ============================================================================== --- cfe/trunk/lib/Format/FormatToken.h (original) +++ cfe/trunk/lib/Format/FormatToken.h Tue Oct 2 09:37:51 2018 @@ -86,6 +86,7 @@ namespace format { TYPE(RegexLiteral) \ TYPE(SelectorName) \ TYPE(StartOfName) \ + TYPE(StatementMacro) \ TYPE(StructuredBindingLSquare) \ TYPE(TemplateCloser) \ TYPE(TemplateOpener) \ Modified: cfe/trunk/lib/Format/FormatTokenLexer.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/FormatTokenLexer.cpp?rev=343602&r1=343601&r2=343602&view=diff ============================================================================== --- cfe/trunk/lib/Format/FormatTokenLexer.cpp (original) +++ cfe/trunk/lib/Format/FormatTokenLexer.cpp Tue Oct 2 09:37:51 2018 @@ -37,8 +37,9 @@ FormatTokenLexer::FormatTokenLexer(const Lex->SetKeepWhitespaceMode(true); for (const std::string &ForEachMacro : Style.ForEachMacros) - ForEachMacros.push_back(&IdentTable.get(ForEachMacro)); - llvm::sort(ForEachMacros); + Macros.insert({&IdentTable.get(ForEachMacro), TT_ForEachMacro}); + for (const std::string &StatementMacro : Style.StatementMacros) + Macros.insert({&IdentTable.get(StatementMacro), TT_StatementMacro}); } ArrayRef<FormatToken *> FormatTokenLexer::lex() { @@ -657,12 +658,12 @@ FormatToken *FormatTokenLexer::getNextTo } if (Style.isCpp()) { + auto it = Macros.find(FormatTok->Tok.getIdentifierInfo()); 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.end()) { + FormatTok->Type = it->second; } else if (FormatTok->is(tok::identifier)) { if (MacroBlockBeginRegex.match(Text)) { FormatTok->Type = TT_MacroBlockBegin; Modified: cfe/trunk/lib/Format/FormatTokenLexer.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/FormatTokenLexer.h?rev=343602&r1=343601&r2=343602&view=diff ============================================================================== --- cfe/trunk/lib/Format/FormatTokenLexer.h (original) +++ cfe/trunk/lib/Format/FormatTokenLexer.h Tue Oct 2 09:37:51 2018 @@ -22,6 +22,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Format/Format.h" #include "llvm/Support/Regex.h" +#include "llvm/ADT/MapVector.h" #include <stack> @@ -99,7 +100,8 @@ private: // Index (in 'Tokens') of the last token that starts a new line. unsigned FirstInLineIndex; SmallVector<FormatToken *, 16> Tokens; - SmallVector<IdentifierInfo *, 8> ForEachMacros; + + llvm::SmallMapVector<IdentifierInfo *, TokenType, 8> Macros; bool FormattingDisabled; Modified: cfe/trunk/lib/Format/UnwrappedLineParser.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/UnwrappedLineParser.cpp?rev=343602&r1=343601&r2=343602&view=diff ============================================================================== --- cfe/trunk/lib/Format/UnwrappedLineParser.cpp (original) +++ cfe/trunk/lib/Format/UnwrappedLineParser.cpp Tue Oct 2 09:37:51 2018 @@ -480,6 +480,10 @@ void UnwrappedLineParser::calculateBrace } LBraceStack.pop_back(); break; + case tok::identifier: + if (!Tok->is(TT_StatementMacro)) + break; + LLVM_FALLTHROUGH; case tok::at: case tok::semi: case tok::kw_if: @@ -1108,6 +1112,10 @@ void UnwrappedLineParser::parseStructura return; } } + if (Style.isCpp() && FormatTok->is(TT_StatementMacro)) { + parseStatementMacro(); + return; + } // In all other cases, parse the declaration. break; default: @@ -1309,6 +1317,11 @@ void UnwrappedLineParser::parseStructura return; } + if (Style.isCpp() && FormatTok->is(TT_StatementMacro)) { + parseStatementMacro(); + return; + } + // See if the following token should start a new unwrapped line. StringRef Text = FormatTok->TokenText; nextToken(); @@ -2328,6 +2341,16 @@ void UnwrappedLineParser::parseJavaScrip } } +void UnwrappedLineParser::parseStatementMacro() +{ + nextToken(); + if (FormatTok->is(tok::l_paren)) + parseParens(); + if (FormatTok->is(tok::semi)) + nextToken(); + addUnwrappedLine(); +} + LLVM_ATTRIBUTE_UNUSED static void printDebugInfo(const UnwrappedLine &Line, StringRef Prefix = "") { llvm::dbgs() << Prefix << "Line(" << Line.Level Modified: cfe/trunk/lib/Format/UnwrappedLineParser.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Format/UnwrappedLineParser.h?rev=343602&r1=343601&r2=343602&view=diff ============================================================================== --- cfe/trunk/lib/Format/UnwrappedLineParser.h (original) +++ cfe/trunk/lib/Format/UnwrappedLineParser.h Tue Oct 2 09:37:51 2018 @@ -126,6 +126,7 @@ private: void parseObjCInterfaceOrImplementation(); bool parseObjCProtocol(); void parseJavaScriptEs6ImportExport(); + void parseStatementMacro(); bool tryToParseLambda(); bool tryToParseLambdaIntroducer(); void tryToParseJSFunction(); Modified: cfe/trunk/unittests/Format/FormatTest.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Format/FormatTest.cpp?rev=343602&r1=343601&r2=343602&view=diff ============================================================================== --- cfe/trunk/unittests/Format/FormatTest.cpp (original) +++ cfe/trunk/unittests/Format/FormatTest.cpp Tue Oct 2 09:37:51 2018 @@ -2660,6 +2660,45 @@ TEST_F(FormatTest, MacroCallsWithoutTrai getLLVMStyleWithColumns(40))); verifyFormat("MACRO(>)"); + + // Some macros contain an implicit semicolon. + 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); + verifyFormat("void foo(int a) { FOO(a) }\n" + "uint32_t bar() {}", + Style); } TEST_F(FormatTest, LayoutMacroDefinitionsStatementsSpanningBlocks) { @@ -11095,6 +11134,12 @@ TEST_F(FormatTest, ParsesConfiguration) CHECK_PARSE("ForEachMacros: [BOOST_FOREACH, Q_FOREACH]", ForEachMacros, BoostAndQForeach); + Style.StatementMacros.clear(); + CHECK_PARSE("StatementMacros: [QUNUSED]", StatementMacros, + std::vector<std::string>{"QUNUSED"}); + CHECK_PARSE("StatementMacros: [QUNUSED, QT_REQUIRE_VERSION]", StatementMacros, + std::vector<std::string>({"QUNUSED", "QT_REQUIRE_VERSION"})); + Style.IncludeStyle.IncludeCategories.clear(); std::vector<tooling::IncludeStyle::IncludeCategory> ExpectedCategories = { {"abc/.*", 2}, {".*", 1}}; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits