[clang-format] Add option to insert braces after control statements

Adds a new option InsertBraces to insert the optional braces after
if, else, for, while, and do in C++.

Differential Revision: https://reviews.llvm.org/D120217
This commit is contained in:
owenca 2022-02-07 22:58:50 -08:00
parent 95fed2b267
commit 77e60bc42c
8 changed files with 373 additions and 19 deletions

View File

@ -2756,6 +2756,39 @@ the configuration (without a prefix: ``Auto``).
LoooooooooooooooooooooooooooooooooooooooongReturnType
LoooooooooooooooooooooooooooooooongFunctionDeclaration();
**InsertBraces** (``Boolean``) :versionbadge:`clang-format 15`
Insert braces after control statements (``if``, ``else``, ``for``, ``do``,
and ``while``) in C++ unless the control statements are inside macro
definitions or the braces would enclose preprocessor directives.
.. warning::
Setting this option to `true` could lead to incorrect code formatting due
to clang-format's lack of complete semantic information. As such, extra
care should be taken to review code changes made by this option.
.. code-block:: c++
false: true:
if (isa<FunctionDecl>(D)) vs. if (isa<FunctionDecl>(D)) {
handleFunctionDecl(D); handleFunctionDecl(D);
else if (isa<VarDecl>(D)) } else if (isa<VarDecl>(D)) {
handleVarDecl(D); handleVarDecl(D);
else } else {
return; return;
}
while (i--) vs. while (i--) {
for (auto *A : D.attrs()) for (auto *A : D.attrs()) {
handleAttr(A); handleAttr(A);
}
}
do vs. do {
--i; --i;
while (i); } while (i);
**InsertTrailingCommas** (``TrailingCommaStyle``) :versionbadge:`clang-format 12`
If set to ``TCS_Wrapped`` will insert trailing commas in container
literals (arrays and objects) that wrap across multiple lines.

View File

@ -188,6 +188,9 @@ clang-format
- Changed ``BreakBeforeConceptDeclarations`` from ``Boolean`` to an enum.
- Option ``InsertBraces`` has been added to insert optional braces after control
statements.
libclang
--------

View File

@ -2571,6 +2571,38 @@ struct FormatStyle {
/// \version 3.7
bool IndentWrappedFunctionNames;
/// Insert braces after control statements (``if``, ``else``, ``for``, ``do``,
/// and ``while``) in C++ unless the control statements are inside macro
/// definitions or the braces would enclose preprocessor directives.
/// \warning
/// Setting this option to `true` could lead to incorrect code formatting due
/// to clang-format's lack of complete semantic information. As such, extra
/// care should be taken to review code changes made by this option.
/// \endwarning
/// \code
/// false: true:
///
/// if (isa<FunctionDecl>(D)) vs. if (isa<FunctionDecl>(D)) {
/// handleFunctionDecl(D); handleFunctionDecl(D);
/// else if (isa<VarDecl>(D)) } else if (isa<VarDecl>(D)) {
/// handleVarDecl(D); handleVarDecl(D);
/// else } else {
/// return; return;
/// }
///
/// while (i--) vs. while (i--) {
/// for (auto *A : D.attrs()) for (auto *A : D.attrs()) {
/// handleAttr(A); handleAttr(A);
/// }
/// }
///
/// do vs. do {
/// --i; --i;
/// while (i); } while (i);
/// \endcode
/// \version 15
bool InsertBraces;
/// A vector of prefixes ordered by the desired groups for Java imports.
///
/// One group's prefix can be a subset of another - the longest prefix is

View File

@ -768,6 +768,7 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("IndentWidth", Style.IndentWidth);
IO.mapOptional("IndentWrappedFunctionNames",
Style.IndentWrappedFunctionNames);
IO.mapOptional("InsertBraces", Style.InsertBraces);
IO.mapOptional("InsertTrailingCommas", Style.InsertTrailingCommas);
IO.mapOptional("JavaImportGroups", Style.JavaImportGroups);
IO.mapOptional("JavaScriptQuotes", Style.JavaScriptQuotes);
@ -1223,6 +1224,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.IndentWrappedFunctionNames = false;
LLVMStyle.IndentWidth = 2;
LLVMStyle.PPIndentWidth = -1;
LLVMStyle.InsertBraces = false;
LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None;
LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave;
LLVMStyle.JavaScriptWrapImports = true;
@ -1661,7 +1663,7 @@ ParseError validateQualifierOrder(FormatStyle *Style) {
return ParseError::DuplicateQualifierSpecified;
}
// Ensure the list has 'type' in it
// Ensure the list has 'type' in it.
auto type = std::find(Style->QualifierOrder.begin(),
Style->QualifierOrder.end(), "type");
if (type == Style->QualifierOrder.end())
@ -1821,6 +1823,48 @@ private:
}
};
class BracesInserter : public TokenAnalyzer {
public:
BracesInserter(const Environment &Env, const FormatStyle &Style)
: TokenAnalyzer(Env, Style) {}
std::pair<tooling::Replacements, unsigned>
analyze(TokenAnnotator &Annotator,
SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
FormatTokenLexer &Tokens) override {
AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
tooling::Replacements Result;
insertBraces(AnnotatedLines, Result);
return {Result, 0};
}
private:
void insertBraces(SmallVectorImpl<AnnotatedLine *> &Lines,
tooling::Replacements &Result) {
const auto &SourceMgr = Env.getSourceManager();
for (AnnotatedLine *Line : Lines) {
insertBraces(Line->Children, Result);
if (!Line->Affected)
continue;
for (FormatToken *Token = Line->First; Token && !Token->Finalized;
Token = Token->Next) {
if (Token->BraceCount == 0)
continue;
std::string Brace;
if (Token->BraceCount < 0) {
assert(Token->BraceCount == -1);
Brace = '{';
} else {
Brace = std::string(Token->BraceCount, '}');
}
Token->BraceCount = 0;
const auto Start = Token->Tok.getEndLoc();
cantFail(Result.add(tooling::Replacement(SourceMgr, Start, 0, Brace)));
}
}
}
};
class JavaScriptRequoter : public TokenAnalyzer {
public:
JavaScriptRequoter(const Environment &Env, const FormatStyle &Style)
@ -3133,6 +3177,11 @@ reformat(const FormatStyle &Style, StringRef Code,
});
}
if (Style.isCpp() && Style.InsertBraces)
Passes.emplace_back([&](const Environment &Env) {
return BracesInserter(Env, Expanded).process();
});
if (Style.isCpp() && Style.RemoveBracesLLVM)
Passes.emplace_back([&](const Environment &Env) {
return BracesRemover(Env, Expanded).process();

View File

@ -489,6 +489,12 @@ public:
/// Is optional and can be removed.
bool Optional = false;
/// Number of optional braces to be inserted after this token:
/// -1: a single left brace
/// 0: no braces
/// >0: number of right braces
int8_t BraceCount = 0;
/// If this token starts a block, this contains all the unwrapped lines
/// in it.
SmallVector<AnnotatedLine *, 1> Children;

View File

@ -2300,6 +2300,53 @@ void UnwrappedLineParser::keepAncestorBraces() {
NestedTooDeep.push_back(false);
}
static FormatToken *getLastNonComment(const UnwrappedLine &Line) {
for (const auto &Token : llvm::reverse(Line.Tokens))
if (Token.Tok->isNot(tok::comment))
return Token.Tok;
return nullptr;
}
void UnwrappedLineParser::parseUnbracedBody(bool CheckEOF) {
FormatToken *Tok = nullptr;
if (Style.InsertBraces && !Line->InPPDirective && !Line->Tokens.empty() &&
PreprocessorDirectives.empty()) {
Tok = getLastNonComment(*Line);
assert(Tok);
if (Tok->BraceCount < 0) {
assert(Tok->BraceCount == -1);
Tok = nullptr;
} else {
Tok->BraceCount = -1;
}
}
addUnwrappedLine();
++Line->Level;
parseStructuralElement();
if (Tok) {
assert(!Line->InPPDirective);
Tok = nullptr;
for (const auto &L : llvm::reverse(*CurrentLines)) {
if (!L.InPPDirective) {
Tok = getLastNonComment(L);
if (Tok)
break;
}
}
assert(Tok);
++Tok->BraceCount;
}
if (CheckEOF && FormatTok->is(tok::eof))
addUnwrappedLine();
--Line->Level;
}
static void markOptionalBraces(FormatToken *LeftBrace) {
if (!LeftBrace)
return;
@ -2354,10 +2401,7 @@ FormatToken *UnwrappedLineParser::parseIfThenElse(IfStmtKind *IfKind,
else
NeedsUnwrappedLine = true;
} else {
addUnwrappedLine();
++Line->Level;
parseStructuralElement();
--Line->Level;
parseUnbracedBody();
}
bool KeepIfBraces = false;
@ -2403,12 +2447,7 @@ FormatToken *UnwrappedLineParser::parseIfThenElse(IfStmtKind *IfKind,
if (IsPrecededByComment)
--Line->Level;
} else {
addUnwrappedLine();
++Line->Level;
parseStructuralElement();
if (FormatTok->is(tok::eof))
addUnwrappedLine();
--Line->Level;
parseUnbracedBody(/*CheckEOF=*/true);
}
} else {
if (Style.RemoveBracesLLVM)
@ -2654,10 +2693,7 @@ void UnwrappedLineParser::parseForOrWhileLoop() {
}
addUnwrappedLine();
} else {
addUnwrappedLine();
++Line->Level;
parseStructuralElement();
--Line->Level;
parseUnbracedBody();
}
if (Style.RemoveBracesLLVM)
@ -2676,10 +2712,7 @@ void UnwrappedLineParser::parseDoWhile() {
if (Style.BraceWrapping.BeforeWhile)
addUnwrappedLine();
} else {
addUnwrappedLine();
++Line->Level;
parseStructuralElement();
--Line->Level;
parseUnbracedBody();
}
if (Style.RemoveBracesLLVM)

View File

@ -119,6 +119,7 @@ private:
void parseParens(TokenType AmpAmpTokenType = TT_Unknown);
void parseSquare(bool LambdaIntroducer = false);
void keepAncestorBraces();
void parseUnbracedBody(bool CheckEOF = false);
FormatToken *parseIfThenElse(IfStmtKind *IfKind, bool KeepBraces = false);
void parseTryCatch();
void parseForOrWhileLoop();

View File

@ -19474,6 +19474,7 @@ TEST_F(FormatTest, ParsesConfigurationBools) {
CHECK_PARSE_BOOL_FIELD(IndentRequiresClause, "IndentRequires");
CHECK_PARSE_BOOL(IndentRequiresClause);
CHECK_PARSE_BOOL(IndentWrappedFunctionNames);
CHECK_PARSE_BOOL(InsertBraces);
CHECK_PARSE_BOOL(KeepEmptyLinesAtTheStartOfBlocks);
CHECK_PARSE_BOOL(ObjCSpaceAfterProperty);
CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList);
@ -24300,6 +24301,202 @@ TEST_F(FormatTest, ShortTemplatedArgumentLists) {
verifyFormat("template <int N> struct Foo<char[N]> {};", Style);
}
TEST_F(FormatTest, InsertBraces) {
FormatStyle Style = getLLVMStyle();
Style.InsertBraces = true;
verifyFormat("// clang-format off\n"
"// comment\n"
"if (a) f();\n"
"// clang-format on\n"
"if (b) {\n"
" g();\n"
"}",
"// clang-format off\n"
"// comment\n"
"if (a) f();\n"
"// clang-format on\n"
"if (b) g();",
Style);
verifyFormat("if (a) {\n"
" switch (b) {\n"
" case 1:\n"
" c = 0;\n"
" break;\n"
" default:\n"
" c = 1;\n"
" }\n"
"}",
"if (a)\n"
" switch (b) {\n"
" case 1:\n"
" c = 0;\n"
" break;\n"
" default:\n"
" c = 1;\n"
" }",
Style);
verifyFormat("for (auto node : nodes) {\n"
" if (node) {\n"
" break;\n"
" }\n"
"}",
"for (auto node : nodes)\n"
" if (node)\n"
" break;",
Style);
verifyFormat("for (auto node : nodes) {\n"
" if (node)\n"
"}",
"for (auto node : nodes)\n"
" if (node)",
Style);
verifyFormat("do {\n"
" --a;\n"
"} while (a);",
"do\n"
" --a;\n"
"while (a);",
Style);
verifyFormat("if (i) {\n"
" ++i;\n"
"} else {\n"
" --i;\n"
"}",
"if (i)\n"
" ++i;\n"
"else {\n"
" --i;\n"
"}",
Style);
verifyFormat("void f() {\n"
" while (j--) {\n"
" while (i) {\n"
" --i;\n"
" }\n"
" }\n"
"}",
"void f() {\n"
" while (j--)\n"
" while (i)\n"
" --i;\n"
"}",
Style);
verifyFormat("f({\n"
" if (a) {\n"
" g();\n"
" }\n"
"});",
"f({\n"
" if (a)\n"
" g();\n"
"});",
Style);
verifyFormat("if (a) {\n"
" f();\n"
"} else if (b) {\n"
" g();\n"
"} else {\n"
" h();\n"
"}",
"if (a)\n"
" f();\n"
"else if (b)\n"
" g();\n"
"else\n"
" h();",
Style);
verifyFormat("if (a) {\n"
" f();\n"
"}\n"
"// comment\n"
"/* comment */",
"if (a)\n"
" f();\n"
"// comment\n"
"/* comment */",
Style);
verifyFormat("if (a) {\n"
" // foo\n"
" // bar\n"
" f();\n"
"}",
"if (a)\n"
" // foo\n"
" // bar\n"
" f();",
Style);
verifyFormat("if (a) { // comment\n"
" // comment\n"
" f();\n"
"}",
"if (a) // comment\n"
" // comment\n"
" f();",
Style);
verifyFormat("if (a) {\n"
" f();\n"
"}\n"
"#undef A\n"
"#undef B",
"if (a)\n"
" f();\n"
"#undef A\n"
"#undef B",
Style);
verifyFormat("if (a)\n"
"#ifdef A\n"
" f();\n"
"#else\n"
" g();\n"
"#endif",
Style);
verifyFormat("#if 0\n"
"#elif 1\n"
"#endif\n"
"void f() {\n"
" if (a) {\n"
" g();\n"
" }\n"
"}",
"#if 0\n"
"#elif 1\n"
"#endif\n"
"void f() {\n"
" if (a) g();\n"
"}",
Style);
Style.ColumnLimit = 15;
verifyFormat("#define A \\\n"
" if (a) \\\n"
" f();",
Style);
verifyFormat("if (a + b >\n"
" c) {\n"
" f();\n"
"}",
"if (a + b > c)\n"
" f();",
Style);
}
TEST_F(FormatTest, RemoveBraces) {
FormatStyle Style = getLLVMStyle();
Style.RemoveBracesLLVM = true;