https://github.com/denzor200 updated https://github.com/llvm/llvm-project/pull/169965
>From 0476cfd46a63af25a7f12e7ece5882246208ced9 Mon Sep 17 00:00:00 2001 From: denzor200 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
