clang-format: Add MacroBlock{Begin,End} options

The MacroBlockBegin and MacroBlockEnd options make matching macro identifiers
behave like '{' and '}', respectively, in terms of indentation.

Mozilla code, for example, uses several macros that begin and end a scope.
Previously, Clang-Format removed the indentation resulting in:

    MACRO_BEGIN(...)
    MACRO_ENTRY(...)
    MACRO_ENTRY(...)
    MACRO_END

Now, using the options

    MacroBlockBegin: "^[A-Z_]+_BEGIN$"
    MacroBlockEnd: "^[A-Z_]+_END$"

will yield the expected result:

    MACRO_BEGIN(...)
      MACRO_ENTRY(...)
      MACRO_ENTRY(...)
    MACRO_END

Differential Revision: http://reviews.llvm.org/D10840

llvm-svn: 241363
This commit is contained in:
Birunthan Mohanathas 2015-07-03 17:25:16 +00:00
parent 15b86155c9
commit b001a0ba5e
6 changed files with 91 additions and 10 deletions

View File

@ -394,6 +394,12 @@ the configuration (without a prefix: ``Auto``).
(https://developers.google.com/protocol-buffers/).
**MacroBlockBegin** (``std::string``)
A regular expression matching macros that start a block.
**MacroBlockEnd** (``std::string``)
A regular expression matching macros that end a block.
**MaxEmptyLinesToKeep** (``unsigned``)
The maximum number of consecutive empty lines to keep.

View File

@ -290,6 +290,12 @@ struct FormatStyle {
/// \brief Language, this format style is targeted at.
LanguageKind Language;
/// \brief A regular expression matching macros that start a block.
std::string MacroBlockBegin;
/// \brief A regular expression matching macros that end a block.
std::string MacroBlockEnd;
/// \brief The maximum number of consecutive empty lines to keep.
unsigned MaxEmptyLinesToKeep;
@ -479,6 +485,8 @@ struct FormatStyle {
IndentWrappedFunctionNames == R.IndentWrappedFunctionNames &&
KeepEmptyLinesAtTheStartOfBlocks ==
R.KeepEmptyLinesAtTheStartOfBlocks &&
MacroBlockBegin == R.MacroBlockBegin &&
MacroBlockEnd == R.MacroBlockEnd &&
MaxEmptyLinesToKeep == R.MaxEmptyLinesToKeep &&
NamespaceIndentation == R.NamespaceIndentation &&
ObjCBlockIndentWidth == R.ObjCBlockIndentWidth &&

View File

@ -27,6 +27,7 @@
#include "llvm/Support/Allocator.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/YAMLTraits.h"
#include <queue>
#include <string>
@ -244,6 +245,8 @@ template <> struct MappingTraits<FormatStyle> {
Style.IndentWrappedFunctionNames);
IO.mapOptional("KeepEmptyLinesAtTheStartOfBlocks",
Style.KeepEmptyLinesAtTheStartOfBlocks);
IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin);
IO.mapOptional("MacroBlockEnd", Style.MacroBlockEnd);
IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep);
IO.mapOptional("NamespaceIndentation", Style.NamespaceIndentation);
IO.mapOptional("ObjCBlockIndentWidth", Style.ObjCBlockIndentWidth);
@ -468,6 +471,8 @@ FormatStyle getChromiumStyle(FormatStyle::LanguageKind Language) {
ChromiumStyle.BinPackParameters = false;
ChromiumStyle.DerivePointerAlignment = false;
}
ChromiumStyle.MacroBlockBegin = "^IPC_BEGIN_MESSAGE_MAP$";
ChromiumStyle.MacroBlockBegin = "^IPC_END_MESSAGE_MAP$";
return ChromiumStyle;
}
@ -620,7 +625,9 @@ public:
LessStashed(false), Column(0), TrailingWhitespace(0),
SourceMgr(SourceMgr), ID(ID), Style(Style),
IdentTable(getFormattingLangOpts(Style)), Keywords(IdentTable),
Encoding(Encoding), FirstInLineIndex(0), FormattingDisabled(false) {
Encoding(Encoding), FirstInLineIndex(0), FormattingDisabled(false),
MacroBlockBeginRegex(Style.MacroBlockBegin),
MacroBlockEndRegex(Style.MacroBlockEnd) {
Lex.reset(new Lexer(ID, SourceMgr.getBuffer(ID), SourceMgr,
getFormattingLangOpts(Style)));
Lex->SetKeepWhitespaceMode(true);
@ -1168,12 +1175,21 @@ private:
Column = FormatTok->LastLineColumnWidth;
}
if (Style.Language == FormatStyle::LK_Cpp) {
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->Tok.getIdentifierInfo()) != ForEachMacros.end()) {
FormatTok->Type = TT_ForEachMacro;
} else if (FormatTok->is(tok::identifier)) {
if (MacroBlockBeginRegex.match(Text)) {
FormatTok->Type = TT_MacroBlockBegin;
} else if (MacroBlockEndRegex.match(Text)) {
FormatTok->Type = TT_MacroBlockEnd;
}
}
}
return FormatTok;
}
@ -1198,6 +1214,9 @@ private:
bool FormattingDisabled;
llvm::Regex MacroBlockBeginRegex;
llvm::Regex MacroBlockEndRegex;
void readRawToken(FormatToken &Tok) {
Lex->LexFromRawLexer(Tok.Tok);
Tok.TokenText = StringRef(SourceMgr.getCharacterData(Tok.Tok.getLocation()),

View File

@ -59,6 +59,8 @@ enum TokenType {
TT_LambdaLSquare,
TT_LeadingJavaAnnotation,
TT_LineComment,
TT_MacroBlockBegin,
TT_MacroBlockEnd,
TT_ObjCBlockLBrace,
TT_ObjCBlockLParen,
TT_ObjCDecl,

View File

@ -269,7 +269,14 @@ void UnwrappedLineParser::parseFile() {
void UnwrappedLineParser::parseLevel(bool HasOpeningBrace) {
bool SwitchLabelEncountered = false;
do {
switch (FormatTok->Tok.getKind()) {
tok::TokenKind kind = FormatTok->Tok.getKind();
if (FormatTok->Type == TT_MacroBlockBegin) {
kind = tok::l_brace;
} else if (FormatTok->Type == TT_MacroBlockEnd) {
kind = tok::r_brace;
}
switch (kind) {
case tok::comment:
nextToken();
addUnwrappedLine();
@ -393,10 +400,16 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) {
void UnwrappedLineParser::parseBlock(bool MustBeDeclaration, bool AddLevel,
bool MunchSemi) {
assert(FormatTok->Tok.is(tok::l_brace) && "'{' expected");
assert(FormatTok->isOneOf(tok::l_brace, TT_MacroBlockBegin) &&
"'{' or macro block token expected");
const bool MacroBlock = FormatTok->is(TT_MacroBlockBegin);
unsigned InitialLevel = Line->Level;
nextToken();
if (MacroBlock && FormatTok->is(tok::l_paren))
parseParens();
addUnwrappedLine();
ScopedDeclarationState DeclarationState(*Line, DeclarationScopeStack,
@ -405,12 +418,17 @@ void UnwrappedLineParser::parseBlock(bool MustBeDeclaration, bool AddLevel,
++Line->Level;
parseLevel(/*HasOpeningBrace=*/true);
if (!FormatTok->Tok.is(tok::r_brace)) {
if (MacroBlock ? !FormatTok->is(TT_MacroBlockEnd)
: !FormatTok->is(tok::r_brace)) {
Line->Level = InitialLevel;
return;
}
nextToken(); // Munch the closing brace.
if (MacroBlock && FormatTok->is(tok::l_paren))
parseParens();
if (MunchSemi && FormatTok->Tok.is(tok::semi))
nextToken();
Line->Level = InitialLevel;
@ -757,6 +775,11 @@ void UnwrappedLineParser::parseStructuralElement() {
parseForOrWhileLoop();
return;
}
if (FormatTok->is(TT_MacroBlockBegin)) {
parseBlock(/*MustBeDeclaration=*/false, /*AddLevel=*/true,
/*MunchSemi=*/false);
return;
}
if (Style.Language == FormatStyle::LK_JavaScript &&
FormatTok->is(Keywords.kw_import)) {
parseJavaScriptEs6ImportExport();
@ -860,6 +883,11 @@ void UnwrappedLineParser::parseStructuralElement() {
parseTryCatch();
return;
case tok::identifier: {
if (FormatTok->is(TT_MacroBlockEnd)) {
addUnwrappedLine();
return;
}
// Parse function literal unless 'function' is the first token in a line
// in which case this should be treated as a free-standing function.
if (Style.Language == FormatStyle::LK_JavaScript &&

View File

@ -3216,6 +3216,24 @@ TEST_F(FormatTest, PutEmptyBlocksIntoOneLine) {
verifyFormat("enum E {}");
}
TEST_F(FormatTest, FormatBeginBlockEndMacros) {
FormatStyle Style = getLLVMStyle();
Style.MacroBlockBegin = "^[A-Z_]+_BEGIN$";
Style.MacroBlockEnd = "^[A-Z_]+_END$";
verifyFormat("FOO_BEGIN\n"
" FOO_ENTRY\n"
"FOO_END", Style);
verifyFormat("FOO_BEGIN\n"
" NESTED_FOO_BEGIN\n"
" NESTED_FOO_ENTRY\n"
" NESTED_FOO_END\n"
"FOO_END", Style);
verifyFormat("FOO_BEGIN(Foo, Bar)\n"
" int x;\n"
" x = 1;\n"
"FOO_END(Baz)", Style);
}
//===----------------------------------------------------------------------===//
// Line break tests.
//===----------------------------------------------------------------------===//