MyDeveloperDay updated this revision to Diff 383744.
MyDeveloperDay added a reviewer: HazardyKnusperkeks.
MyDeveloperDay added a comment.
I'd like to carry the mantle for this, before making any further changes I'd
like to address some of the review comments
1. Add documentation warning about a modifying change (as agreed by the RFC)
2. Add version introduced information
3. Fix issue with Macro continuation (don't add the \n before the } unless you
see a \\ line comment, otherwise let clang-format deal with the \n depending on
the BraceWrapping style
4. Add unit tests for Macro continuation and Comment handling
5. Allow InsertBraces in other languages
TODO:
- break out into its own file (Format.cpp is getting too big)
- Look further into possible Removal (I have an idea for how this might be
possible, and super useful for LLVM where we don't like single if {} ), I'd
like to round out on this before introducing the options rather than having to
change them later
- Should we add the possibility of removal should we change the option name to
"AutomaticBraces" (thoughts?)
- @tiagoma please add your name and email addreess to this review so when the
time comes to land this I can attribute to you as "Author".
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D95168/new/
https://reviews.llvm.org/D95168
Files:
clang/docs/ClangFormatStyleOptions.rst
clang/include/clang/Format/Format.h
clang/lib/Format/Format.cpp
clang/unittests/Format/FormatTest.cpp
Index: clang/unittests/Format/FormatTest.cpp
===================================================================
--- clang/unittests/Format/FormatTest.cpp
+++ clang/unittests/Format/FormatTest.cpp
@@ -72,7 +72,10 @@
EXPECT_EQ(Expected.str(), format(Expected, Style))
<< "Expected code is not stable";
EXPECT_EQ(Expected.str(), format(Code, Style));
- if (Style.Language == FormatStyle::LK_Cpp) {
+ // clang::format::internal::reformat does not run any of the options that
+ // modify code for ObjC
+ if (Style.Language == FormatStyle::LK_Cpp &&
+ Style.InsertBraces == FormatStyle::BIS_Never) {
// Objective-C++ is a superset of C++, so everything checked for C++
// needs to be checked for Objective-C++ as well.
FormatStyle ObjCStyle = Style;
@@ -18758,6 +18761,12 @@
CHECK_PARSE("IndentExternBlock: false", IndentExternBlock,
FormatStyle::IEBS_NoIndent);
+ Style.InsertBraces = FormatStyle::BIS_Never;
+ CHECK_PARSE("InsertBraces: Always", InsertBraces, FormatStyle::BIS_Always);
+ CHECK_PARSE("InsertBraces: WrapLikely", InsertBraces,
+ FormatStyle::BIS_WrapLikely);
+ CHECK_PARSE("InsertBraces: Never", InsertBraces, FormatStyle::BIS_Never);
+
Style.BitFieldColonSpacing = FormatStyle::BFCS_None;
CHECK_PARSE("BitFieldColonSpacing: Both", BitFieldColonSpacing,
FormatStyle::BFCS_Both);
@@ -22397,6 +22406,271 @@
EXPECT_EQ(Code, format(Code, Style));
}
+TEST_F(FormatTest, InsertBraces) {
+ FormatStyle Style = getLLVMStyle();
+
+ StringRef IfSourceShort = "if (condition)\n"
+ " call_function(arg, arg);";
+ verifyFormat(IfSourceShort);
+
+ StringRef IfSourceLong = "if (condition)\n"
+ " call_function(arg, arg, arg, arg, arg, arg);";
+ verifyFormat(IfSourceLong);
+
+ StringRef IfElseSourceShort = "if (condition)\n"
+ " a_function(arg, arg);\n"
+ "else\n"
+ " another_function(arg, arg);";
+ verifyFormat(IfElseSourceShort);
+
+ StringRef IfElseSourceLong =
+ "if (condition)\n"
+ " a_function(arg, arg, arg, arg, arg, arg);\n"
+ "else\n"
+ " another_function(arg, arg, arg, arg, arg, arg);";
+ verifyFormat(IfElseSourceLong);
+
+ StringRef ForSourceShort = "for (auto val : container)\n"
+ " call_function(arg, arg);";
+ verifyFormat(ForSourceShort);
+
+ StringRef ForSourceLong = "for (auto val : container)\n"
+ " call_function(arg, arg, arg, arg, arg, arg);";
+ verifyFormat(ForSourceLong);
+
+ StringRef WhileShort = "while (condition)\n"
+ " call_function(arg, arg);";
+ verifyFormat(WhileShort);
+
+ StringRef WhileLong = "while (condition)\n"
+ " call_function(arg, arg, arg, arg, arg, arg);";
+ verifyFormat(WhileLong);
+
+ StringRef DoShort = "do\n"
+ " call_function(arg, arg);\n"
+ "while (condition);";
+ verifyFormat(DoShort);
+
+ StringRef DoLong = "do\n"
+ " call_function(arg, arg, arg, arg, arg, arg);\n"
+ "while (condition);";
+ verifyFormat(DoLong);
+
+ StringRef ChainedConditionals = "if (A)\n"
+ " if (B)\n"
+ " callAB();\n"
+ " else\n"
+ " callA();\n"
+ "else if (B)\n"
+ " callB();\n"
+ "else\n"
+ " call();";
+ verifyFormat(ChainedConditionals);
+
+ StringRef ChainedDoWhiles = "do\n"
+ " while (arg++)\n"
+ " do\n"
+ " while (arg++)\n"
+ " call_function(arg, arg);\n"
+ " while (condition);\n"
+ "while (condition);";
+ verifyFormat(ChainedDoWhiles);
+
+ StringRef IfWithMultilineComment =
+ "if /*condition*/ (condition) /*condition*/\n"
+ " you_do_you(); /*condition*/";
+ verifyFormat(IfWithMultilineComment);
+
+ StringRef IfSinglelineCommentOnConditional = "if (condition) // my test\n"
+ " you_do_you();";
+ verifyFormat(IfSinglelineCommentOnConditional);
+
+ Style.InsertBraces = FormatStyle::BIS_Always;
+
+ verifyFormat("while (condition) {\n"
+ " call_function(arg, arg);\n"
+ "}",
+ WhileShort, Style);
+
+ verifyFormat("while (condition) {\n"
+ " call_function(arg, arg, arg, arg, arg, arg);\n"
+ "}",
+ WhileLong, Style);
+
+ verifyFormat("do {\n"
+ " call_function(arg, arg);\n"
+ "} while (condition);",
+ DoShort, Style);
+
+ verifyFormat("do {\n"
+ " call_function(arg, arg, arg, arg, arg, arg);\n"
+ "} while (condition);",
+ DoLong, Style);
+
+ verifyFormat("if (condition) {\n"
+ " call_function(arg, arg);\n"
+ "}",
+ IfSourceShort, Style);
+
+ verifyFormat("if (condition) {\n"
+ " call_function(arg, arg);\n"
+ "}",
+ IfSourceShort, Style);
+
+ verifyFormat("if (condition) {\n"
+ " call_function(arg, arg, arg, arg, arg, arg);\n"
+ "}",
+ IfSourceLong, Style);
+
+ verifyFormat("if (condition) {\n"
+ " a_function(arg, arg);\n"
+ "} else {\n"
+ " another_function(arg, arg);\n"
+ "}",
+ IfElseSourceShort, Style);
+
+ verifyFormat("if (condition) {\n"
+ " a_function(arg, arg, arg, arg, arg, arg);\n"
+ "} else {\n"
+ " another_function(arg, arg, arg, arg, arg, arg);\n"
+ "}",
+ IfElseSourceLong, Style);
+
+ verifyFormat("for (auto val : container) {\n"
+ " call_function(arg, arg);\n"
+ "}",
+ ForSourceShort, Style);
+
+ verifyFormat("for (auto val : container) {\n"
+ " call_function(arg, arg, arg, arg, arg, arg);\n"
+ "}",
+ ForSourceLong, Style);
+
+ verifyFormat("if (A)\n"
+ " if (B) {\n"
+ " callAB();\n"
+ " } else {\n"
+ " callA();\n"
+ " }\n"
+ "else if (B) {\n"
+ " callB();\n"
+ "} else {\n"
+ " call();\n"
+ "}",
+ ChainedConditionals, Style);
+
+ verifyFormat("do\n"
+ " while (arg++)\n"
+ " do\n"
+ " while (arg++) {\n"
+ " call_function(arg, arg);\n"
+ " }\n"
+ " while (condition);\n"
+ "while (condition);",
+ ChainedDoWhiles, Style);
+
+ verifyFormat("if /*condition*/ (condition) /*condition*/\n"
+ "{\n"
+ " you_do_you(); /*condition*/\n"
+ "}",
+ IfWithMultilineComment, Style);
+
+ verifyFormat("if (condition) // my test\n"
+ "{\n"
+ " you_do_you();\n"
+ "}",
+ IfSinglelineCommentOnConditional, Style);
+
+ Style.InsertBraces = FormatStyle::BIS_WrapLikely;
+ Style.ColumnLimit = 35;
+
+ verifyFormat(IfSourceShort, IfSourceShort, Style);
+
+ verifyFormat("if (condition) {\n"
+ " call_function(arg, arg, arg, arg,\n"
+ " arg, arg);\n"
+ "}",
+ IfSourceLong, Style);
+
+ verifyFormat(IfElseSourceShort, IfElseSourceShort, Style);
+
+ verifyFormat("if (condition) {\n"
+ " a_function(arg, arg, arg, arg,\n"
+ " arg, arg);\n"
+ "} else {\n"
+ " another_function(arg, arg, arg,\n"
+ " arg, arg, arg);\n"
+ "}",
+ IfElseSourceLong, Style);
+
+ verifyFormat(ForSourceShort, ForSourceShort, Style);
+
+ verifyFormat("for (auto val : container) {\n"
+ " call_function(arg, arg, arg, arg,\n"
+ " arg, arg);\n"
+ "}",
+ ForSourceLong, Style);
+
+ format("while (foo) ", Style);
+}
+
+TEST_F(FormatTest, InsertBracesMacro) {
+ auto Style = getLLVMStyleWithColumns(20);
+
+ verifyFormat("#define FOO1(x) \\\n"
+ " if (x) \\\n"
+ " return true; \\\n"
+ " else \\\n"
+ " return false;",
+ Style);
+
+ Style.InsertBraces = FormatStyle::BIS_Always;
+
+ verifyFormat("#define FOO2(x) \\\n"
+ " if (x) { \\\n"
+ " return true; \\\n"
+ " } else { \\\n"
+ " return false; \\\n"
+ " }",
+ "#define FOO2(x) \\\n"
+ " if (x) \\\n"
+ " return true; \\\n"
+ " else \\\n"
+ " return false;",
+ Style);
+
+ verifyFormat("#define FOO3(x) \\\n"
+ " if (x) { \\\n"
+ " return true; \\\n"
+ " } else { \\\n"
+ " return false; \\\n"
+ " }",
+ Style);
+}
+
+TEST_F(FormatTest, InsertBracesComments) {
+ auto Style = getLLVMStyle();
+
+ verifyFormat("if (x)\n"
+ " return true;\n"
+ "else\n"
+ " return false;",
+ Style);
+
+ Style.InsertBraces = FormatStyle::BIS_Always;
+
+ verifyFormat("if (x) {\n"
+ " return true; // Comment\n"
+ "} else {\n"
+ " return false; // Comment\n"
+ "}",
+ "if (x)\n"
+ " return true; // Comment\n"
+ "else\n"
+ " return false; // Comment",
+ Style);
+}
+
} // namespace
} // namespace format
} // namespace clang
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -214,6 +214,14 @@
}
};
+template <> struct ScalarEnumerationTraits<FormatStyle::BraceInsertionStyle> {
+ static void enumeration(IO &IO, FormatStyle::BraceInsertionStyle &Value) {
+ IO.enumCase(Value, "Never", FormatStyle::BIS_Never);
+ IO.enumCase(Value, "Always", FormatStyle::BIS_Always);
+ IO.enumCase(Value, "WrapLikely", FormatStyle::BIS_WrapLikely);
+ }
+};
+
template <> struct ScalarEnumerationTraits<FormatStyle::BinaryOperatorStyle> {
static void enumeration(IO &IO, FormatStyle::BinaryOperatorStyle &Value) {
IO.enumCase(Value, "All", FormatStyle::BOS_All);
@@ -731,6 +739,7 @@
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);
@@ -1137,6 +1146,7 @@
LLVMStyle.IndentRequires = false;
LLVMStyle.IndentWrappedFunctionNames = false;
LLVMStyle.IndentWidth = 2;
+ LLVMStyle.InsertBraces = FormatStyle::BIS_Never;
LLVMStyle.PPIndentWidth = -1;
LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None;
LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave;
@@ -1897,6 +1907,85 @@
FormattingAttemptStatus *Status;
};
+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 {
+ tooling::Replacements Result;
+ if (AffectedRangeMgr.computeAffectedLines(AnnotatedLines))
+ insertBraces(AnnotatedLines, Result);
+ return {Result, 0};
+ }
+
+private:
+ void insertBraces(SmallVectorImpl<AnnotatedLine *> &Lines,
+ tooling::Replacements &Result) {
+ bool InsideCtrlFlowStmt = false;
+ for (AnnotatedLine *Line : Lines) {
+ insertBraces(Line->Children, Result);
+
+ // Get first token that is not a comment.
+ const FormatToken *FirstTok = Line->First;
+ if (FirstTok->is(tok::comment))
+ FirstTok = FirstTok->getNextNonComment();
+ // The line is a comment.
+ if (!FirstTok)
+ continue;
+ // Get last token that is not a comment.
+ const FormatToken *LastTok = Line->Last;
+ if (LastTok->is(tok::comment))
+ LastTok = LastTok->getPreviousNonComment();
+
+ // If this token starts a control flow stmt, mark it.
+ if (FirstTok->isOneOf(tok::kw_if, tok::kw_else, tok::kw_for, tok::kw_do,
+ tok::kw_while)) {
+ InsideCtrlFlowStmt = !LastTok->isOneOf(tok::l_brace, tok::semi);
+ continue;
+ }
+
+ // If the previous line started a ctrl flow block and this line does not
+ // start with curly.
+ if (Line->Affected && !FirstTok->Finalized && InsideCtrlFlowStmt &&
+ FirstTok->isNot(tok::l_brace)) {
+ const SourceLocation startBraceLoc = Line->First->Tok.getLocation();
+ // In some cases clang-format will run the same transform twice which
+ // could lead to adding the curlies twice, skip if we already added one
+ // at this location.
+ if (Done.count(startBraceLoc))
+ break;
+
+ if (Style.InsertBraces ==
+ FormatStyle::BraceInsertionStyle::BIS_Always ||
+ Line->Last->OriginalColumn > Style.ColumnLimit) {
+ Done.insert(startBraceLoc);
+ cantFail(Result.add(tooling::Replacement(Env.getSourceManager(),
+ startBraceLoc, 0, "{")));
+
+ // Mostly we can ignore putting a \n before the } and let the
+ // Formatter handle it, unless the last token is a // comment, Doing
+ // so avoids issues with macro continuation. And a line comment
+ // cannot be part of a continuation.
+ std::string ending = "}";
+ if (Line->Last->is(tok::comment))
+ ending = "\n}";
+ cantFail(Result.add(tooling::Replacement(
+ Env.getSourceManager(),
+ Line->Last->Tok.getLocation().getLocWithOffset(
+ Line->Last->TokenText.size()),
+ 0, ending)));
+ }
+ }
+ InsideCtrlFlowStmt = false;
+ }
+ }
+ std::set<SourceLocation> Done;
+};
+
/// TrailingCommaInserter inserts trailing commas into container literals.
/// E.g.:
/// const x = [
@@ -2982,6 +3071,11 @@
});
}
+ if (Style.InsertBraces != FormatStyle::BIS_Never)
+ Passes.emplace_back([&](const Environment &Env) {
+ return BracesInserter(Env, Expanded).process();
+ });
+
if (Style.Language == FormatStyle::LK_JavaScript &&
Style.JavaScriptQuotes != FormatStyle::JSQS_Leave)
Passes.emplace_back([&](const Environment &Env) {
Index: clang/include/clang/Format/Format.h
===================================================================
--- clang/include/clang/Format/Format.h
+++ clang/include/clang/Format/Format.h
@@ -982,6 +982,30 @@
/// \version 12
TrailingCommaStyle InsertTrailingCommas;
+ /// The style of inserting braces in control flow statements.
+ enum BraceInsertionStyle : unsigned char {
+ /// Do not insert braces.
+ BIS_Never,
+ /// Always insert braces.
+ BIS_Always,
+ /// Insert braces if the line is likely to wrap.
+ BIS_WrapLikely,
+ };
+
+ /// If set to ``BIS_WrapLikely`` will insert braces if the line after the
+ /// control flow statement is likely to wrap.
+ /// If set to ``BIS_Always`` will always insert braces.
+ /// The default is the disabled value of ``BIS_Never``.
+ /// \warning
+ /// Setting ``InsertBraces`` to something other than `Never`, COULD
+ /// lead to incorrect code formatting due to incorrect decisions made due to
+ /// clang-formats lack of complete semantic information.
+ /// As such extra care should be taken to review code changes made by the use
+ /// of this option.
+ /// \endwarning
+ /// \version 14
+ BraceInsertionStyle InsertBraces;
+
/// If ``false``, a function declaration's or function definition's
/// parameters will either all be on the same line or will have one line each.
/// \code
@@ -3666,7 +3690,7 @@
IndentPPDirectives == R.IndentPPDirectives &&
IndentExternBlock == R.IndentExternBlock &&
IndentRequires == R.IndentRequires && IndentWidth == R.IndentWidth &&
- Language == R.Language &&
+ InsertBraces == R.InsertBraces && Language == R.Language &&
IndentWrappedFunctionNames == R.IndentWrappedFunctionNames &&
JavaImportGroups == R.JavaImportGroups &&
JavaScriptQuotes == R.JavaScriptQuotes &&
Index: clang/docs/ClangFormatStyleOptions.rst
===================================================================
--- clang/docs/ClangFormatStyleOptions.rst
+++ clang/docs/ClangFormatStyleOptions.rst
@@ -2717,6 +2717,33 @@
LoooooooooooooooooooooooooooooooooooooooongReturnType
LoooooooooooooooooooooooooooooooongFunctionDeclaration();
+**InsertBraces** (``BraceInsertionStyle``) :versionbadge:`clang-format 14`
+ If set to ``BIS_WrapLikely`` will insert braces if the line after the
+ control flow statement is likely to wrap.
+ If set to ``BIS_Always`` will always insert braces.
+ The default is the disabled value of ``BIS_Never``.
+
+ .. warning::
+
+ Setting ``InsertBraces`` to something other than `Never`, COULD
+ lead to incorrect code formatting due to incorrect decisions made due to
+ clang-formats lack of complete semantic information.
+ As such extra care should be taken to review code changes made by the use
+ of this option.
+
+ Possible values:
+
+ * ``BIS_Never`` (in configuration: ``Never``)
+ Do not insert braces.
+
+ * ``BIS_Always`` (in configuration: ``Always``)
+ Always insert braces.
+
+ * ``BIS_WrapLikely`` (in configuration: ``WrapLikely``)
+ Insert braces if the line is likely to wrap.
+
+
+
**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.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits