llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-adt

Author: Denis Mikhailov (denzor200)

<details>
<summary>Changes</summary>

I need this mather at least to implement 
https://github.com/llvm/llvm-project/issues/133110 and 
https://github.com/llvm/llvm-project/issues/38471
Also maybe we will use it in https://github.com/llvm/llvm-project/pull/158462

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


6 Files Affected:

- (modified) clang/include/clang/ASTMatchers/ASTMatchers.h (+25-1) 
- (modified) clang/include/clang/ASTMatchers/ASTMatchersInternal.h (+31) 
- (modified) clang/lib/ASTMatchers/ASTMatchersInternal.cpp (+57) 
- (modified) clang/lib/ASTMatchers/Dynamic/Registry.cpp (+1) 
- (modified) clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp (+200) 
- (modified) llvm/include/llvm/ADT/STLExtras.h (+5) 


``````````diff
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h 
b/clang/include/clang/ASTMatchers/ASTMatchers.h
index bca2d8425b3f5..87d6bd7e1b9a3 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -95,6 +95,7 @@
 #include <limits>
 #include <optional>
 #include <string>
+#include <tuple>
 #include <utility>
 #include <vector>
 
@@ -5889,7 +5890,6 @@ AST_MATCHER_P(FunctionDecl, hasAnyBody,
           InnerMatcher.matches(*Statement, Finder, Builder));
 }
 
-
 /// Matches compound statements where at least one substatement matches
 /// a given matcher. Also matches StmtExprs that have CompoundStmt as children.
 ///
@@ -5911,6 +5911,30 @@ AST_POLYMORPHIC_MATCHER_P(hasAnySubstatement,
                                           Builder) != CS->body_end();
 }
 
+/// Matches compound statements that contain adjacent substatements matching
+/// the provided sequence of matchers. Also matches StmtExprs that have
+/// CompoundStmt as children.
+///
+/// Given
+/// \code
+///   { {}; 1+2; }
+/// \endcode
+/// hasAdjSubstatements(compoundStmt(), binaryOperator())
+///   matches '{ {}; 1+2; }'
+/// with compoundStmt()
+///   matching '{}'
+/// with binaryOperator()
+///   matching '1+2'
+///
+///    hasAdjSubstatements(compoundStmt(), binaryOperator(), returnStmt())
+///  Is equivalent to matching a compound statement that contains
+///  a compound statement immediately followed by a binary operator
+///  immediately followed by a return statement.
+extern const internal::VariadicFunction<
+    internal::HasAdjSubstatementsMatcherType,
+    internal::Matcher<Stmt>, internal::hasAdjSubstatementsFunc>
+    hasAdjSubstatements;
+
 /// Checks that a compound statement contains a specific number of
 /// child statements.
 ///
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h 
b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
index c050fb7d797e3..5fb63af28dc1b 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -2283,6 +2283,37 @@ using HasOpNameMatcher =
 
 HasOpNameMatcher hasAnyOperatorNameFunc(ArrayRef<const StringRef *> NameRefs);
 
+/// Matches nodes of type T (CompoundStmt or StmtExpr) that contain a sequence
+/// of consecutive substatements matching the provided matchers in order.
+///
+/// See \c hasAdjSubstatements() in ASTMatchers.h for details.
+template <typename T, typename ArgT = std::vector<Matcher<Stmt>>>
+class HasAdjSubstatementsMatcher : public MatcherInterface<T> {
+  static_assert(std::is_same<T, CompoundStmt>::value ||
+                    std::is_same<T, StmtExpr>::value,
+                "Matcher only supports `CompoundStmt` and `StmtExpr`");
+  static_assert(std::is_same<ArgT, std::vector<Matcher<Stmt>>>::value,
+                "Matcher ArgT must be std::vector<Matcher<Stmt>>");
+
+public:
+  explicit HasAdjSubstatementsMatcher(std::vector<Matcher<Stmt>> Matchers)
+      : Matchers(std::move(Matchers)) {}
+
+  bool matches(const T &Node, ASTMatchFinder *Finder,
+               BoundNodesTreeBuilder *Builder) const override;
+
+private:
+  std::vector<Matcher<Stmt>> Matchers;
+};
+
+using HasAdjSubstatementsMatcherType =
+    PolymorphicMatcher<HasAdjSubstatementsMatcher,
+                       void(TypeList<CompoundStmt, StmtExpr>),
+                       std::vector<Matcher<Stmt>>>;
+
+HasAdjSubstatementsMatcherType
+hasAdjSubstatementsFunc(ArrayRef<const Matcher<Stmt> *> MatcherRefs);
+
 using HasOverloadOpNameMatcher =
     PolymorphicMatcher<HasOverloadedOperatorNameMatcher,
                        void(TypeList<CXXOperatorCallExpr, FunctionDecl>),
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp 
b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 0874b3d0c45f5..14ff5d919eae2 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -31,6 +31,7 @@
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/WithColor.h"
 #include "llvm/Support/raw_ostream.h"
+#include <algorithm>
 #include <cassert>
 #include <cstddef>
 #include <optional>
@@ -467,6 +468,58 @@ hasAnyOverloadedOperatorNameFunc(ArrayRef<const StringRef 
*> NameRefs) {
   return HasOverloadOpNameMatcher(vectorFromRefs(NameRefs));
 }
 
+static std::vector<Matcher<Stmt>>
+vectorFromMatcherRefs(ArrayRef<const Matcher<Stmt> *> MatcherRefs) {
+  std::vector<Matcher<Stmt>> Matchers;
+  Matchers.reserve(MatcherRefs.size());
+  for (auto *Matcher : MatcherRefs)
+    Matchers.push_back(*Matcher);
+  return Matchers;
+}
+
+HasAdjSubstatementsMatcherType
+hasAdjSubstatementsFunc(ArrayRef<const Matcher<Stmt> *> MatcherRefs) {
+  return HasAdjSubstatementsMatcherType(vectorFromMatcherRefs(MatcherRefs));
+}
+
+template <typename T, typename ArgT>
+bool HasAdjSubstatementsMatcher<T, ArgT>::matches(
+    const T &Node, ASTMatchFinder *Finder,
+    BoundNodesTreeBuilder *Builder) const {
+  const CompoundStmt *CS = CompoundStmtMatcher<T>::get(Node);
+  if (!CS)
+    return false;
+
+  // Use llvm::search with lambda predicate that matches statements against
+  // matchers and accumulates BoundNodesTreeBuilder state
+  BoundNodesTreeBuilder CurrentBuilder;
+  const auto Found = llvm::search(
+      CS->body(), Matchers,
+      [&](const Stmt *StmtPtr, const Matcher<Stmt> &Matcher) mutable {
+        BoundNodesTreeBuilder StepBuilder;
+        StepBuilder.addMatch(CurrentBuilder);
+        if (!Matcher.matches(*StmtPtr, Finder, &StepBuilder)) {
+          // reset the state
+          CurrentBuilder = {};
+          return false;
+        }
+        // Invalidate the state
+        CurrentBuilder = StepBuilder;
+        return true;
+      });
+
+  if (Found == CS->body_end())
+    return false;
+
+  Builder->addMatch(CurrentBuilder);
+  return true;
+}
+
+template bool HasAdjSubstatementsMatcher<CompoundStmt>::matches(
+    const CompoundStmt &, ASTMatchFinder *, BoundNodesTreeBuilder *) const;
+template bool HasAdjSubstatementsMatcher<StmtExpr>::matches(
+    const StmtExpr &, ASTMatchFinder *, BoundNodesTreeBuilder *) const;
+
 HasNameMatcher::HasNameMatcher(std::vector<std::string> N)
     : UseUnqualifiedMatch(
           llvm::all_of(N, [](StringRef Name) { return !Name.contains("::"); 
})),
@@ -1046,6 +1099,10 @@ const 
internal::VariadicFunction<internal::Matcher<NamedDecl>, StringRef,
 const internal::VariadicFunction<internal::HasOpNameMatcher, StringRef,
                                  internal::hasAnyOperatorNameFunc>
     hasAnyOperatorName = {};
+const internal::VariadicFunction<internal::HasAdjSubstatementsMatcherType,
+                                 internal::Matcher<Stmt>,
+                                 internal::hasAdjSubstatementsFunc>
+    hasAdjSubstatements = {};
 const internal::VariadicFunction<internal::HasOverloadOpNameMatcher, StringRef,
                                  internal::hasAnyOverloadedOperatorNameFunc>
     hasAnyOverloadedOperatorName = {};
diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp 
b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
index 66848f7c42127..7b21a60c5f189 100644
--- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp
+++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp
@@ -288,6 +288,7 @@ RegistryMaps::RegistryMaps() {
   REGISTER_MATCHER(hasAnyPlacementArg);
   REGISTER_MATCHER(hasAnySelector);
   REGISTER_MATCHER(hasAnySubstatement);
+  REGISTER_MATCHER(hasAdjSubstatements);
   REGISTER_MATCHER(hasAnyTemplateArgument);
   REGISTER_MATCHER(hasAnyTemplateArgumentLoc);
   REGISTER_MATCHER(hasAnyUsingShadowDecl);
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp 
b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index c0a03deb5b543..1c86b16037d0f 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -2399,6 +2399,206 @@ TEST(HasAnySubstatement, 
FindsSubstatementBetweenOthers) {
                       compoundStmt(hasAnySubstatement(forStmt()))));
 }
 
+TEST(HasAdjSubstatements, MatchesAdjacentSubstatements) {
+  // Basic case: compound statement followed by binary operator
+  EXPECT_TRUE(matches("void f() { {} 1+2; }",
+                      compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                        binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, DoesNotMatchNonAdjacentSubstatements) {
+  // Statements exist but not adjacent
+  EXPECT_TRUE(notMatches("void f() { {} 1; 1+2; }",
+                         compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                          binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, MatchesInNestedCompoundStatements) {
+  // Should match in nested compound statements
+  EXPECT_TRUE(matches("void f() { if (true) { {} 1+2; } }",
+                      compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                      binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, MatchesFirstAdjacentPair) {
+  // When multiple adjacent pairs exist, should match the first one
+  EXPECT_TRUE(matches("void f() { {} 1+2; {} 3+4; }",
+                      compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                      binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, DoesNotMatchEmptyCompound) {
+  // Empty compound statement has no adjacent pairs
+  EXPECT_TRUE(notMatches("void f() { }",
+                         compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                         binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, DoesNotMatchSingleStatement) {
+  // Single statement has no adjacent pairs
+  EXPECT_TRUE(notMatches("void f() { 1+2; }",
+                         compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                        binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, MatchesDifferentStatementTypes) {
+  // Test with different statement types
+  EXPECT_TRUE(matches("void f() { for (;;); while (true); }",
+                      compoundStmt(hasAdjSubstatements(forStmt(), 
whileStmt()))));
+  
+  EXPECT_TRUE(matches("void f() { int x; return; }",
+                      compoundStmt(hasAdjSubstatements(declStmt(), 
returnStmt()))));
+}
+
+TEST(HasAdjSubstatements, WorksWithStmtExpr) {
+  // Test that it works with StmtExpr (polymorphic support)
+  EXPECT_TRUE(matches("void f() { int x = ({ {} 1+2; }); }",
+                      stmtExpr(hasAdjSubstatements(compoundStmt(),
+                                                   binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, DoesNotMatchWrongOrder) {
+  // The order matters - binaryOperator must come after compoundStmt
+  EXPECT_TRUE(notMatches("void f() { 1+2; {} }",
+                         compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                         binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, MatchesWithStatementsBetween) {
+  // Should still match even if there are other statements before/after
+  EXPECT_TRUE(matches("void f() { int x; {} 1+2; int y; }",
+                      compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                     binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicMatchesThreeAdjacentSubstatements) {
+  // Test variadic version with 3 matchers
+  EXPECT_TRUE(matches("void f() { {} 1+2; 3+4; }",
+                      compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                       binaryOperator(),
+                                                       binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicMatchesFourAdjacentSubstatements) {
+  // Test variadic version with 4 matchers
+  EXPECT_TRUE(matches("void f() { int x; return; {} 1+2; }",
+                      compoundStmt(hasAdjSubstatements(declStmt(),
+                                                       returnStmt(),
+                                                       compoundStmt(),
+                                                       binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicMatchesFiveAdjacentSubstatements) {
+  // Test variadic version with 5 matchers
+  EXPECT_TRUE(matches("void f() { for (;;); while (true); if (true) {} return; 
1+2; }",
+                      compoundStmt(hasAdjSubstatements(forStmt(),
+                                                       whileStmt(),
+                                                       ifStmt(),
+                                                       returnStmt(),
+                                                       binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicDoesNotMatchNonAdjacentSequence) {
+  // Three matchers but statements are not all adjacent
+  EXPECT_TRUE(notMatches("void f() { {} 1; 1+2; 3+4; }",
+                         compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                         binaryOperator(),
+                                                         binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicDoesNotMatchPartialSequence) {
+  // First two match but third doesn't
+  EXPECT_TRUE(notMatches("void f() { {} 1+2; return; }",
+                         compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                         binaryOperator(),
+                                                         binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicMatchesInNestedCompound) {
+  // Test variadic version in nested compound statements
+  EXPECT_TRUE(matches("void f() { if (true) { {} 1+2; 3+4; } }",
+                      compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                      binaryOperator(),
+                                                      binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicMatchesWithDifferentTypes) {
+  // Test variadic version with different statement types
+  EXPECT_TRUE(matches("void f() { for (;;); while (true); if (true) {} }",
+                      compoundStmt(hasAdjSubstatements(forStmt(),
+                                                       whileStmt(),
+                                                       ifStmt()))));
+}
+
+TEST(HasAdjSubstatements, VariadicDoesNotMatchWrongOrder) {
+  // Order matters in variadic version
+  EXPECT_TRUE(notMatches("void f() { 1+2; {} 3+4; }",
+                         compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                          binaryOperator(),
+                                                          binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicMatchesFirstSequence) {
+  // When multiple sequences exist, should match the first one
+  EXPECT_TRUE(matches("void f() { {} 1+2; 3+4; {} 5+6; 7+8; }",
+                      compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                      binaryOperator(),
+                                                      binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicWorksWithStmtExpr) {
+  // Test variadic version with StmtExpr
+  EXPECT_TRUE(matches("void f() { int x = ({ {} 1+2; 3+4; }); }",
+                      stmtExpr(hasAdjSubstatements(compoundStmt(),
+                                                   binaryOperator(),
+                                                   binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicRequiresMinimumStatements) {
+  // Need at least as many statements as matchers
+  EXPECT_TRUE(notMatches("void f() { {} 1+2; }",
+                         compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                          binaryOperator(),
+                                                          binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicMatchesWithStatementsBetween) {
+  // Should still match even if there are other statements before/after
+  EXPECT_TRUE(matches("void f() { int x; {} 1+2; 3+4; int y; }",
+                      compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                       binaryOperator(),
+                                                       binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicMatchesComplexSequence) {
+  // Test with a complex sequence of different statement types
+  EXPECT_TRUE(matches("void f() { int a; int b; return; {} 1+2; }",
+                      compoundStmt(hasAdjSubstatements(declStmt(),
+                                                       declStmt(),
+                                                       returnStmt(),
+                                                       compoundStmt(),
+                                                       binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicDoesNotMatchGapInSequence) {
+  // Sequence has a gap in the middle
+  EXPECT_TRUE(notMatches("void f() { {} 1+2; int x; 3+4; }",
+                         compoundStmt(hasAdjSubstatements(compoundStmt(),
+                                                         binaryOperator(),
+                                                         binaryOperator()))));
+}
+
+TEST(HasAdjSubstatements, VariadicMatchesLongSequence) {
+  // Test with a longer sequence (6 statements)
+  EXPECT_TRUE(matches("void f() { int a; int b; int c; return; {} 1+2; }",
+                      compoundStmt(hasAdjSubstatements(declStmt(),
+                                                       declStmt(),
+                                                       declStmt(),
+                                                       returnStmt(),
+                                                       compoundStmt(),
+                                                       binaryOperator()))));
+}
+
 TEST(Member, MatchesMemberAllocationFunction) {
   // Fails in C++11 mode
   EXPECT_TRUE(matchesConditionally(
diff --git a/llvm/include/llvm/ADT/STLExtras.h 
b/llvm/include/llvm/ADT/STLExtras.h
index af0e4a36be1b1..9fc8095389860 100644
--- a/llvm/include/llvm/ADT/STLExtras.h
+++ b/llvm/include/llvm/ADT/STLExtras.h
@@ -1778,6 +1778,11 @@ OutputIt copy_if(R &&Range, OutputIt Out, UnaryPredicate 
P) {
   return std::copy_if(adl_begin(Range), adl_end(Range), Out, P);
 }
 
+template <typename R1, typename R2, typename BinaryPredicate>
+auto search(R1 &&Range1, R2 &&Range2, BinaryPredicate P) {
+  return std::search(adl_begin(Range1), adl_end(Range1), adl_begin(Range2), 
adl_end(Range2), P);
+}
+
 /// Return the single value in \p Range that satisfies
 /// \p P(<member of \p Range> *, AllowRepeats)->T * returning nullptr
 /// when no values or multiple values were found.

``````````

</details>


https://github.com/llvm/llvm-project/pull/169965
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to