[clang] [llvm] [clang] Add hasAdjSubstatements matcher (PR #169965)
Denis Mikhailov via llvm-commits
llvm-commits at lists.llvm.org
Fri Nov 28 15:56:14 PST 2025
https://github.com/denzor200 updated https://github.com/llvm/llvm-project/pull/169965
>From 0476cfd46a63af25a7f12e7ece5882246208ced9 Mon Sep 17 00:00:00 2001
From: denzor200 <denismikhaylov38 at gmail.com>
Date: Fri, 28 Nov 2025 23:51:14 +0300
Subject: [PATCH 1/6] Implement simple matcher
---
clang/include/clang/ASTMatchers/ASTMatchers.h | 46 +++++++++++-
clang/lib/ASTMatchers/Dynamic/Registry.cpp | 1 +
.../ASTMatchers/ASTMatchersTraversalTest.cpp | 72 +++++++++++++++++++
3 files changed, 118 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index bca2d8425b3f5..080fc47c59d94 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -5889,7 +5889,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 +5910,51 @@ AST_POLYMORPHIC_MATCHER_P(hasAnySubstatement,
Builder) != CS->body_end();
}
+/// Matches compound statements that contain a PrevStmt immediately followed by
+/// NextStmt. 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'
+AST_POLYMORPHIC_MATCHER_P2(hasAdjSubstatements,
+ AST_POLYMORPHIC_SUPPORTED_TYPES(CompoundStmt,
+ StmtExpr),
+ ast_matchers::internal::Matcher<Stmt>, PrevMatcher,
+ ast_matchers::internal::Matcher<Stmt>, NextMatcher) {
+ const CompoundStmt *CS = CompoundStmtMatcher<NodeType>::get(Node);
+ if (!CS)
+ return false;
+
+ auto Begin = CS->body_begin();
+ auto End = CS->body_end();
+
+ if (CS->size() < 2)
+ return false;
+
+ return std::adjacent_find(
+ Begin, End,
+ [&](const Stmt *PrevStmt, const Stmt *NextStmt) {
+ clang::ast_matchers::internal::BoundNodesTreeBuilder PrevBuilder;
+ if (!PrevMatcher.matches(*PrevStmt, Finder, &PrevBuilder))
+ return false;
+
+ clang::ast_matchers::internal::BoundNodesTreeBuilder NextBuilder;
+ NextBuilder.addMatch(PrevBuilder);
+ if (!NextMatcher.matches(*NextStmt, Finder, &NextBuilder))
+ return false;
+
+ Builder->addMatch(NextBuilder);
+ return true;
+ }) != End;
+}
+
/// Checks that a compound statement contains a specific number of
/// child statements.
///
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..d1d468dcf3116 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -2399,6 +2399,78 @@ 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(Member, MatchesMemberAllocationFunction) {
// Fails in C++11 mode
EXPECT_TRUE(matchesConditionally(
>From 56bf3da670afb8d2ae99bbc0d0d9d82f2cc58975 Mon Sep 17 00:00:00 2001
From: denzor200 <denismikhaylov38 at gmail.com>
Date: Sat, 29 Nov 2025 00:28:56 +0300
Subject: [PATCH 2/6] make matcher variadic
---
clang/include/clang/ASTMatchers/ASTMatchers.h | 54 +++-----
.../clang/ASTMatchers/ASTMatchersInternal.h | 27 ++++
clang/lib/ASTMatchers/ASTMatchersInternal.cpp | 65 +++++++++
.../ASTMatchers/ASTMatchersTraversalTest.cpp | 128 ++++++++++++++++++
4 files changed, 241 insertions(+), 33 deletions(-)
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 080fc47c59d94..03b369dccc11e 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>
@@ -5910,8 +5911,9 @@ AST_POLYMORPHIC_MATCHER_P(hasAnySubstatement,
Builder) != CS->body_end();
}
-/// Matches compound statements that contain a PrevStmt immediately followed by
-/// NextStmt. Also matches StmtExprs that have CompoundStmt as children.
+/// Matches compound statements that contain adjacent substatements matching
+/// the provided sequence of matchers. Also matches StmtExprs that have
+/// CompoundStmt as children.
///
/// Given
/// \code
@@ -5923,37 +5925,23 @@ AST_POLYMORPHIC_MATCHER_P(hasAnySubstatement,
/// matching '{}'
/// with binaryOperator()
/// matching '1+2'
-AST_POLYMORPHIC_MATCHER_P2(hasAdjSubstatements,
- AST_POLYMORPHIC_SUPPORTED_TYPES(CompoundStmt,
- StmtExpr),
- ast_matchers::internal::Matcher<Stmt>, PrevMatcher,
- ast_matchers::internal::Matcher<Stmt>, NextMatcher) {
- const CompoundStmt *CS = CompoundStmtMatcher<NodeType>::get(Node);
- if (!CS)
- return false;
-
- auto Begin = CS->body_begin();
- auto End = CS->body_end();
-
- if (CS->size() < 2)
- return false;
-
- return std::adjacent_find(
- Begin, End,
- [&](const Stmt *PrevStmt, const Stmt *NextStmt) {
- clang::ast_matchers::internal::BoundNodesTreeBuilder PrevBuilder;
- if (!PrevMatcher.matches(*PrevStmt, Finder, &PrevBuilder))
- return false;
-
- clang::ast_matchers::internal::BoundNodesTreeBuilder NextBuilder;
- NextBuilder.addMatch(PrevBuilder);
- if (!NextMatcher.matches(*NextStmt, Finder, &NextBuilder))
- return false;
-
- Builder->addMatch(NextBuilder);
- return true;
- }) != End;
-}
+///
+/// Given
+/// \code
+/// { {}; 1+2; 3+4; }
+/// \endcode
+/// hasAdjSubstatements(compoundStmt(), binaryOperator(), binaryOperator())
+/// matches '{ {}; 1+2; 3+4; }'
+/// with the matchers matching the three consecutive statements in order.
+///
+/// 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..4a8cfe381175c 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -2283,6 +2283,33 @@ using HasOpNameMatcher =
HasOpNameMatcher hasAnyOperatorNameFunc(ArrayRef<const StringRef *> NameRefs);
+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..8f5ec1c27181d 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -467,6 +467,67 @@ 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;
+
+ if (Matchers.empty())
+ return false;
+
+ auto Begin = CS->body_begin();
+ auto End = CS->body_end();
+ size_t NumMatchers = Matchers.size();
+
+ if (CS->size() < NumMatchers)
+ return false;
+
+ // Try to find a sequence of consecutive statements matching all matchers
+ for (auto It = Begin; It + NumMatchers <= End; ++It) {
+ BoundNodesTreeBuilder CurrentBuilder;
+ bool AllMatch = true;
+
+ for (size_t i = 0; i < NumMatchers; ++i) {
+ BoundNodesTreeBuilder StepBuilder;
+ StepBuilder.addMatch(CurrentBuilder);
+ if (!Matchers[i].matches(**(It + i), Finder, &StepBuilder)) {
+ AllMatch = false;
+ break;
+ }
+ CurrentBuilder = StepBuilder;
+ }
+
+ if (AllMatch) {
+ Builder->addMatch(CurrentBuilder);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+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 +1107,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/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index d1d468dcf3116..1c86b16037d0f 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -2471,6 +2471,134 @@ TEST(HasAdjSubstatements, MatchesWithStatementsBetween) {
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(
>From 4493ad80b429e0d9e2e43286a84cf809274d29c5 Mon Sep 17 00:00:00 2001
From: denzor200 <denismikhaylov38 at gmail.com>
Date: Sat, 29 Nov 2025 02:07:11 +0300
Subject: [PATCH 3/6] refactor - step 1
---
clang/include/clang/ASTMatchers/ASTMatchers.h | 8 -----
.../clang/ASTMatchers/ASTMatchersInternal.h | 4 +++
clang/lib/ASTMatchers/ASTMatchersInternal.cpp | 30 ++++++++-----------
3 files changed, 17 insertions(+), 25 deletions(-)
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 03b369dccc11e..87d6bd7e1b9a3 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -5926,14 +5926,6 @@ AST_POLYMORPHIC_MATCHER_P(hasAnySubstatement,
/// with binaryOperator()
/// matching '1+2'
///
-/// Given
-/// \code
-/// { {}; 1+2; 3+4; }
-/// \endcode
-/// hasAdjSubstatements(compoundStmt(), binaryOperator(), binaryOperator())
-/// matches '{ {}; 1+2; 3+4; }'
-/// with the matchers matching the three consecutive statements in order.
-///
/// hasAdjSubstatements(compoundStmt(), binaryOperator(), returnStmt())
/// Is equivalent to matching a compound statement that contains
/// a compound statement immediately followed by a binary operator
diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
index 4a8cfe381175c..5fb63af28dc1b 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -2283,6 +2283,10 @@ 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 ||
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 8f5ec1c27181d..48f3ed8e6318d 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -439,9 +439,14 @@ optionallyVariadicOperator(const DynTypedNode &DynNode, ASTMatchFinder *Finder,
return true;
}
-inline static
-std::vector<std::string> vectorFromRefs(ArrayRef<const StringRef *> NameRefs) {
- std::vector<std::string> Names;
+template<typename T, typename T1>
+using replace_void_t = typename std::conditional<
+ std::is_void_v<T>, T1, T
+>::type;
+
+template<typename To=void, typename From>
+static auto convertRefsToVector(ArrayRef<const From *> NameRefs) {
+ std::vector<replace_void_t<To, From>> Names;
Names.reserve(NameRefs.size());
for (auto *Name : NameRefs)
Names.emplace_back(*Name);
@@ -450,35 +455,26 @@ std::vector<std::string> vectorFromRefs(ArrayRef<const StringRef *> NameRefs) {
Matcher<NamedDecl> hasAnyNameFunc(ArrayRef<const StringRef *> NameRefs) {
return internal::Matcher<NamedDecl>(
- new internal::HasNameMatcher(vectorFromRefs(NameRefs)));
+ new internal::HasNameMatcher(convertRefsToVector<std::string>(NameRefs)));
}
Matcher<ObjCMessageExpr> hasAnySelectorFunc(
ArrayRef<const StringRef *> NameRefs) {
- return hasAnySelectorMatcher(vectorFromRefs(NameRefs));
+ return hasAnySelectorMatcher(convertRefsToVector<std::string>(NameRefs));
}
HasOpNameMatcher hasAnyOperatorNameFunc(ArrayRef<const StringRef *> NameRefs) {
- return HasOpNameMatcher(vectorFromRefs(NameRefs));
+ return HasOpNameMatcher(convertRefsToVector<std::string>(NameRefs));
}
HasOverloadOpNameMatcher
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;
+ return HasOverloadOpNameMatcher(convertRefsToVector<std::string>(NameRefs));
}
HasAdjSubstatementsMatcherType
hasAdjSubstatementsFunc(ArrayRef<const Matcher<Stmt> *> MatcherRefs) {
- return HasAdjSubstatementsMatcherType(vectorFromMatcherRefs(MatcherRefs));
+ return HasAdjSubstatementsMatcherType(convertRefsToVector(MatcherRefs));
}
template <typename T, typename ArgT>
>From 73edaf80e49aee5d6614bea9380bc1188963658e Mon Sep 17 00:00:00 2001
From: denzor200 <denismikhaylov38 at gmail.com>
Date: Sat, 29 Nov 2025 02:35:46 +0300
Subject: [PATCH 4/6] refactor - step 2
---
clang/lib/ASTMatchers/ASTMatchersInternal.cpp | 78 +++++++++++--------
1 file changed, 46 insertions(+), 32 deletions(-)
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 48f3ed8e6318d..281b0e8a2a3d6 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>
@@ -439,14 +440,9 @@ optionallyVariadicOperator(const DynTypedNode &DynNode, ASTMatchFinder *Finder,
return true;
}
-template<typename T, typename T1>
-using replace_void_t = typename std::conditional<
- std::is_void_v<T>, T1, T
->::type;
-
-template<typename To=void, typename From>
-static auto convertRefsToVector(ArrayRef<const From *> NameRefs) {
- std::vector<replace_void_t<To, From>> Names;
+inline static
+std::vector<std::string> vectorFromRefs(ArrayRef<const StringRef *> NameRefs) {
+ std::vector<std::string> Names;
Names.reserve(NameRefs.size());
for (auto *Name : NameRefs)
Names.emplace_back(*Name);
@@ -455,26 +451,35 @@ static auto convertRefsToVector(ArrayRef<const From *> NameRefs) {
Matcher<NamedDecl> hasAnyNameFunc(ArrayRef<const StringRef *> NameRefs) {
return internal::Matcher<NamedDecl>(
- new internal::HasNameMatcher(convertRefsToVector<std::string>(NameRefs)));
+ new internal::HasNameMatcher(vectorFromRefs(NameRefs)));
}
Matcher<ObjCMessageExpr> hasAnySelectorFunc(
ArrayRef<const StringRef *> NameRefs) {
- return hasAnySelectorMatcher(convertRefsToVector<std::string>(NameRefs));
+ return hasAnySelectorMatcher(vectorFromRefs(NameRefs));
}
HasOpNameMatcher hasAnyOperatorNameFunc(ArrayRef<const StringRef *> NameRefs) {
- return HasOpNameMatcher(convertRefsToVector<std::string>(NameRefs));
+ return HasOpNameMatcher(vectorFromRefs(NameRefs));
}
HasOverloadOpNameMatcher
hasAnyOverloadedOperatorNameFunc(ArrayRef<const StringRef *> NameRefs) {
- return HasOverloadOpNameMatcher(convertRefsToVector<std::string>(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(convertRefsToVector(MatcherRefs));
+ return HasAdjSubstatementsMatcherType(vectorFromMatcherRefs(MatcherRefs));
}
template <typename T, typename ArgT>
@@ -495,28 +500,37 @@ bool HasAdjSubstatementsMatcher<T, ArgT>::matches(
if (CS->size() < NumMatchers)
return false;
- // Try to find a sequence of consecutive statements matching all matchers
- for (auto It = Begin; It + NumMatchers <= End; ++It) {
- BoundNodesTreeBuilder CurrentBuilder;
- bool AllMatch = true;
-
- for (size_t i = 0; i < NumMatchers; ++i) {
- BoundNodesTreeBuilder StepBuilder;
- StepBuilder.addMatch(CurrentBuilder);
- if (!Matchers[i].matches(**(It + i), Finder, &StepBuilder)) {
- AllMatch = false;
- break;
- }
- CurrentBuilder = StepBuilder;
- }
+ // Use std::search with lambda predicate that matches statements against
+ // matchers and accumulates BoundNodesTreeBuilder state
+ BoundNodesTreeBuilder CurrentBuilder;
+ size_t ChainLength = 0;
- if (AllMatch) {
- Builder->addMatch(CurrentBuilder);
- return true;
+ auto Pred = [&](
+ Stmt *const &StmtPtr, const Matcher<Stmt> &Matcher) mutable {
+ if (ChainLength >= Matchers.size())
+ return false;
+
+ BoundNodesTreeBuilder StepBuilder;
+ StepBuilder.addMatch(CurrentBuilder);
+ if (!Matcher.matches(*StmtPtr, Finder, &StepBuilder)) {
+ // Reset on mismatch
+ CurrentBuilder = BoundNodesTreeBuilder();
+ ChainLength = 0;
+ return false;
}
- }
+ // Advance chain
+ CurrentBuilder = StepBuilder;
+ ++ChainLength;
+ return true;
+ };
- return false;
+ auto Found = std::search(Begin, End, Matchers.begin(), Matchers.end(), Pred);
+
+ if (Found == End || ChainLength != NumMatchers)
+ return false;
+
+ Builder->addMatch(CurrentBuilder);
+ return true;
}
template bool HasAdjSubstatementsMatcher<CompoundStmt>::matches(
>From f6235e02f9437edd42aa5cd9f7a919f1a9529808 Mon Sep 17 00:00:00 2001
From: denzor200 <denismikhaylov38 at gmail.com>
Date: Sat, 29 Nov 2025 02:36:02 +0300
Subject: [PATCH 5/6] refactor - step 3
---
clang/lib/ASTMatchers/ASTMatchersInternal.cpp | 50 ++++++-------------
llvm/include/llvm/ADT/STLExtras.h | 5 ++
2 files changed, 21 insertions(+), 34 deletions(-)
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 281b0e8a2a3d6..14ff5d919eae2 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -490,43 +490,25 @@ bool HasAdjSubstatementsMatcher<T, ArgT>::matches(
if (!CS)
return false;
- if (Matchers.empty())
- return false;
-
- auto Begin = CS->body_begin();
- auto End = CS->body_end();
- size_t NumMatchers = Matchers.size();
-
- if (CS->size() < NumMatchers)
- return false;
-
- // Use std::search with lambda predicate that matches statements against
+ // Use llvm::search with lambda predicate that matches statements against
// matchers and accumulates BoundNodesTreeBuilder state
BoundNodesTreeBuilder CurrentBuilder;
- size_t ChainLength = 0;
-
- auto Pred = [&](
- Stmt *const &StmtPtr, const Matcher<Stmt> &Matcher) mutable {
- if (ChainLength >= Matchers.size())
- return false;
-
- BoundNodesTreeBuilder StepBuilder;
- StepBuilder.addMatch(CurrentBuilder);
- if (!Matcher.matches(*StmtPtr, Finder, &StepBuilder)) {
- // Reset on mismatch
- CurrentBuilder = BoundNodesTreeBuilder();
- ChainLength = 0;
- return false;
- }
- // Advance chain
- CurrentBuilder = StepBuilder;
- ++ChainLength;
- return true;
- };
-
- auto Found = std::search(Begin, End, Matchers.begin(), Matchers.end(), Pred);
+ 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 == End || ChainLength != NumMatchers)
+ if (Found == CS->body_end())
return false;
Builder->addMatch(CurrentBuilder);
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.
>From ef4fd5653e59f884c7ae32b5a6ba5e253ab2a914 Mon Sep 17 00:00:00 2001
From: denzor200 <denismikhaylov38 at gmail.com>
Date: Sat, 29 Nov 2025 02:55:53 +0300
Subject: [PATCH 6/6] format
---
clang/include/clang/ASTMatchers/ASTMatchers.h | 4 +-
.../ASTMatchers/ASTMatchersTraversalTest.cpp | 191 +++++++++---------
llvm/include/llvm/ADT/STLExtras.h | 3 +-
3 files changed, 96 insertions(+), 102 deletions(-)
diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h
index 87d6bd7e1b9a3..3e874b37c05c1 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchers.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchers.h
@@ -5931,8 +5931,8 @@ AST_POLYMORPHIC_MATCHER_P(hasAnySubstatement,
/// 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>
+ internal::HasAdjSubstatementsMatcherType, internal::Matcher<Stmt>,
+ internal::hasAdjSubstatementsFunc>
hasAdjSubstatements;
/// Checks that a compound statement contains a specific number of
diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index 1c86b16037d0f..f113f2a114757 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -2401,202 +2401,195 @@ TEST(HasAnySubstatement, FindsSubstatementBetweenOthers) {
TEST(HasAdjSubstatements, MatchesAdjacentSubstatements) {
// Basic case: compound statement followed by binary operator
- EXPECT_TRUE(matches("void f() { {} 1+2; }",
- compoundStmt(hasAdjSubstatements(compoundStmt(),
- binaryOperator()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ 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()))));
+ compoundStmt(hasAdjSubstatements(
+ declStmt(), declStmt(), declStmt(), returnStmt(),
+ compoundStmt(), binaryOperator()))));
}
TEST(Member, MatchesMemberAllocationFunction) {
diff --git a/llvm/include/llvm/ADT/STLExtras.h b/llvm/include/llvm/ADT/STLExtras.h
index 9fc8095389860..f75830ecc53a8 100644
--- a/llvm/include/llvm/ADT/STLExtras.h
+++ b/llvm/include/llvm/ADT/STLExtras.h
@@ -1780,7 +1780,8 @@ OutputIt copy_if(R &&Range, OutputIt Out, UnaryPredicate 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 std::search(adl_begin(Range1), adl_end(Range1), adl_begin(Range2),
+ adl_end(Range2), P);
}
/// Return the single value in \p Range that satisfies
More information about the llvm-commits
mailing list