tiagoma created this revision.
tiagoma requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

InsertBraces will insert braces for single line control flow statements 
(currently if/else/for).
It currently accepts the following options:

- Never - Keeps the current behavior and never add braces (also default value)
- Always - Always add braces to single lines after the control flow statement
- WrapLikely - If the line after the control flow statement is not braced and 
likely to wrap, braces will be  added


Repository:
  rG LLVM Github Monorepo

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
@@ -14749,6 +14749,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);
@@ -17890,6 +17896,105 @@
             "}",
             format(Source, Style));
 }
+
+TEST_F(FormatTest, InsertBraces) {
+  FormatStyle Style = getLLVMStyle();
+  StringRef IfSourceShort = "if (condition)\n"
+                            "  call_function(arg, arg);";
+  EXPECT_EQ(IfSourceShort, format(IfSourceShort, Style));
+
+  StringRef IfSourceLong = "if (condition)\n"
+                           "  call_function(arg, arg, arg, arg, arg, arg);";
+  EXPECT_EQ(IfSourceLong, format(IfSourceLong, Style));
+
+  StringRef IfElseSourceShort = "if (condition)\n"
+                                "  a_function(arg, arg);\n"
+                                "else\n"
+                                "  another_function(arg, arg);";
+  EXPECT_EQ(IfElseSourceShort, format(IfElseSourceShort, Style));
+
+  StringRef IfElseSourceLong =
+      "if (condition)\n"
+      "  a_function(arg, arg, arg, arg, arg, arg);\n"
+      "else\n"
+      "  another_function(arg, arg, arg, arg, arg, arg);";
+  EXPECT_EQ(IfElseSourceLong, format(IfElseSourceLong, Style));
+
+  StringRef ForSourceShort = "for (auto val : container)\n"
+                             "  call_function(arg, arg);";
+  EXPECT_EQ(ForSourceShort, format(ForSourceShort, Style));
+
+  StringRef ForSourceLong = "for (auto val : container)\n"
+                            "  call_function(arg, arg, arg, arg, arg, arg);";
+  EXPECT_EQ(ForSourceLong, format(ForSourceLong, Style));
+
+  Style.InsertBraces = FormatStyle::BIS_Always;
+
+  EXPECT_EQ("if (condition) {\n"
+            "  call_function(arg, arg);\n"
+            "}",
+            format(IfSourceShort, Style));
+
+  EXPECT_EQ("if (condition) {\n"
+            "  call_function(arg, arg, arg, arg, arg, arg);\n"
+            "}",
+            format(IfSourceLong, Style));
+
+  EXPECT_EQ("if (condition) {\n"
+            "  a_function(arg, arg);\n"
+            "} else {\n"
+            "  another_function(arg, arg);\n"
+            "}",
+            format(IfElseSourceShort, Style));
+
+  EXPECT_EQ("if (condition) {\n"
+            "  a_function(arg, arg, arg, arg, arg, arg);\n"
+            "} else {\n"
+            "  another_function(arg, arg, arg, arg, arg, arg);\n"
+            "}",
+            format(IfElseSourceLong, Style));
+
+  EXPECT_EQ("for (auto val : container) {\n"
+            "  call_function(arg, arg);\n"
+            "}",
+            format(ForSourceShort, Style));
+
+  EXPECT_EQ("for (auto val : container) {\n"
+            "  call_function(arg, arg, arg, arg, arg, arg);\n"
+            "}",
+            format(ForSourceLong, Style));
+
+  Style.InsertBraces = FormatStyle::BIS_WrapLikely;
+  Style.ColumnLimit = 35;
+
+  EXPECT_EQ(IfSourceShort, format(IfSourceShort, Style));
+
+  EXPECT_EQ("if (condition) {\n"
+            "  call_function(arg, arg, arg, arg,\n"
+            "                arg, arg);\n"
+            "}",
+            format(IfSourceLong, Style));
+
+  EXPECT_EQ(IfElseSourceShort, format(IfElseSourceShort, Style));
+
+  EXPECT_EQ("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"
+            "}",
+            format(IfElseSourceLong, Style));
+
+  EXPECT_EQ(ForSourceShort, format(ForSourceShort, Style));
+
+  EXPECT_EQ("for (auto val : container) {\n"
+            "  call_function(arg, arg, arg, arg,\n"
+            "                arg, arg);\n"
+            "}",
+            format(ForSourceLong, Style));
+}
+
 } // namespace
 } // namespace format
 } // namespace clang
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -166,6 +166,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);
@@ -565,6 +573,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);
@@ -932,6 +941,7 @@
   LLVMStyle.IndentRequires = false;
   LLVMStyle.IndentWrappedFunctionNames = false;
   LLVMStyle.IndentWidth = 2;
+  LLVMStyle.InsertBraces = FormatStyle::BIS_Never;
   LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None;
   LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave;
   LLVMStyle.JavaScriptWrapImports = true;
@@ -1645,6 +1655,72 @@
   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();
+      if (!FirstTok)
+        continue;
+
+      // If this token starts a control flow stmt, mark it
+      // We ignore do/while for now as they have a more complex logic
+      if (FirstTok->isOneOf(tok::kw_if, tok::kw_else, tok::kw_for)) {
+        InsideCtrlFlowStmt = true;
+        continue;
+      }
+
+      // if the previous line starte 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, "{")));
+          cantFail(Result.add(tooling::Replacement(
+              Env.getSourceManager(),
+              Line->Last->Tok.getLocation().getLocWithOffset(
+                  Line->Last->TokenText.size()),
+              0, "}")));
+        }
+      }
+      InsideCtrlFlowStmt = false;
+    }
+  }
+  std::set<SourceLocation> Done;
+};
+
 /// TrailingCommaInserter inserts trailing commas into container literals.
 /// E.g.:
 ///     const x = [
@@ -2687,6 +2763,11 @@
       Passes.emplace_back([&](const Environment &Env) {
         return UsingDeclarationsSorter(Env, Expanded).process();
       });
+
+    if (Style.InsertBraces != FormatStyle::BIS_Never)
+      Passes.emplace_back([&](const Environment &Env) {
+        return BracesInserter(Env, Expanded).process();
+      });
   }
 
   if (Style.Language == FormatStyle::LK_JavaScript &&
Index: clang/include/clang/Format/Format.h
===================================================================
--- clang/include/clang/Format/Format.h
+++ clang/include/clang/Format/Format.h
@@ -648,6 +648,22 @@
   /// \endcode
   TrailingCommaStyle InsertTrailingCommas;
 
+  /// The style of inserting braces in control flow statements (if/else/for).
+  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``.
+  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
@@ -2786,7 +2802,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
@@ -2228,6 +2228,44 @@
      LoooooooooooooooooooooooooooooooooooooooongReturnType
      LoooooooooooooooooooooooooooooooongFunctionDeclaration();
 
+**InsertBraces** (``BraceInsertionStyle``)
+  The brace insertion style to use for control flow statements (if/else/for).
+
+  Possible values:
+
+  * ``BIS_Never`` (in configuration: ``Never``)
+    Does not insert braces.
+
+    .. code-block:: c++
+
+       if (condition)
+         funtion_call(arg1, arg2);
+
+  * ``BIS_Always`` (in configuration: ``Always``)
+    Always insert braces.
+
+    .. code-block:: c++
+
+       if (condition) {
+       //             ^ inserted
+         funtion_call(arg1, arg2);
+       } // < inserted
+
+  * ``BIS_WrapLikely`` (in configuration: ``WrapLikely``)
+    Insert braces if wrapping is likely
+
+    .. code-block:: c++
+    
+       if (condition) {
+         short_line(arg1, arg2);
+       }
+
+       if (condition) {
+       //             ^ inserted
+         very_very_long_line(
+           arg1, arg2, arg3);
+       } // < inserted
+
 **InsertTrailingCommas** (``TrailingCommaStyle``)
   If set to ``TCS_Wrapped`` will insert trailing commas in container
   literals (arrays and objects) that wrap across multiple lines.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to