Skip to content

Commit

Permalink
clang-format: better handle statement macros
Browse files Browse the repository at this point in the history
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
  • Loading branch information
francoisferrand committed Oct 2, 2018
1 parent 59500f7 commit 6f40e21
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 7 deletions.
9 changes: 9 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Expand Up @@ -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.

Expand Down
12 changes: 11 additions & 1 deletion clang/include/clang/Format/Format.h
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Format/Format.cpp
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Format/FormatToken.h
Expand Up @@ -86,6 +86,7 @@ namespace format {
TYPE(RegexLiteral) \
TYPE(SelectorName) \
TYPE(StartOfName) \
TYPE(StatementMacro) \
TYPE(StructuredBindingLSquare) \
TYPE(TemplateCloser) \
TYPE(TemplateOpener) \
Expand Down
11 changes: 6 additions & 5 deletions clang/lib/Format/FormatTokenLexer.cpp
Expand Up @@ -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() {
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Format/FormatTokenLexer.h
Expand Up @@ -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>

Expand Down Expand Up @@ -99,7 +100,8 @@ class FormatTokenLexer {
// 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;

Expand Down
23 changes: 23 additions & 0 deletions clang/lib/Format/UnwrappedLineParser.cpp
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Format/UnwrappedLineParser.h
Expand Up @@ -126,6 +126,7 @@ class UnwrappedLineParser {
void parseObjCInterfaceOrImplementation();
bool parseObjCProtocol();
void parseJavaScriptEs6ImportExport();
void parseStatementMacro();
bool tryToParseLambda();
bool tryToParseLambdaIntroducer();
void tryToParseJSFunction();
Expand Down
45 changes: 45 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Expand Up @@ -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) {
Expand Down Expand Up @@ -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}};
Expand Down

0 comments on commit 6f40e21

Please sign in to comment.