[clang] [clang-format] Add BreakBeforeTemplateClose option (PR #118046)

via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 25 10:44:51 PST 2024


================
@@ -11077,6 +11077,281 @@ TEST_F(FormatTest, WrapsTemplateDeclarationsWithComments) {
       Style);
 }
 
+TEST_F(FormatTest, BreakBeforeTemplateClose) {
+  FormatStyle Style = getGoogleStyle(FormatStyle::LK_Cpp);
+  Style.ColumnLimit = 0;
+  verifyNoChange("template <typename Foo>\n"
+                 "void foo() {}",
+                 Style);
+  verifyNoChange("template <\n"
+                 "    typename Foo,\n"
+                 "    typename Bar>\n"
+                 "void foo() {}",
+                 Style);
+  // when BreakBeforeTemplateClose is off, this line break is removed:
+  verifyFormat("template <\n"
+               "    typename Foo,\n"
+               "    typename Bar>\n"
+               "void foo() {}",
+               "template <\n"
+               "    typename Foo,\n"
+               "    typename Bar\n"
+               ">\n"
+               "void foo() {}",
+               Style);
+  Style.BreakBeforeTemplateClose = true;
+  // BreakBeforeTemplateClose should NOT force multiline templates
+  verifyNoChange("template <typename Foo>\n"
+                 "void foo() {}",
+                 Style);
+  verifyNoChange("template <typename Foo, typename Bar>\n"
+                 "void foo() {}",
+                 Style);
+  // it should allow a line break:
+  verifyNoChange("template <\n"
+                 "    typename Foo\n"
+                 ">\n"
+                 "void foo() {}",
+                 Style);
+  verifyNoChange("template <\n"
+                 "    typename Foo,\n"
+                 "    typename Bar\n"
+                 ">\n"
+                 "void foo() {}",
+                 Style);
+  // it should add a line break if not already present:
+  verifyFormat("template <\n"
+               "    typename Foo\n"
+               ">\n"
+               "void foo() {}",
+               "template <\n"
+               "    typename Foo>\n"
+               "void foo() {}",
+               Style);
+  verifyFormat("template <\n"
+               "    typename Foo,\n"
+               "    typename Bar\n"
+               ">\n"
+               "void foo() {}",
+               "template <\n"
+               "    typename Foo,\n"
+               "    typename Bar>\n"
+               "void foo() {}",
+               Style);
+  // when within an indent scope, the > should be placed appropriately:
+  verifyFormat("struct Baz {\n"
+               "  template <\n"
+               "      typename Foo,\n"
+               "      typename Bar\n"
+               "  >\n"
+               "  void foo() {}\n"
+               "};",
+               "struct Baz {\n"
+               "  template <\n"
+               "      typename Foo,\n"
+               "      typename Bar>\n"
+               "  void foo() {}\n"
+               "};",
+               Style);
+
+  // test from issue #80049
+  verifyFormat(
+      "void foo() {\n"
+      "  using type = std::remove_cv_t<\n"
+      "      add_common_cv_reference<\n"
+      "          std::common_type_t<std::decay_t<T0>, std::decay_t<T1>>,\n"
+      "          T0,\n"
+      "          T1\n"
+      "      >\n"
+      "  >;\n"
+      "}\n",
+      "void foo() {\n"
+      "  using type = std::remove_cv_t<\n"
+      "      add_common_cv_reference<\n"
+      "          std::common_type_t<std::decay_t<T0>, std::decay_t<T1>>,\n"
+      "          T0,\n"
+      "          T1>>;\n"
+      "}\n",
+      Style);
+
+  // test lambda goes to next line:
+  verifyFormat("void foo() {\n"
+               "  auto lambda = []<\n"
+               "                    typename T\n"
+               "                >(T t) {\n"
+               "  };\n"
+               "}\n",
+               "void foo() {\n"
+               "  auto lambda = []<\n"
+               "  typename T>(T t){\n"
+               "  };\n"
+               "}\n",
+               Style);
+  // with no column limit, two parameters can go on the same line:
+  verifyFormat("void foo() {\n"
+               "  auto lambda = []<\n"
+               "                    typename T, typename Foo\n"
+               "                >(T t) {\n"
+               "  };\n"
+               "}\n",
+               "void foo() {\n"
+               "  auto lambda = []<\n"
+               "  typename T, typename Foo>(T t){\n"
+               "  };\n"
+               "}\n",
+               Style);
+  // or on different lines:
+  verifyFormat("void foo() {\n"
+               "  auto lambda = []<\n"
+               "                    typename T,\n"
+               "                    typename Foo\n"
+               "                >(T t) {\n"
+               "  };\n"
+               "}\n",
+               "void foo() {\n"
+               "  auto lambda = []<\n"
+               "  typename T,\n"
+               "  typename Foo>(T t){\n"
+               "  };\n"
+               "}\n",
+               Style);
+
+  // same line with no column limit
+  verifyFormat("void foo() {\n"
+               "  auto lambda = []<typename "
+               "Looooooooooooooooooooooooooooong>("
+               "Looooooooooooooooooooooooooooong t) {};\n"
+               "}\n",
+               Style);
+
+  // test template usage goes to next line:
+  verifyFormat("void foo() {\n"
+               "  myFunc<\n"
+               "      T\n"
+               "  >();\n"
+               "}\n",
+               "void foo() {\n"
+               "  myFunc<\n"
+               "  T>();\n"
+               "}\n",
+               Style);
+
+  // now test that it handles the cases when the column limit forces wrapping
+  Style.ColumnLimit = 40;
+  // when the column limit allows it, the template should be combined back into
+  // one line:
+  verifyFormat("template <typename Foo, typename Bar>\n"
+               "void foo() {}",
+               "template <\n"
+               "    typename Foo,\n"
+               "    typename Bar\n"
+               ">\n"
+               "void foo() {}",
+               Style);
+  // but not when the name is looong
+  verifyFormat("template <\n"
+               "    typename Foo,\n"
+               "    typename Barrrrrrrrrrrrrrrrrrrrrrrrrr\n"
+               ">\n"
+               "void foo() {}",
+               Style);
+  verifyFormat("template <\n"
+               "    typename Fooooooooooooooooooooooooooo,\n"
+               "    typename Bar\n"
+               ">\n"
+               "void foo() {}",
+               Style);
+  // additionally, long names should be split in one step:
+  verifyFormat(
+      "template <\n"
+      "    typename Foo,\n"
+      "    typename Barrrrrrrrrrrrrrrrrrrrrrrrrr\n"
+      ">\n"
+      "void foo() {}",
+      "template <typename Foo, typename Barrrrrrrrrrrrrrrrrrrrrrrrrr>\n"
+      "void foo() {}",
+      Style);
+  verifyFormat(
+      "template <\n"
+      "    typename Fooooooooooooooooooooooooooo,\n"
+      "    typename Bar\n"
+      ">\n"
+      "void foo() {}",
+      "template <typename Fooooooooooooooooooooooooooo, typename Bar>\n"
+      "void foo() {}",
+      Style);
+  // even when there is only one long name:
+  verifyFormat("template <\n"
+               "    typename Fooooooooooooooooooooooooooo\n"
+               ">\n"
+               "void foo() {}",
+               "template <typename Fooooooooooooooooooooooooooo>\n"
+               "void foo() {}",
+               Style);
+  // test lambda goes to next line if the type is looong:
+  verifyFormat(
+      "void foo() {\n"
+      // in this case, breaking "typename Looong" onto the next line would
+      // actually exceed the column limit by even more. same goes for "auto
+      // lambda = []<\n" because then the continuation indent would be all the
+      // way to the "[". therefore, this is correct for the column limited case:
+      "  auto lambda =\n"
+      "      []<typename Loooooooooooooooooooooooooooooooooong\n"
+      "      >(T t) {};\n"
+      // for completeness, let's also make sure it's willing to break if and
+      // when doing so is helpful. if we put something long into the square
+      // brackets, now it's worth it:
+      "  auto lambda =\n"
+      "      [looooooooooooooong]<\n"
+      "          typename Loooooooooooooooooooooooooooooooooong\n"
+      "      >(T t) {};\n"
+      "  auto lambda =\n"
+      "      []<typename T,\n"
+      "         typename Loooooooooooooooooooooooooooooooooong\n"
+      "      >(T t) {};\n"
+      // nested:
+      "  auto lambda =\n"
+      "      []<template <typename, typename>\n"
+      "         typename Looooooooooooooooooong\n"
+      "      >(T t) {};\n"
+      // nested with long capture:
+      "  auto lambda =\n"
+      "      [loooooooooooooooooooong]<\n"
+      "          template <typename, typename>\n"
----------------
leijurv wrote:

No, I don't think so, because the matching `<` is on the same line. In this part of the test, there is a column limit, and it pulls the template back onto one line whenever it can, which I think is good behavior. When there is no column limit, it keeps the state of multiple lines versus one line, which is also covered by the tests.

The analogy would be: if you set `AlignAfterOpenBracket: BlockIndent`, what if it didn't allow you to write `f(x)`, it would always force line breaks after the `(` and before the `)`, meaning all your functions calls were forced to look like this:
```c++
    f(
        x
    )
```

See these parts of the test:

With a column limit, everything gets pulled onto one line, except if the template name is long:
```c++
  // now test that it handles the cases when the column limit forces wrapping
  Style.ColumnLimit = 40;
  // when the column limit allows it, the template should be combined back into
  // one line:
  verifyFormat("template <typename Foo, typename Bar>\n"
               "void foo() {}",
               "template <\n"
               "    typename Foo,\n"
               "    typename Bar\n"
               ">\n"
               "void foo() {}",
               Style);
  // but not when the name is looong
  verifyFormat("template <\n"
               "    typename Foo,\n"
               "    typename Barrrrrrrrrrrrrrrrrrrrrrrrrr\n"
               ">\n"
               "void foo() {}",
               Style);
...
  verifyFormat("void foo() { myFunc<T>(); }\n", Style);
  verifyFormat("void foo() {\n"
               "  myFunc<\n"
               "      Loooooooooooooooooooooooooooooooooooooooong\n"
               "  >();\n"
               "}\n",
               Style);
```

And for the no column limit case, these are the relevant tests to your question:

```c++
  verifyNoChange("template <typename Foo>\n"
                 "void foo() {}",
                 Style);
  verifyNoChange("template <typename Foo, typename Bar>\n"
                 "void foo() {}",
                 Style);
...
  // it should add a line break if not already present:
  verifyFormat("template <\n"
               "    typename Foo\n"
               ">\n"
               "void foo() {}",
               "template <\n"
               "    typename Foo>\n"
               "void foo() {}",
               Style);
  verifyFormat("template <\n"
               "    typename Foo,\n"
               "    typename Bar\n"
               ">\n"
               "void foo() {}",
               "template <\n"
               "    typename Foo,\n"
               "    typename Bar>\n"
               "void foo() {}",
               Style);
```

https://github.com/llvm/llvm-project/pull/118046


More information about the cfe-commits mailing list