tiagoma updated this revision to Diff 322240.
tiagoma added a comment.

Add support for do/while


Repository:
  rG LLVM Github Monorepo

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;
@@ -15879,6 +15882,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);
@@ -19050,6 +19059,215 @@
             "}",
             format(Source, 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);
+}
+
 } // namespace
 } // namespace format
 } // namespace clang
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -181,6 +181,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);
@@ -606,6 +614,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);
@@ -990,6 +999,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;
@@ -1704,6 +1714,77 @@
   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, "{")));
+          cantFail(Result.add(tooling::Replacement(
+              Env.getSourceManager(),
+              Line->Last->Tok.getLocation().getLocWithOffset(
+                  Line->Last->TokenText.size()),
+              0, "\n}")));
+        }
+      }
+      InsideCtrlFlowStmt = false;
+    }
+  }
+  std::set<SourceLocation> Done;
+};
+
 /// TrailingCommaInserter inserts trailing commas into container literals.
 /// E.g.:
 ///     const x = [
@@ -2759,6 +2840,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
@@ -892,6 +892,22 @@
   /// \endcode
   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``.
+  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
@@ -3162,7 +3178,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
@@ -2543,6 +2543,44 @@
      LoooooooooooooooooooooooooooooooooooooooongReturnType
      LoooooooooooooooooooooooooooooooongFunctionDeclaration();
 
+**InsertBraces** (``BraceInsertionStyle``)
+  The brace insertion style to use for control flow statements.
+
+  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