https://github.com/5chmidti updated 
https://github.com/llvm/llvm-project/pull/71245

>From 6a0a568fe0887c9665aa78bd51610e063d022c19 Mon Sep 17 00:00:00 2001
From: Julian Schmidt <44101708+5chmi...@users.noreply.github.com>
Date: Fri, 3 Nov 2023 21:51:57 +0100
Subject: [PATCH] [clang][ASTMatcher] Add matchers for CXXFoldExpr

---
 clang/docs/LibASTMatchersReference.html       | 213 +++++++++++++++++-
 clang/docs/ReleaseNotes.rst                   |   3 +
 clang/include/clang/ASTMatchers/ASTMatchers.h | 199 ++++++++++++++--
 clang/lib/ASTMatchers/ASTMatchersInternal.cpp |   1 +
 clang/lib/ASTMatchers/Dynamic/Registry.cpp    |   2 +-
 clang/unittests/AST/ASTImporterTest.cpp       |  18 +-
 .../ASTMatchers/ASTMatchersNarrowingTest.cpp  |  99 +++++++-
 .../ASTMatchers/ASTMatchersNodeTest.cpp       |  13 ++
 .../ASTMatchers/ASTMatchersTraversalTest.cpp  |  87 +++++++
 9 files changed, 592 insertions(+), 43 deletions(-)

diff --git a/clang/docs/LibASTMatchersReference.html 
b/clang/docs/LibASTMatchersReference.html
index fcd3114bb523105..d559732c0fe4764 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -1629,6 +1629,17 @@ <h2 id="decl-matchers">Node Matchers</h2>
 </pre></td></tr>
 
 
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html";>Stmt</a>&gt;</td><td
 class="name" onclick="toggle('cxxFoldExpr0')"><a 
name="cxxFoldExpr0Anchor">cxxFoldExpr</a></td><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html";>CXXFoldExpr</a>&gt;...</td></tr>
+<tr><td colspan="4" class="doc" id="cxxFoldExpr0"><pre>Matches C++17 fold 
expressions.
+
+Example matches `(0 + ... + args)`:
+  template &lt;typename... Args&gt;
+  auto sum(Args... args) {
+      return (0 + ... + args);
+  }
+</pre></td></tr>
+
+
 <tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html";>Stmt</a>&gt;</td><td
 class="name" onclick="toggle('cxxForRangeStmt0')"><a 
name="cxxForRangeStmt0Anchor">cxxForRangeStmt</a></td><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXForRangeStmt.html";>CXXForRangeStmt</a>&gt;...</td></tr>
 <tr><td colspan="4" class="doc" id="cxxForRangeStmt0"><pre>Matches range-based 
for statements.
 
@@ -3430,6 +3441,92 @@ <h2 id="narrowing-matchers">Narrowing Matchers</h2>
 </pre></td></tr>
 
 
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html";>CXXFoldExpr</a>&gt;</td><td
 class="name" onclick="toggle('hasOperator0')"><a 
name="hasOperator0Anchor">hasOperator</a></td><td>BinaryOperatorKind 
Op</td></tr>
+<tr><td colspan="4" class="doc" id="hasOperator0"><pre>Matches the operator 
kind of the fold expression.
+
+Example matches `(0 + ... + args)`
+    (matcher = cxxFoldExpr(hasOperator(BO_Add)))
+  template &lt;typename... Args&gt;
+  auto sum(Args... args) {
+      return (0 + ... + args);
+  }
+
+  template &lt;typename... Args&gt;
+  auto multiply(Args... args) {
+      return (args * ... * 1);
+  }
+</pre></td></tr>
+
+
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html";>CXXFoldExpr</a>&gt;</td><td
 class="name" onclick="toggle('isBinaryFold0')"><a 
name="isBinaryFold0Anchor">isBinaryFold</a></td><td></td></tr>
+<tr><td colspan="4" class="doc" id="isBinaryFold0"><pre>Matches binary fold 
expressions, i.e. fold expressions with an initializer.
+
+Example matches `(0 + ... + args)`
+    (matcher = cxxFoldExpr(isBinaryFold()))
+  template &lt;typename... Args&gt;
+  auto sum(Args... args) {
+      return (0 + ... + args);
+  }
+
+  template &lt;typename... Args&gt;
+  auto multiply(Args... args) {
+      return (args * ...);
+  }
+</pre></td></tr>
+
+
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html";>CXXFoldExpr</a>&gt;</td><td
 class="name" onclick="toggle('isLeftFold0')"><a 
name="isLeftFold0Anchor">isLeftFold</a></td><td></td></tr>
+<tr><td colspan="4" class="doc" id="isLeftFold0"><pre>Matches left-folding 
fold expressions.
+
+Example matches `(0 + ... + args)`
+    (matcher = cxxFoldExpr(isLeftFold()))
+  template &lt;typename... Args&gt;
+  auto sum(Args... args) {
+      return (0 + ... + args);
+  }
+
+  template &lt;typename... Args&gt;
+  auto multiply(Args... args) {
+      return (args * ... * 1);
+  }
+</pre></td></tr>
+
+
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html";>CXXFoldExpr</a>&gt;</td><td
 class="name" onclick="toggle('isRightFold0')"><a 
name="isRightFold0Anchor">isRightFold</a></td><td></td></tr>
+<tr><td colspan="4" class="doc" id="isRightFold0"><pre>Matches right-folding 
fold expressions.
+
+Example matches `(args * ... * 1)`
+    (matcher = cxxFoldExpr(isRightFold()))
+  template &lt;typename... Args&gt;
+  auto sum(Args... args) {
+      return (0 + ... + args);
+  }
+
+  template &lt;typename... Args&gt;
+  auto multiply(Args... args) {
+      return (args * ... * 1);
+  }
+</pre></td></tr>
+
+
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html";>CXXFoldExpr</a>&gt;</td><td
 class="name" onclick="toggle('isUnaryFold0')"><a 
name="isUnaryFold0Anchor">isUnaryFold</a></td><td></td></tr>
+<tr><td colspan="4" class="doc" id="isUnaryFold0"><pre>Matches unary fold 
expressions, i.e. fold expressions without an
+initializer.
+
+Example matches `(args * ...)`
+    (matcher = cxxFoldExpr(isUnaryFold()))
+  template &lt;typename... Args&gt;
+  auto sum(Args... args) {
+      return (0 + ... + args);
+  }
+
+  template &lt;typename... Args&gt;
+  auto multiply(Args... args) {
+      return (args * ...);
+  }
+</pre></td></tr>
+
+
 <tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html";>CXXMethodDecl</a>&gt;</td><td
 class="name" onclick="toggle('isConst0')"><a 
name="isConst0Anchor">isConst</a></td><td></td></tr>
 <tr><td colspan="4" class="doc" id="isConst0"><pre>Matches if the given method 
declaration is const.
 
@@ -6885,6 +6982,93 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
 </pre></td></tr>
 
 
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html";>CXXFoldExpr</a>&gt;</td><td
 class="name" onclick="toggle('callee1')"><a 
name="callee1Anchor">callee</a></td><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html";>Stmt</a>&gt; 
InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="callee1"><pre>Matches if the call or fold 
expression's callee expression matches.
+
+Given
+  class Y { void x() { this-&gt;x(); x(); Y y; y.x(); } };
+  void f() { f(); }
+callExpr(callee(expr()))
+  matches this-&gt;x(), x(), y.x(), f()
+with callee(...)
+  matching this-&gt;x, x, y.x, f respectively
+
+Given
+  template &lt;typename... Args&gt;
+  auto sum(Args... args) {
+      return (0 + ... + args);
+  }
+
+  template &lt;typename... Args&gt;
+  auto multiply(Args... args) {
+      return (args * ... * 1);
+  }
+cxxFoldExpr(callee(expr()))
+  matches (args * ... * 1)
+with callee(...)
+  matching *
+
+Note: Callee cannot take the more general internal::Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt;
+because this introduces ambiguous overloads with calls to Callee taking a
+internal::Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html";>Decl</a>&gt;, as 
the matcher hierarchy is purely
+implemented in terms of implicit casts.
+</pre></td></tr>
+
+
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html";>CXXFoldExpr</a>&gt;</td><td
 class="name" onclick="toggle('hasInit0')"><a 
name="hasInit0Anchor">hasInit</a></td><td>ast_matchers::Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt; 
InnerMacher</td></tr>
+<tr><td colspan="4" class="doc" id="hasInit0"><pre>Matches the operand that 
does not contain the parameter pack.
+
+Example matches `(0 + ... + args)` and `(args * ... * 1)`
+    (matcher = cxxFoldExpr(hasInit(expr())))
+  with hasInit(...)
+    matching `0` and `1` respectively
+  template &lt;typename... Args&gt;
+  auto sum(Args... args) {
+      return (0 + ... + args);
+  }
+
+  template &lt;typename... Args&gt;
+  auto multiply(Args... args) {
+      return (args * ... * 1);
+  }
+</pre></td></tr>
+
+
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html";>CXXFoldExpr</a>&gt;</td><td
 class="name" onclick="toggle('hasLHS4')"><a 
name="hasLHS4Anchor">hasLHS</a></td><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt; 
InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasLHS4"><pre>Matches the left hand side 
of binary operator expressions.
+
+Example matches a (matcher = binaryOperator(hasLHS()))
+  a || b
+</pre></td></tr>
+
+
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html";>CXXFoldExpr</a>&gt;</td><td
 class="name" onclick="toggle('hasPattern0')"><a 
name="hasPattern0Anchor">hasPattern</a></td><td>ast_matchers::Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt; 
InnerMacher</td></tr>
+<tr><td colspan="4" class="doc" id="hasPattern0"><pre>Matches the operand that 
contains the parameter pack.
+
+Example matches `(0 + ... + args)`
+    (matcher = cxxFoldExpr(hasPattern(expr())))
+  with hasPattern(...)
+    matching `args`
+  template &lt;typename... Args&gt;
+  auto sum(Args... args) {
+      return (0 + ... + args);
+  }
+
+  template &lt;typename... Args&gt;
+  auto multiply(Args... args) {
+      return (args * ... * 1);
+  }
+</pre></td></tr>
+
+
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXFoldExpr.html";>CXXFoldExpr</a>&gt;</td><td
 class="name" onclick="toggle('hasRHS4')"><a 
name="hasRHS4Anchor">hasRHS</a></td><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt; 
InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasRHS4"><pre>Matches the right hand side 
of binary operator expressions.
+
+Example matches b (matcher = binaryOperator(hasRHS()))
+  a || b
+</pre></td></tr>
+
+
 <tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CXXForRangeStmt.html";>CXXForRangeStmt</a>&gt;</td><td
 class="name" onclick="toggle('hasBody3')"><a 
name="hasBody3Anchor">hasBody</a></td><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html";>Stmt</a>&gt; 
InnerMatcher</td></tr>
 <tr><td colspan="4" class="doc" id="hasBody3"><pre>Matches a 'for', 'while', 
'while' statement or a function or coroutine
 definition that has a given body. Note that in case of functions or
@@ -7436,8 +7620,8 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
 </pre></td></tr>
 
 
-<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CallExpr.html";>CallExpr</a>&gt;</td><td
 class="name" onclick="toggle('callee2')"><a 
name="callee2Anchor">callee</a></td><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html";>Decl</a>&gt; 
InnerMatcher</td></tr>
-<tr><td colspan="4" class="doc" id="callee2"><pre>Matches 1) if the call 
expression's callee's declaration matches the
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CallExpr.html";>CallExpr</a>&gt;</td><td
 class="name" onclick="toggle('callee3')"><a 
name="callee3Anchor">callee</a></td><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html";>Decl</a>&gt; 
InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="callee3"><pre>Matches 1) if the call 
expression's callee's declaration matches the
 given matcher; or 2) if the Obj-C message expression's callee's method
 declaration matches the given matcher.
 
@@ -7458,7 +7642,7 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
 
 
 <tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1CallExpr.html";>CallExpr</a>&gt;</td><td
 class="name" onclick="toggle('callee0')"><a 
name="callee0Anchor">callee</a></td><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Stmt.html";>Stmt</a>&gt; 
InnerMatcher</td></tr>
-<tr><td colspan="4" class="doc" id="callee0"><pre>Matches if the call 
expression's callee expression matches.
+<tr><td colspan="4" class="doc" id="callee0"><pre>Matches if the call or fold 
expression's callee expression matches.
 
 Given
   class Y { void x() { this-&gt;x(); x(); Y y; y.x(); } };
@@ -7468,6 +7652,21 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
 with callee(...)
   matching this-&gt;x, x, y.x, f respectively
 
+Given
+  template &lt;typename... Args&gt;
+  auto sum(Args... args) {
+      return (0 + ... + args);
+  }
+
+  template &lt;typename... Args&gt;
+  auto multiply(Args... args) {
+      return (args * ... * 1);
+  }
+cxxFoldExpr(callee(expr()))
+  matches (args * ... * 1)
+with callee(...)
+  matching *
+
 Note: Callee cannot take the more general internal::Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt;
 because this introduces ambiguous overloads with calls to Callee taking a
 internal::Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html";>Decl</a>&gt;, as 
the matcher hierarchy is purely
@@ -8711,8 +8910,8 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
 </pre></td></tr>
 
 
-<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1InitListExpr.html";>InitListExpr</a>&gt;</td><td
 class="name" onclick="toggle('hasInit0')"><a 
name="hasInit0Anchor">hasInit</a></td><td>unsigned N, 
ast_matchers::Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt; 
InnerMatcher</td></tr>
-<tr><td colspan="4" class="doc" id="hasInit0"><pre>Matches the n'th item of an 
initializer list expression.
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1InitListExpr.html";>InitListExpr</a>&gt;</td><td
 class="name" onclick="toggle('hasInit1')"><a 
name="hasInit1Anchor">hasInit</a></td><td>unsigned N, 
ast_matchers::Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html";>Expr</a>&gt; 
InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="hasInit1"><pre>Matches the n'th item of an 
initializer list expression.
 
 Example matches y.
     (matcher = initListExpr(hasInit(0, expr())))
@@ -9087,8 +9286,8 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2>
 </pre></td></tr>
 
 
-<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1ObjCMessageExpr.html";>ObjCMessageExpr</a>&gt;</td><td
 class="name" onclick="toggle('callee1')"><a 
name="callee1Anchor">callee</a></td><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html";>Decl</a>&gt; 
InnerMatcher</td></tr>
-<tr><td colspan="4" class="doc" id="callee1"><pre>Matches 1) if the call 
expression's callee's declaration matches the
+<tr><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1ObjCMessageExpr.html";>ObjCMessageExpr</a>&gt;</td><td
 class="name" onclick="toggle('callee2')"><a 
name="callee2Anchor">callee</a></td><td>Matcher&lt;<a 
href="https://clang.llvm.org/doxygen/classclang_1_1Decl.html";>Decl</a>&gt; 
InnerMatcher</td></tr>
+<tr><td colspan="4" class="doc" id="callee2"><pre>Matches 1) if the call 
expression's callee's declaration matches the
 given matcher; or 2) if the Obj-C message expression's callee's method
 declaration matches the given matcher.
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index afe7e2e79c2d087..9f753af4f7dfe6f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -830,6 +830,9 @@ AST Matchers
 - Add ``convertVectorExpr``.
 - Add ``dependentSizedExtVectorType``.
 - Add ``macroQualifiedType``.
+- Add ``CXXFoldExpr`` related matchers: ``cxxFoldExpr``, ``callee``,
+  ``hasInit``, ``hasPattern``, ``isRightFold``, ``isLeftFold``,
+  ``isUnaryFold``, ``isBinaryFold``, ``hasOperator``, ``hasLHS``, ``hasRHS``.
 
 clang-format
 ------------
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h 
b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 82a26356c58f556..993ec0b7213fdb0 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -2062,6 +2062,18 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, 
CXXDefaultArgExpr>
 extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXOperatorCallExpr>
     cxxOperatorCallExpr;
 
+/// Matches C++17 fold expressions.
+///
+/// Example matches `(0 + ... + args)`:
+/// \code
+///   template <typename... Args>
+///   auto sum(Args... args) {
+///       return (0 + ... + args);
+///   }
+/// \endcode
+extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXFoldExpr>
+    cxxFoldExpr;
+
 /// Matches rewritten binary operators
 ///
 /// Example matches use of "<":
@@ -3881,7 +3893,7 @@ AST_MATCHER_P(ObjCMessageExpr, numSelectorArgs, unsigned, 
N) {
   return Node.getSelector().getNumArgs() == N;
 }
 
-/// Matches if the call expression's callee expression matches.
+/// Matches if the call or fold expression's callee expression matches.
 ///
 /// Given
 /// \code
@@ -3893,13 +3905,32 @@ AST_MATCHER_P(ObjCMessageExpr, numSelectorArgs, 
unsigned, N) {
 /// with callee(...)
 ///   matching this->x, x, y.x, f respectively
 ///
+/// Given
+/// \code
+///   template <typename... Args>
+///   auto sum(Args... args) {
+///       return (0 + ... + args);
+///   }
+///
+///   template <typename... Args>
+///   auto multiply(Args... args) {
+///       return (args * ... * 1);
+///   }
+/// \endcode
+/// cxxFoldExpr(callee(expr()))
+///   matches (args * ... * 1)
+/// with callee(...)
+///   matching *
+///
 /// Note: Callee cannot take the more general internal::Matcher<Expr>
 /// because this introduces ambiguous overloads with calls to Callee taking a
 /// internal::Matcher<Decl>, as the matcher hierarchy is purely
 /// implemented in terms of implicit casts.
-AST_MATCHER_P(CallExpr, callee, internal::Matcher<Stmt>,
-              InnerMatcher) {
-  const Expr *ExprNode = Node.getCallee();
+AST_POLYMORPHIC_MATCHER_P_OVERLOAD(callee,
+                                   AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr,
+                                                                   
CXXFoldExpr),
+                                   internal::Matcher<Stmt>, InnerMatcher, 0) {
+  const auto *ExprNode = Node.getCallee();
   return (ExprNode != nullptr &&
           InnerMatcher.matches(*ExprNode, Finder, Builder));
 }
@@ -4532,6 +4563,139 @@ AST_POLYMORPHIC_MATCHER_P2(hasArgument,
   return InnerMatcher.matches(*Arg->IgnoreParenImpCasts(), Finder, Builder);
 }
 
+/// Matches the operand that does not contain the parameter pack.
+///
+/// Example matches `(0 + ... + args)` and `(args * ... * 1)`
+///     (matcher = cxxFoldExpr(hasInit(expr())))
+///   with hasInit(...)
+///     matching `0` and `1` respectively
+/// \code
+///   template <typename... Args>
+///   auto sum(Args... args) {
+///       return (0 + ... + args);
+///   }
+///
+///   template <typename... Args>
+///   auto multiply(Args... args) {
+///       return (args * ... * 1);
+///   }
+/// \endcode
+AST_MATCHER_P_OVERLOAD(CXXFoldExpr, hasInit,
+                       ast_matchers::internal::Matcher<Expr>, InnerMacher, 0) {
+  const auto *const Init = Node.getInit();
+  return Init && InnerMacher.matches(*Init, Finder, Builder);
+}
+
+/// Matches the operand that contains the parameter pack.
+///
+/// Example matches `(0 + ... + args)`
+///     (matcher = cxxFoldExpr(hasPattern(expr())))
+///   with hasPattern(...)
+///     matching `args`
+/// \code
+///   template <typename... Args>
+///   auto sum(Args... args) {
+///       return (0 + ... + args);
+///   }
+///
+///   template <typename... Args>
+///   auto multiply(Args... args) {
+///       return (args * ... * 1);
+///   }
+/// \endcode
+AST_MATCHER_P(CXXFoldExpr, hasPattern, ast_matchers::internal::Matcher<Expr>,
+              InnerMacher) {
+  return InnerMacher.matches(*Node.getPattern(), Finder, Builder);
+}
+
+/// Matches right-folding fold expressions.
+///
+/// Example matches `(args * ... * 1)`
+///     (matcher = cxxFoldExpr(isRightFold()))
+/// \code
+///   template <typename... Args>
+///   auto sum(Args... args) {
+///       return (0 + ... + args);
+///   }
+///
+///   template <typename... Args>
+///   auto multiply(Args... args) {
+///       return (args * ... * 1);
+///   }
+/// \endcode
+AST_MATCHER(CXXFoldExpr, isRightFold) { return Node.isRightFold(); }
+
+/// Matches left-folding fold expressions.
+///
+/// Example matches `(0 + ... + args)`
+///     (matcher = cxxFoldExpr(isLeftFold()))
+/// \code
+///   template <typename... Args>
+///   auto sum(Args... args) {
+///       return (0 + ... + args);
+///   }
+///
+///   template <typename... Args>
+///   auto multiply(Args... args) {
+///       return (args * ... * 1);
+///   }
+/// \endcode
+AST_MATCHER(CXXFoldExpr, isLeftFold) { return Node.isLeftFold(); }
+
+/// Matches unary fold expressions, i.e. fold expressions without an
+/// initializer.
+///
+/// Example matches `(args * ...)`
+///     (matcher = cxxFoldExpr(isUnaryFold()))
+/// \code
+///   template <typename... Args>
+///   auto sum(Args... args) {
+///       return (0 + ... + args);
+///   }
+///
+///   template <typename... Args>
+///   auto multiply(Args... args) {
+///       return (args * ...);
+///   }
+/// \endcode
+AST_MATCHER(CXXFoldExpr, isUnaryFold) { return Node.getInit() == nullptr; }
+
+/// Matches binary fold expressions, i.e. fold expressions with an initializer.
+///
+/// Example matches `(0 + ... + args)`
+///     (matcher = cxxFoldExpr(isBinaryFold()))
+/// \code
+///   template <typename... Args>
+///   auto sum(Args... args) {
+///       return (0 + ... + args);
+///   }
+///
+///   template <typename... Args>
+///   auto multiply(Args... args) {
+///       return (args * ...);
+///   }
+/// \endcode
+AST_MATCHER(CXXFoldExpr, isBinaryFold) { return Node.getInit() != nullptr; }
+
+/// Matches the operator kind of the fold expression.
+///
+/// Example matches `(0 + ... + args)`
+///     (matcher = cxxFoldExpr(hasOperator(BO_Add)))
+/// \code
+///   template <typename... Args>
+///   auto sum(Args... args) {
+///       return (0 + ... + args);
+///   }
+///
+///   template <typename... Args>
+///   auto multiply(Args... args) {
+///       return (args * ... * 1);
+///   }
+/// \endcode
+AST_MATCHER_P(CXXFoldExpr, hasOperator, BinaryOperatorKind, Op) {
+  return Node.getOperator() == Op;
+}
+
 /// Matches the n'th item of an initializer list expression.
 ///
 /// Example matches y.
@@ -4539,8 +4703,9 @@ AST_POLYMORPHIC_MATCHER_P2(hasArgument,
 /// \code
 ///   int x{y}.
 /// \endcode
-AST_MATCHER_P2(InitListExpr, hasInit, unsigned, N,
-               ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+AST_MATCHER_P2_OVERLOAD(InitListExpr, hasInit, unsigned, N,
+                        ast_matchers::internal::Matcher<Expr>, InnerMatcher,
+                        1) {
   return N < Node.getNumInits() &&
           InnerMatcher.matches(*Node.getInit(N), Finder, Builder);
 }
@@ -5789,11 +5954,12 @@ AST_POLYMORPHIC_MATCHER(
 /// \code
 ///   a || b
 /// \endcode
-AST_POLYMORPHIC_MATCHER_P(hasLHS,
-                          AST_POLYMORPHIC_SUPPORTED_TYPES(
-                              BinaryOperator, CXXOperatorCallExpr,
-                              CXXRewrittenBinaryOperator, ArraySubscriptExpr),
-                          internal::Matcher<Expr>, InnerMatcher) {
+AST_POLYMORPHIC_MATCHER_P(
+    hasLHS,
+    AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
+                                    CXXRewrittenBinaryOperator,
+                                    ArraySubscriptExpr, CXXFoldExpr),
+    internal::Matcher<Expr>, InnerMatcher) {
   const Expr *LeftHandSide = internal::getLHS(Node);
   return (LeftHandSide != nullptr &&
           InnerMatcher.matches(*LeftHandSide, Finder, Builder));
@@ -5805,11 +5971,12 @@ AST_POLYMORPHIC_MATCHER_P(hasLHS,
 /// \code
 ///   a || b
 /// \endcode
-AST_POLYMORPHIC_MATCHER_P(hasRHS,
-                          AST_POLYMORPHIC_SUPPORTED_TYPES(
-                              BinaryOperator, CXXOperatorCallExpr,
-                              CXXRewrittenBinaryOperator, ArraySubscriptExpr),
-                          internal::Matcher<Expr>, InnerMatcher) {
+AST_POLYMORPHIC_MATCHER_P(
+    hasRHS,
+    AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
+                                    CXXRewrittenBinaryOperator,
+                                    ArraySubscriptExpr, CXXFoldExpr),
+    internal::Matcher<Expr>, InnerMatcher) {
   const Expr *RightHandSide = internal::getRHS(Node);
   return (RightHandSide != nullptr &&
           InnerMatcher.matches(*RightHandSide, Finder, Builder));
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp 
b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 435bbdeda22066e..616e6a959e0f59d 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -893,6 +893,7 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, 
CXXOperatorCallExpr>
     cxxOperatorCallExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, CXXRewrittenBinaryOperator>
     cxxRewrittenBinaryOperator;
+const internal::VariadicDynCastAllOfMatcher<Stmt, CXXFoldExpr> cxxFoldExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, Expr> expr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, DeclRefExpr> declRefExpr;
 const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCIvarRefExpr> 
objcIvarRefExpr;
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp 
b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index 2e43dec331b75f8..1a711c852242079 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -106,6 +106,7 @@ RegistryMaps::RegistryMaps() {
                   std::make_unique<internal::MapAnyOfBuilderDescriptor>());
 
   REGISTER_OVERLOADED_2(callee);
+  REGISTER_OVERLOADED_2(hasInit);
   REGISTER_OVERLOADED_2(hasPrefix);
   REGISTER_OVERLOADED_2(hasType);
   REGISTER_OVERLOADED_2(ignoringParens);
@@ -324,7 +325,6 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(hasInClassInitializer);
   REGISTER_MATCHER(hasIncrement);
   REGISTER_MATCHER(hasIndex);
-  REGISTER_MATCHER(hasInit);
   REGISTER_MATCHER(hasInitializer);
   REGISTER_MATCHER(hasInitStatement);
   REGISTER_MATCHER(hasKeywordSelector);
diff --git a/clang/unittests/AST/ASTImporterTest.cpp 
b/clang/unittests/AST/ASTImporterTest.cpp
index 5f4d8d040772cb1..0b095ab3384c3c7 100644
--- a/clang/unittests/AST/ASTImporterTest.cpp
+++ b/clang/unittests/AST/ASTImporterTest.cpp
@@ -803,22 +803,14 @@ TEST_P(ImportExpr, ImportSizeOfPackExpr) {
           hasUnqualifiedDesugaredType(constantArrayType(hasSize(7))))))))));
 }
 
-const internal::VariadicDynCastAllOfMatcher<Stmt, CXXFoldExpr> cxxFoldExpr;
-
-AST_MATCHER_P(CXXFoldExpr, hasOperator, BinaryOperatorKind, Op) {
-  return Node.getOperator() == Op;
-}
-AST_MATCHER(CXXFoldExpr, hasInit) { return Node.getInit(); }
-AST_MATCHER(CXXFoldExpr, isRightFold) { return Node.isRightFold(); }
-AST_MATCHER(CXXFoldExpr, isLeftFold) { return Node.isLeftFold(); }
-
 TEST_P(ImportExpr, ImportCXXFoldExpr) {
   auto Match1 =
-      cxxFoldExpr(hasOperator(BO_Add), isLeftFold(), unless(hasInit()));
-  auto Match2 = cxxFoldExpr(hasOperator(BO_Sub), isLeftFold(), hasInit());
+      cxxFoldExpr(hasOperator(BO_Add), isLeftFold(), unless(hasInit(expr())));
+  auto Match2 = cxxFoldExpr(hasOperator(BO_Sub), isLeftFold(), 
hasInit(expr()));
   auto Match3 =
-      cxxFoldExpr(hasOperator(BO_Mul), isRightFold(), unless(hasInit()));
-  auto Match4 = cxxFoldExpr(hasOperator(BO_Div), isRightFold(), hasInit());
+      cxxFoldExpr(hasOperator(BO_Mul), isRightFold(), unless(hasInit(expr())));
+  auto Match4 =
+      cxxFoldExpr(hasOperator(BO_Div), isRightFold(), hasInit(expr()));
 
   MatchVerifier<Decl> Verifier;
   testImport("template <typename... Ts>"
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp 
b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
index d78676fd289d5e5..ff107995715c745 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
@@ -4103,15 +4103,102 @@ TEST_P(ASTMatchersTest, IsComparisonOperator) {
       notMatches("void x() { int a; if(a = 0) return; }", BinCompOperator));
 }
 
-TEST_P(ASTMatchersTest, HasInit) {
-  if (!GetParam().isCXX11OrLater()) {
-    // FIXME: Add a test for `hasInit()` that does not depend on C++.
+TEST_P(ASTMatchersTest, isRightFold) {
+  if (!GetParam().isCXX17OrLater()) {
+    return;
+  }
+
+  EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
+                       "return (0 + ... + args); }",
+                       cxxFoldExpr(isRightFold())));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (args + ... + 0); }",
+                      cxxFoldExpr(isRightFold())));
+  EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
+                       "return (... + args); };",
+                       cxxFoldExpr(isRightFold())));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (args + ...); };",
+                      cxxFoldExpr(isRightFold())));
+}
+
+TEST_P(ASTMatchersTest, isLeftFold) {
+  if (!GetParam().isCXX17OrLater()) {
+    return;
+  }
+
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (0 + ... + args); }",
+                      cxxFoldExpr(isLeftFold())));
+  EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
+                       "return (args + ... + 0); }",
+                       cxxFoldExpr(isLeftFold())));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (... + args); };",
+                      cxxFoldExpr(isLeftFold())));
+  EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
+                       "return (args + ...); };",
+                       cxxFoldExpr(isLeftFold())));
+}
+
+TEST_P(ASTMatchersTest, isUnaryFold) {
+  if (!GetParam().isCXX17OrLater()) {
     return;
   }
 
-  EXPECT_TRUE(matches("int x{0};", initListExpr(hasInit(0, expr()))));
-  EXPECT_FALSE(matches("int x{0};", initListExpr(hasInit(1, expr()))));
-  EXPECT_FALSE(matches("int x;", initListExpr(hasInit(0, expr()))));
+  EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
+                       "return (0 + ... + args); }",
+                       cxxFoldExpr(isUnaryFold())));
+  EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
+                       "return (args + ... + 0); }",
+                       cxxFoldExpr(isUnaryFold())));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (... + args); };",
+                      cxxFoldExpr(isUnaryFold())));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (args + ...); };",
+                      cxxFoldExpr(isUnaryFold())));
+}
+
+TEST_P(ASTMatchersTest, isBinaryFold) {
+  if (!GetParam().isCXX17OrLater()) {
+    return;
+  }
+
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (0 + ... + args); }",
+                      cxxFoldExpr(isBinaryFold())));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (args + ... + 0); }",
+                      cxxFoldExpr(isBinaryFold())));
+  EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
+                       "return (... + args); };",
+                       cxxFoldExpr(isBinaryFold())));
+  EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
+                       "return (args + ...); };",
+                       cxxFoldExpr(isBinaryFold())));
+}
+
+TEST_P(ASTMatchersTest, hasOperator) {
+  if (!GetParam().isCXX17OrLater()) {
+    return;
+  }
+
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (0 + ... + args); }",
+                      cxxFoldExpr(hasOperator(BO_Add))));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (... + args); };",
+                      cxxFoldExpr(hasOperator(BO_Add))));
+
+  EXPECT_FALSE(
+      matches("template <typename... Args> auto multiply(Args... args) { "
+              "return (0 * ... * args); }",
+              cxxFoldExpr(hasOperator(BO_Add))));
+  EXPECT_FALSE(
+      matches("template <typename... Args> auto multiply(Args... args) { "
+              "return (... * args); };",
+              cxxFoldExpr(hasOperator(BO_Add))));
 }
 
 TEST_P(ASTMatchersTest, IsMain) {
diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp 
b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
index 8f0dd5602307c53..ae30c03126d768c 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp
@@ -471,6 +471,19 @@ TEST_P(ASTMatchersTest, CXXOperatorCallExpr) {
   EXPECT_TRUE(notMatches("int t = 5 << 2;", OpCall));
 }
 
+TEST_P(ASTMatchersTest, FoldExpr) {
+  if (!GetParam().isCXX() || !GetParam().isCXX17OrLater()) {
+    return;
+  }
+
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (0 + ... + args); }",
+                      cxxFoldExpr()));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (args + ...); }",
+                      cxxFoldExpr()));
+}
+
 TEST_P(ASTMatchersTest, ThisPointerType) {
   if (!GetParam().isCXX()) {
     return;
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp 
b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index d4a695b974bf0e5..fcf68f9eb8bc411 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -2000,6 +2000,93 @@ TEST(Matcher, UnaryOperatorTypes) {
       "void x() { A a; !a; }", unaryOperator(hasOperatorName("!"))));
 }
 
+TEST_P(ASTMatchersTest, HasInit) {
+  if (!GetParam().isCXX11OrLater()) {
+    // FIXME: Add a test for `hasInit()` that does not depend on C++.
+    return;
+  }
+
+  EXPECT_TRUE(matches("int x{0};", initListExpr(hasInit(0, expr()))));
+  EXPECT_FALSE(matches("int x{0};", initListExpr(hasInit(1, expr()))));
+  EXPECT_FALSE(matches("int x;", initListExpr(hasInit(0, expr()))));
+
+  if (!GetParam().isCXX17OrLater()) {
+    return;
+  }
+
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (0 + ... + args); }",
+                      cxxFoldExpr(hasInit(expr()))));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (args + ... + 0); }",
+                      cxxFoldExpr(hasInit(expr()))));
+  EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
+                       "return (... + args); };",
+                       cxxFoldExpr(hasInit(expr()))));
+}
+
+TEST_P(ASTMatchersTest, HasPattern) {
+  if (!GetParam().isCXX17OrLater()) {
+    return;
+  }
+
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (0 + ... + args); }",
+                      cxxFoldExpr(hasPattern(expr()))));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (args + ... + 0); }",
+                      cxxFoldExpr(hasPattern(expr()))));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (... + args); };",
+                      cxxFoldExpr(hasPattern(expr()))));
+}
+
+TEST_P(ASTMatchersTest, HasLHSAndHasRHS) {
+  if (!GetParam().isCXX17OrLater()) {
+    return;
+  }
+
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (0 + ... + args); }",
+                      cxxFoldExpr(hasLHS(expr()))));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (args + ... + 0); }",
+                      cxxFoldExpr(hasLHS(expr()))));
+  EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
+                       "return (... + args); };",
+                       cxxFoldExpr(hasLHS(expr()))));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (args + ...); };",
+                      cxxFoldExpr(hasLHS(expr()))));
+
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (0 + ... + args); }",
+                      cxxFoldExpr(hasRHS(expr()))));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (args + ... + 0); }",
+                      cxxFoldExpr(hasRHS(expr()))));
+  EXPECT_TRUE(matches("template <typename... Args> auto sum(Args... args) { "
+                      "return (... + args); };",
+                      cxxFoldExpr(hasRHS(expr()))));
+  EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
+                       "return (args + ...); };",
+                       cxxFoldExpr(hasRHS(expr()))));
+}
+
+TEST_P(ASTMatchersTest, Callee) {
+  if (!GetParam().isCXX17OrLater()) {
+    return;
+  }
+
+  EXPECT_TRUE(matches(
+      "struct Dummy {}; Dummy operator+(Dummy, Dummy); template "
+      "<typename... Args> auto sum(Args... args) { return (0 + ... + args); }",
+      cxxFoldExpr(callee(expr()))));
+  EXPECT_FALSE(matches("template <typename... Args> auto sum(Args... args) { "
+                       "return (0 + ... + args); }",
+                       cxxFoldExpr(callee(expr()))));
+}
+
 TEST(ArraySubscriptMatchers, ArrayIndex) {
   EXPECT_TRUE(matches(
     "int i[2]; void f() { i[1] = 1; }",

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to