forked from OSchip/llvm-project
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 llvm-svn: 343602
This commit is contained in:
parent
59500f7a0b
commit
6f40e21a16
|
@ -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.
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -86,6 +86,7 @@ namespace format {
|
|||
TYPE(RegexLiteral) \
|
||||
TYPE(SelectorName) \
|
||||
TYPE(StartOfName) \
|
||||
TYPE(StatementMacro) \
|
||||
TYPE(StructuredBindingLSquare) \
|
||||
TYPE(TemplateCloser) \
|
||||
TYPE(TemplateOpener) \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -126,6 +126,7 @@ private:
|
|||
void parseObjCInterfaceOrImplementation();
|
||||
bool parseObjCProtocol();
|
||||
void parseJavaScriptEs6ImportExport();
|
||||
void parseStatementMacro();
|
||||
bool tryToParseLambda();
|
||||
bool tryToParseLambdaIntroducer();
|
||||
void tryToParseJSFunction();
|
||||
|
|
|
@ -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}};
|
||||
|
|
Loading…
Reference in New Issue