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
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@343602
91177308-0d34-0410-b5e6-
96231b3b80d8
+**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.
/// 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.
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;
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);
}
LLVMStyle.DisableFormat = false;
LLVMStyle.SortIncludes = true;
LLVMStyle.SortUsingDeclarations = true;
+ LLVMStyle.StatementMacros.push_back("Q_UNUSED");
+ LLVMStyle.StatementMacros.push_back("QT_REQUIRE_VERSION");
return LLVMStyle;
}
TYPE(RegexLiteral) \
TYPE(SelectorName) \
TYPE(StartOfName) \
+ TYPE(StatementMacro) \
TYPE(StructuredBindingLSquare) \
TYPE(TemplateCloser) \
TYPE(TemplateOpener) \
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() {
}
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;
#include "clang/Basic/SourceManager.h"
#include "clang/Format/Format.h"
#include "llvm/Support/Regex.h"
+#include "llvm/ADT/MapVector.h"
#include <stack>
// 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;
}
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:
return;
}
}
+ if (Style.isCpp() && FormatTok->is(TT_StatementMacro)) {
+ parseStatementMacro();
+ return;
+ }
// In all other cases, parse the declaration.
break;
default:
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();
}
}
+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
void parseObjCInterfaceOrImplementation();
bool parseObjCProtocol();
void parseJavaScriptEs6ImportExport();
+ void parseStatementMacro();
bool tryToParseLambda();
bool tryToParseLambdaIntroducer();
void tryToParseJSFunction();
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) {
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}};