[clang] 77e60bc - [clang-format] Add option to insert braces after control statements
via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 21 20:16:33 PST 2022
Author: owenca
Date: 2022-02-21T20:16:25-08:00
New Revision: 77e60bc42c48e16d646488d43210b1630cd4db49
URL: https://github.com/llvm/llvm-project/commit/77e60bc42c48e16d646488d43210b1630cd4db49
DIFF: https://github.com/llvm/llvm-project/commit/77e60bc42c48e16d646488d43210b1630cd4db49.diff
LOG: [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
Added:
Modified:
clang/docs/ClangFormatStyleOptions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Format/Format.h
clang/lib/Format/Format.cpp
clang/lib/Format/FormatToken.h
clang/lib/Format/UnwrappedLineParser.cpp
clang/lib/Format/UnwrappedLineParser.h
clang/unittests/Format/FormatTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 0cddf022ead3..2cb67fa5492e 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -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.
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 43a2cf98e7c8..499b065fe6e0 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -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
--------
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index d4a479e7c512..484438306b35 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -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
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index bc3f0c93426b..ec6574b33a8c 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -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 @@ class BracesRemover : public TokenAnalyzer {
}
};
+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();
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index 6b7d475232b0..5f05986addf6 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -489,6 +489,12 @@ struct FormatToken {
/// 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;
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 4c5ab5346b7d..7d29afb0c042 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -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)
diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h
index 52f7618d9bea..b2a2ae1bedc1 100644
--- a/clang/lib/Format/UnwrappedLineParser.h
+++ b/clang/lib/Format/UnwrappedLineParser.h
@@ -119,6 +119,7 @@ class UnwrappedLineParser {
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();
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index f6810766d83d..51f6239bf210 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -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;
More information about the cfe-commits
mailing list