summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/docs/ClangFormatStyleOptions.rst9
-rw-r--r--clang/include/clang/Format/Format.h12
-rw-r--r--clang/lib/Format/Format.cpp3
-rw-r--r--clang/lib/Format/FormatToken.h1
-rw-r--r--clang/lib/Format/FormatTokenLexer.cpp11
-rw-r--r--clang/lib/Format/FormatTokenLexer.h4
-rw-r--r--clang/lib/Format/UnwrappedLineParser.cpp23
-rw-r--r--clang/lib/Format/UnwrappedLineParser.h1
-rw-r--r--clang/unittests/Format/FormatTest.cpp45
9 files changed, 102 insertions, 7 deletions
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 49f9f68088b..60696b3bb96 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -1979,6 +1979,15 @@ the configuration (without a prefix: ``Auto``).
+**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.
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 85dda948c11..80262e17d2d 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -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;
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 0637f76f07c..adc19fb11a3 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -469,6 +469,7 @@ template <> struct MappingTraits<FormatStyle> {
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;
}
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index f65fe5b0049..6e801a346b8 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -86,6 +86,7 @@ namespace format {
TYPE(RegexLiteral) \
TYPE(SelectorName) \
TYPE(StartOfName) \
+ TYPE(StatementMacro) \
TYPE(StructuredBindingLSquare) \
TYPE(TemplateCloser) \
TYPE(TemplateOpener) \
diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp
index 7f2edf102c7..146f5d68b55 100644
--- a/clang/lib/Format/FormatTokenLexer.cpp
+++ b/clang/lib/Format/FormatTokenLexer.cpp
@@ -37,8 +37,9 @@ FormatTokenLexer::FormatTokenLexer(const SourceManager &SourceMgr, FileID ID,
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::getNextToken() {
}
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;
diff --git a/clang/lib/Format/FormatTokenLexer.h b/clang/lib/Format/FormatTokenLexer.h
index 3b79d27480e..0cf357c85f3 100644
--- a/clang/lib/Format/FormatTokenLexer.h
+++ b/clang/lib/Format/FormatTokenLexer.h
@@ -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;
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 09cd660e6de..6c81d8b60e9 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -480,6 +480,10 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) {
}
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::parseStructuralElement() {
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::parseStructuralElement() {
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::parseJavaScriptEs6ImportExport() {
}
}
+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
diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h
index 87254832c63..55d60dff915 100644
--- a/clang/lib/Format/UnwrappedLineParser.h
+++ b/clang/lib/Format/UnwrappedLineParser.h
@@ -126,6 +126,7 @@ private:
void parseObjCInterfaceOrImplementation();
bool parseObjCProtocol();
void parseJavaScriptEs6ImportExport();
+ void parseStatementMacro();
bool tryToParseLambda();
bool tryToParseLambdaIntroducer();
void tryToParseJSFunction();
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 3e23c5e11b5..b29f67addf8 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -2660,6 +2660,45 @@ TEST_F(FormatTest, MacroCallsWithoutTrailingSemicolon) {
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}};
OpenPOWER on IntegriCloud