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
