Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang-format] Don't remove parentheses of fold expressions #91045

Merged
merged 1 commit into from
May 6, 2024

Conversation

owenca
Copy link
Contributor

@owenca owenca commented May 4, 2024

Fixes #90966.

Copy link
Member

@rymiel rymiel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can definitely think of cases where an expression might contain ... and still have redundant parentheses, i.e. if the ellipsis isn't part of a fold expression

For example:

template <typename... N>
std::tuple<N...> foo() {
    return (std::tuple<N...>{});
}

Those parens around the returned expression are redundant, but they would now no longer be removed.

I wouldn't worry too much about this, but, pedantically, you can be sure it's a fold expression if the ellipsis is followed with or preceded by an operator

https://eel.is/c++draft/expr.prim.fold#nt:fold-operator

@llvmbot
Copy link
Collaborator

llvmbot commented May 4, 2024

@llvm/pr-subscribers-clang-format

Author: Owen Pan (owenca)

Changes

Fixes #90966.


Full diff: https://github.com/llvm/llvm-project/pull/91045.diff

2 Files Affected:

  • (modified) clang/lib/Format/UnwrappedLineParser.cpp (+6-1)
  • (modified) clang/unittests/Format/FormatTest.cpp (+9)
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index e8a8dd58d07eea..7d0278bce9a9c6 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -2510,6 +2510,7 @@ bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) {
   assert(FormatTok->is(tok::l_paren) && "'(' expected.");
   auto *LeftParen = FormatTok;
   bool SeenEqual = false;
+  bool MightBeFoldExpr = false;
   const bool MightBeStmtExpr = Tokens->peekNextToken()->is(tok::l_brace);
   nextToken();
   do {
@@ -2521,7 +2522,7 @@ bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) {
         parseChildBlock();
       break;
     case tok::r_paren:
-      if (!MightBeStmtExpr && !Line->InMacroBody &&
+      if (!MightBeStmtExpr && !MightBeFoldExpr && !Line->InMacroBody &&
           Style.RemoveParentheses > FormatStyle::RPS_Leave) {
         const auto *Prev = LeftParen->Previous;
         const auto *Next = Tokens->peekNextToken();
@@ -2564,6 +2565,10 @@ bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) {
         parseBracedList();
       }
       break;
+    case tok::ellipsis:
+      MightBeFoldExpr = true;
+      nextToken();
+      break;
     case tok::equal:
       SeenEqual = true;
       if (Style.isCSharp() && FormatTok->is(TT_FatArrow))
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 32ba6b6853c799..e6f8e4a06515ea 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -27204,8 +27204,14 @@ TEST_F(FormatTest, RemoveParentheses) {
                "if ((({ a; })))\n"
                "  b;",
                Style);
+  verifyFormat("static_assert((std::is_constructible_v<T, Args &&> && ...));",
+               "static_assert(((std::is_constructible_v<T, Args &&> && ...)));",
+               Style);
   verifyFormat("return (0);", "return (((0)));", Style);
   verifyFormat("return (({ 0; }));", "return ((({ 0; })));", Style);
+  verifyFormat("return ((... && std::is_convertible_v<TArgsLocal, TArgs>));",
+               "return (((... && std::is_convertible_v<TArgsLocal, TArgs>)));",
+               Style);
 
   Style.RemoveParentheses = FormatStyle::RPS_ReturnStatement;
   verifyFormat("#define Return0 return (0);", Style);
@@ -27213,6 +27219,9 @@ TEST_F(FormatTest, RemoveParentheses) {
   verifyFormat("co_return 0;", "co_return ((0));", Style);
   verifyFormat("return 0;", "return (((0)));", Style);
   verifyFormat("return ({ 0; });", "return ((({ 0; })));", Style);
+  verifyFormat("return (... && std::is_convertible_v<TArgsLocal, TArgs>);",
+               "return (((... && std::is_convertible_v<TArgsLocal, TArgs>)));",
+               Style);
   verifyFormat("inline decltype(auto) f() {\n"
                "  if (a) {\n"
                "    return (a);\n"

@owenca
Copy link
Contributor Author

owenca commented May 5, 2024

I wouldn't worry too much about this, but, pedantically, you can be sure it's a fold expression if the ellipsis is followed with or preceded by an operator

https://eel.is/c++draft/expr.prim.fold#nt:fold-operator

I had thought of that but decided not to bother. From https://en.cppreference.com/w/cpp/language/fold:

op - any of the following 32 binary operators: + - * / % ^ & | = < > << >> += -= *= /= %= ^= &= |= <<= >>= == != <= >= && || , .* ->*

Because < and > are on the list, we still wouldn't be able to tell if ...> is part of a fold expression. Also, I don't like the overhead of up to 32 comparisons.

@owenca owenca merged commit db0ed55 into llvm:main May 6, 2024
5 of 6 checks passed
@owenca owenca deleted the fold-expr branch May 6, 2024 04:33
@owenca owenca added the invalid-code-generation Tool (e.g. clang-format) produced invalid code that no longer compiles label May 6, 2024
@owenca owenca added this to the LLVM 18.X Release milestone May 6, 2024
@owenca
Copy link
Contributor Author

owenca commented May 6, 2024

/cherry-pick db0ed55

llvmbot pushed a commit to llvmbot/llvm-project that referenced this pull request May 6, 2024
@llvmbot
Copy link
Collaborator

llvmbot commented May 6, 2024

/pull-request #91165

tstellar pushed a commit to llvmbot/llvm-project that referenced this pull request May 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang-format invalid-code-generation Tool (e.g. clang-format) produced invalid code that no longer compiles release:backport
Projects
Development

Successfully merging this pull request may close these issues.

[clang-format] RemoveParentheses option breaks fold expressions
3 participants