modocache created this revision.
modocache added reviewers: rsmith, sammccall, Typz, klimek.
Herald added a project: clang.

When C++ coroutines were adopted as part of the C++20 standard, a change
was committed in https://github.com/llvm/llvm-project/commit/10ab78e854f:
coroutine keywords such as `co_yield` went from being gated on the
presence of the `-fcoroutines-ts` flag, to instead being gated on
`-std=c++2a`. This resulted, perhaps unexpectedly to some users, in a
change in how coroutine keywords were formatted. Because libclangFormat
has only 3 options for formatting according to a language standard --
C++03, C++11, or "auto" -- and because it enabled C++20 keywords for all
settings aside from C++03, users who specified a standard of C++11 in
their style options would have their C++ formatted as if `co_yield` were
a keyword:

- Before, C++03: `co_yield ++i` would be formatted as `co_yield++ i`
- Before, C++11: `co_yield ++i` would be formatted as `co_yield++ i`
- After, C++03: `co_yield ++i` would be formatted as `co_yield++ i`
- After, C++11: `co_yield ++i` would be formatted as `co_yield ++i`

Although the "after" examples above appear like correct formatting
choices to those who are used to seeing coroutine keywords, I would
argue that they aren't technically correct, because a user may define a
variable in C++11 named `co_yield`, and they could increment that
variable by typing `co_yield++`. In this case, clang-format would change
the formatting, despite the user never opting-in to treating `co_yield`
as a keyword.

(There are other examples of clang-format suddenly formatting C++11 code
according to C++20 standards differently as a result of changes like
https://github.com/llvm/llvm-project/commit/10ab78e854f, and I've included
them as tests in this commit, but I won't go into detail explaining them
here.)

To give users the option of formatting according to the C++11 standard,
without the use of coroutines, I've added a style option for the C++20
standard (and, similarly to how the C++11 standard enables C++14, 17,
and 1z, I've written the documentation to indicate using the C++20
option enables any future C++2a standards).

In a future commit, I add a boolean style option to enable coroutines,
so that users may specify they wish to format according to the C++11
standard, but with coroutine keywords enabled.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D65043

Files:
  clang/docs/ClangFormatStyleOptions.rst
  clang/include/clang/Format/Format.h
  clang/lib/Format/Format.cpp
  clang/lib/Format/TokenAnnotator.cpp
  clang/unittests/Format/FormatTest.cpp

Index: clang/unittests/Format/FormatTest.cpp
===================================================================
--- clang/unittests/Format/FormatTest.cpp
+++ clang/unittests/Format/FormatTest.cpp
@@ -3713,10 +3713,20 @@
       "if (aaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaa(\n"
       "        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) == 5) {\n"
       "}");
+
+  // Only in C++20 and above is <=> treated as as operator.
   verifyFormat(
       "if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n"
-      "        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) <=> 5) {\n"
+      "        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) <= >\n"
+      "    5) {\n"
       "}");
+  FormatStyle Cpp20 = getLLVMStyle();
+  Cpp20.Standard = FormatStyle::LS_Cpp20;
+  verifyFormat(
+      "if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n"
+      "        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) <=> 5) {\n"
+      "}", Cpp20);
+
   // Even explicit parentheses stress the precedence enough to make the
   // additional break unnecessary.
   verifyFormat("if ((aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +\n"
@@ -3736,10 +3746,15 @@
                "        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ==\n"
                "    5) {\n"
                "}");
+  // Only in C++20 and above is <=> treated as as operator.
+  verifyFormat("if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +\n"
+               "        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa <=\n"
+               "    > 5) {\n"
+               "}");
   verifyFormat("if (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +\n"
                "        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa <=>\n"
                "    5) {\n"
-               "}");
+               "}", Cpp20);
 
   FormatStyle OnePerLine = getLLVMStyle();
   OnePerLine.BinPackParameters = false;
@@ -13792,6 +13807,18 @@
   verifyFormat("STACK_OF(int*)* a;", Macros);
 }
 
+TEST_F(FormatTest, Coroutines) {
+  FormatStyle Cpp20 = getLLVMStyle();
+  Cpp20.Standard = FormatStyle::LS_Cpp20;
+
+  verifyFormat("co_yield++ i;");
+  verifyFormat("co_yield ++i;", Cpp20);
+
+  verifyFormat("co_await[]() { co_return; }\n"
+               "();");
+  verifyFormat("co_await []() { co_return; }();", Cpp20);
+}
+
 } // end namespace
 } // end namespace format
 } // end namespace clang
Index: clang/lib/Format/TokenAnnotator.cpp
===================================================================
--- clang/lib/Format/TokenAnnotator.cpp
+++ clang/lib/Format/TokenAnnotator.cpp
@@ -2859,7 +2859,8 @@
         (Style.Language == FormatStyle::LK_Proto && Left.is(TT_DictLiteral)))
       return !Style.Cpp11BracedListStyle;
     return Right.is(TT_TemplateCloser) && Left.is(TT_TemplateCloser) &&
-           (Style.Standard != FormatStyle::LS_Cpp11 || Style.SpacesInAngles);
+           (Style.Standard == FormatStyle::LS_Cpp03 ||
+            Style.Standard == FormatStyle::LS_Auto || Style.SpacesInAngles);
   }
   if (Right.isOneOf(tok::arrow, tok::arrowstar, tok::periodstar) ||
       Left.isOneOf(tok::arrow, tok::period, tok::arrowstar, tok::periodstar) ||
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -71,6 +71,8 @@
     IO.enumCase(Value, "C++03", FormatStyle::LS_Cpp03);
     IO.enumCase(Value, "Cpp11", FormatStyle::LS_Cpp11);
     IO.enumCase(Value, "C++11", FormatStyle::LS_Cpp11);
+    IO.enumCase(Value, "Cpp20", FormatStyle::LS_Cpp20);
+    IO.enumCase(Value, "C++20", FormatStyle::LS_Cpp20);
     IO.enumCase(Value, "Auto", FormatStyle::LS_Auto);
   }
 };
@@ -2368,7 +2370,7 @@
   LangOpts.CPlusPlus11 = Style.Standard == FormatStyle::LS_Cpp03 ? 0 : 1;
   LangOpts.CPlusPlus14 = Style.Standard == FormatStyle::LS_Cpp03 ? 0 : 1;
   LangOpts.CPlusPlus17 = Style.Standard == FormatStyle::LS_Cpp03 ? 0 : 1;
-  LangOpts.CPlusPlus2a = Style.Standard == FormatStyle::LS_Cpp03 ? 0 : 1;
+  LangOpts.CPlusPlus2a = Style.Standard == FormatStyle::LS_Cpp20 ? 1 : 0;
   LangOpts.LineComment = 1;
   bool AlternativeOperators = Style.isCpp();
   LangOpts.CXXOperatorNames = AlternativeOperators ? 1 : 0;
Index: clang/include/clang/Format/Format.h
===================================================================
--- clang/include/clang/Format/Format.h
+++ clang/include/clang/Format/Format.h
@@ -1875,6 +1875,10 @@
     /// Use features of C++11, C++14 and C++1z (e.g. ``A<A<int>>`` instead of
     /// ``A<A<int> >``).
     LS_Cpp11,
+    /// Use features of C++20 and C++2a (e.g.: treating ``co_yield`` as a
+    /// keyword, not an identifier, so ``co_yield++ i`` is formatted as
+    /// ``co_yield ++i``).
+    LS_Cpp20,
     /// Automatic detection based on the input.
     LS_Auto
   };
Index: clang/docs/ClangFormatStyleOptions.rst
===================================================================
--- clang/docs/ClangFormatStyleOptions.rst
+++ clang/docs/ClangFormatStyleOptions.rst
@@ -2223,6 +2223,10 @@
     Use features of C++11, C++14 and C++1z (e.g. ``A<A<int>>`` instead of
     ``A<A<int> >``).
 
+  * ``LS_Cpp20`` (in configuration: ``Cpp20``)
+    Use features of C++20 and C++2a (e.g.: treating ``co_yield`` as a keyword,
+    not an identifier, so ``co_yield++ i`` is formatted as ``co_yield ++i``).
+
   * ``LS_Auto`` (in configuration: ``Auto``)
     Automatic detection based on the input.
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to