Skip to content

Commit cbaadb1

Browse files
authored
[clang][ASTMatcher] Add matchers for CXXFoldExpr (#71245)
Adds support for the following matchers related to `CXXFoldExpr`: `cxxFoldExpr`, `callee`, `hasInit`, `hasPattern`, `isRightFold`, `isLeftFold`, `isUnaryFold`, `isBinaryFold`, `hasOperator`, `hasLHS`, `hasRHS`.
1 parent 27acfdd commit cbaadb1

10 files changed

+730
-76
lines changed

clang/docs/LibASTMatchersReference.html

Lines changed: 271 additions & 23 deletions
Large diffs are not rendered by default.

clang/docs/ReleaseNotes.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,9 @@ AST Matchers
11231123
- Add ``convertVectorExpr``.
11241124
- Add ``dependentSizedExtVectorType``.
11251125
- Add ``macroQualifiedType``.
1126+
- Add ``CXXFoldExpr`` related matchers: ``cxxFoldExpr``, ``callee``,
1127+
``hasInit``, ``hasPattern``, ``isRightFold``, ``isLeftFold``,
1128+
``isUnaryFold``, ``isBinaryFold``, ``hasOperator``, ``hasLHS``, ``hasRHS``, ``hasEitherOperand``.
11261129

11271130
clang-format
11281131
------------

clang/include/clang/ASTMatchers/ASTMatchers.h

Lines changed: 180 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,6 +2062,18 @@ extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXDefaultArgExpr>
20622062
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXOperatorCallExpr>
20632063
cxxOperatorCallExpr;
20642064

2065+
/// Matches C++17 fold expressions.
2066+
///
2067+
/// Example matches `(0 + ... + args)`:
2068+
/// \code
2069+
/// template <typename... Args>
2070+
/// auto sum(Args... args) {
2071+
/// return (0 + ... + args);
2072+
/// }
2073+
/// \endcode
2074+
extern const internal::VariadicDynCastAllOfMatcher<Stmt, CXXFoldExpr>
2075+
cxxFoldExpr;
2076+
20652077
/// Matches rewritten binary operators
20662078
///
20672079
/// Example matches use of "<":
@@ -3881,7 +3893,7 @@ AST_MATCHER_P(ObjCMessageExpr, numSelectorArgs, unsigned, N) {
38813893
return Node.getSelector().getNumArgs() == N;
38823894
}
38833895

3884-
/// Matches if the call expression's callee expression matches.
3896+
/// Matches if the call or fold expression's callee expression matches.
38853897
///
38863898
/// Given
38873899
/// \code
@@ -3893,13 +3905,32 @@ AST_MATCHER_P(ObjCMessageExpr, numSelectorArgs, unsigned, N) {
38933905
/// with callee(...)
38943906
/// matching this->x, x, y.x, f respectively
38953907
///
3908+
/// Given
3909+
/// \code
3910+
/// template <typename... Args>
3911+
/// auto sum(Args... args) {
3912+
/// return (0 + ... + args);
3913+
/// }
3914+
///
3915+
/// template <typename... Args>
3916+
/// auto multiply(Args... args) {
3917+
/// return (args * ... * 1);
3918+
/// }
3919+
/// \endcode
3920+
/// cxxFoldExpr(callee(expr()))
3921+
/// matches (args * ... * 1)
3922+
/// with callee(...)
3923+
/// matching *
3924+
///
38963925
/// Note: Callee cannot take the more general internal::Matcher<Expr>
38973926
/// because this introduces ambiguous overloads with calls to Callee taking a
38983927
/// internal::Matcher<Decl>, as the matcher hierarchy is purely
38993928
/// implemented in terms of implicit casts.
3900-
AST_MATCHER_P(CallExpr, callee, internal::Matcher<Stmt>,
3901-
InnerMatcher) {
3902-
const Expr *ExprNode = Node.getCallee();
3929+
AST_POLYMORPHIC_MATCHER_P_OVERLOAD(callee,
3930+
AST_POLYMORPHIC_SUPPORTED_TYPES(CallExpr,
3931+
CXXFoldExpr),
3932+
internal::Matcher<Stmt>, InnerMatcher, 0) {
3933+
const auto *ExprNode = Node.getCallee();
39033934
return (ExprNode != nullptr &&
39043935
InnerMatcher.matches(*ExprNode, Finder, Builder));
39053936
}
@@ -4532,6 +4563,121 @@ AST_POLYMORPHIC_MATCHER_P2(hasArgument,
45324563
return InnerMatcher.matches(*Arg->IgnoreParenImpCasts(), Finder, Builder);
45334564
}
45344565

4566+
/// Matches the operand that does not contain the parameter pack.
4567+
///
4568+
/// Example matches `(0 + ... + args)` and `(args * ... * 1)`
4569+
/// (matcher = cxxFoldExpr(hasFoldInit(expr())))
4570+
/// with hasFoldInit(...)
4571+
/// matching `0` and `1` respectively
4572+
/// \code
4573+
/// template <typename... Args>
4574+
/// auto sum(Args... args) {
4575+
/// return (0 + ... + args);
4576+
/// }
4577+
///
4578+
/// template <typename... Args>
4579+
/// auto multiply(Args... args) {
4580+
/// return (args * ... * 1);
4581+
/// }
4582+
/// \endcode
4583+
AST_MATCHER_P(CXXFoldExpr, hasFoldInit, ast_matchers::internal::Matcher<Expr>,
4584+
InnerMacher) {
4585+
const auto *const Init = Node.getInit();
4586+
return Init && InnerMacher.matches(*Init, Finder, Builder);
4587+
}
4588+
4589+
/// Matches the operand that contains the parameter pack.
4590+
///
4591+
/// Example matches `(0 + ... + args)`
4592+
/// (matcher = cxxFoldExpr(hasPattern(expr())))
4593+
/// with hasPattern(...)
4594+
/// matching `args`
4595+
/// \code
4596+
/// template <typename... Args>
4597+
/// auto sum(Args... args) {
4598+
/// return (0 + ... + args);
4599+
/// }
4600+
///
4601+
/// template <typename... Args>
4602+
/// auto multiply(Args... args) {
4603+
/// return (args * ... * 1);
4604+
/// }
4605+
/// \endcode
4606+
AST_MATCHER_P(CXXFoldExpr, hasPattern, ast_matchers::internal::Matcher<Expr>,
4607+
InnerMacher) {
4608+
const Expr *const Pattern = Node.getPattern();
4609+
return Pattern && InnerMacher.matches(*Pattern, Finder, Builder);
4610+
}
4611+
4612+
/// Matches right-folding fold expressions.
4613+
///
4614+
/// Example matches `(args * ... * 1)`
4615+
/// (matcher = cxxFoldExpr(isRightFold()))
4616+
/// \code
4617+
/// template <typename... Args>
4618+
/// auto sum(Args... args) {
4619+
/// return (0 + ... + args);
4620+
/// }
4621+
///
4622+
/// template <typename... Args>
4623+
/// auto multiply(Args... args) {
4624+
/// return (args * ... * 1);
4625+
/// }
4626+
/// \endcode
4627+
AST_MATCHER(CXXFoldExpr, isRightFold) { return Node.isRightFold(); }
4628+
4629+
/// Matches left-folding fold expressions.
4630+
///
4631+
/// Example matches `(0 + ... + args)`
4632+
/// (matcher = cxxFoldExpr(isLeftFold()))
4633+
/// \code
4634+
/// template <typename... Args>
4635+
/// auto sum(Args... args) {
4636+
/// return (0 + ... + args);
4637+
/// }
4638+
///
4639+
/// template <typename... Args>
4640+
/// auto multiply(Args... args) {
4641+
/// return (args * ... * 1);
4642+
/// }
4643+
/// \endcode
4644+
AST_MATCHER(CXXFoldExpr, isLeftFold) { return Node.isLeftFold(); }
4645+
4646+
/// Matches unary fold expressions, i.e. fold expressions without an
4647+
/// initializer.
4648+
///
4649+
/// Example matches `(args * ...)`
4650+
/// (matcher = cxxFoldExpr(isUnaryFold()))
4651+
/// \code
4652+
/// template <typename... Args>
4653+
/// auto sum(Args... args) {
4654+
/// return (0 + ... + args);
4655+
/// }
4656+
///
4657+
/// template <typename... Args>
4658+
/// auto multiply(Args... args) {
4659+
/// return (args * ...);
4660+
/// }
4661+
/// \endcode
4662+
AST_MATCHER(CXXFoldExpr, isUnaryFold) { return Node.getInit() == nullptr; }
4663+
4664+
/// Matches binary fold expressions, i.e. fold expressions with an initializer.
4665+
///
4666+
/// Example matches `(0 + ... + args)`
4667+
/// (matcher = cxxFoldExpr(isBinaryFold()))
4668+
/// \code
4669+
/// template <typename... Args>
4670+
/// auto sum(Args... args) {
4671+
/// return (0 + ... + args);
4672+
/// }
4673+
///
4674+
/// template <typename... Args>
4675+
/// auto multiply(Args... args) {
4676+
/// return (args * ...);
4677+
/// }
4678+
/// \endcode
4679+
AST_MATCHER(CXXFoldExpr, isBinaryFold) { return Node.getInit() != nullptr; }
4680+
45354681
/// Matches the n'th item of an initializer list expression.
45364682
///
45374683
/// Example matches y.
@@ -5709,17 +5855,27 @@ AST_POLYMORPHIC_MATCHER_P_OVERLOAD(equals,
57095855
.matchesNode(Node);
57105856
}
57115857

5712-
/// Matches the operator Name of operator expressions (binary or
5713-
/// unary).
5858+
/// Matches the operator Name of operator expressions and fold expressions
5859+
/// (binary or unary).
57145860
///
57155861
/// Example matches a || b (matcher = binaryOperator(hasOperatorName("||")))
57165862
/// \code
57175863
/// !(a || b)
57185864
/// \endcode
5865+
///
5866+
/// Example matches `(0 + ... + args)`
5867+
/// (matcher = cxxFoldExpr(hasOperatorName("+")))
5868+
/// \code
5869+
/// template <typename... Args>
5870+
/// auto sum(Args... args) {
5871+
/// return (0 + ... + args);
5872+
/// }
5873+
/// \endcode
57195874
AST_POLYMORPHIC_MATCHER_P(
57205875
hasOperatorName,
57215876
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
5722-
CXXRewrittenBinaryOperator, UnaryOperator),
5877+
CXXRewrittenBinaryOperator, CXXFoldExpr,
5878+
UnaryOperator),
57235879
std::string, Name) {
57245880
if (std::optional<StringRef> OpName = internal::getOpName(Node))
57255881
return *OpName == Name;
@@ -5789,11 +5945,12 @@ AST_POLYMORPHIC_MATCHER(
57895945
/// \code
57905946
/// a || b
57915947
/// \endcode
5792-
AST_POLYMORPHIC_MATCHER_P(hasLHS,
5793-
AST_POLYMORPHIC_SUPPORTED_TYPES(
5794-
BinaryOperator, CXXOperatorCallExpr,
5795-
CXXRewrittenBinaryOperator, ArraySubscriptExpr),
5796-
internal::Matcher<Expr>, InnerMatcher) {
5948+
AST_POLYMORPHIC_MATCHER_P(
5949+
hasLHS,
5950+
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
5951+
CXXRewrittenBinaryOperator,
5952+
ArraySubscriptExpr, CXXFoldExpr),
5953+
internal::Matcher<Expr>, InnerMatcher) {
57975954
const Expr *LeftHandSide = internal::getLHS(Node);
57985955
return (LeftHandSide != nullptr &&
57995956
InnerMatcher.matches(*LeftHandSide, Finder, Builder));
@@ -5805,29 +5962,31 @@ AST_POLYMORPHIC_MATCHER_P(hasLHS,
58055962
/// \code
58065963
/// a || b
58075964
/// \endcode
5808-
AST_POLYMORPHIC_MATCHER_P(hasRHS,
5809-
AST_POLYMORPHIC_SUPPORTED_TYPES(
5810-
BinaryOperator, CXXOperatorCallExpr,
5811-
CXXRewrittenBinaryOperator, ArraySubscriptExpr),
5812-
internal::Matcher<Expr>, InnerMatcher) {
5965+
AST_POLYMORPHIC_MATCHER_P(
5966+
hasRHS,
5967+
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
5968+
CXXRewrittenBinaryOperator,
5969+
ArraySubscriptExpr, CXXFoldExpr),
5970+
internal::Matcher<Expr>, InnerMatcher) {
58135971
const Expr *RightHandSide = internal::getRHS(Node);
58145972
return (RightHandSide != nullptr &&
58155973
InnerMatcher.matches(*RightHandSide, Finder, Builder));
58165974
}
58175975

58185976
/// Matches if either the left hand side or the right hand side of a
5819-
/// binary operator matches.
5977+
/// binary operator or fold expression matches.
58205978
AST_POLYMORPHIC_MATCHER_P(
58215979
hasEitherOperand,
58225980
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
5823-
CXXRewrittenBinaryOperator),
5981+
CXXFoldExpr, CXXRewrittenBinaryOperator),
58245982
internal::Matcher<Expr>, InnerMatcher) {
58255983
return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
58265984
anyOf(hasLHS(InnerMatcher), hasRHS(InnerMatcher)))
58275985
.matches(Node, Finder, Builder);
58285986
}
58295987

5830-
/// Matches if both matchers match with opposite sides of the binary operator.
5988+
/// Matches if both matchers match with opposite sides of the binary operator
5989+
/// or fold expression.
58315990
///
58325991
/// Example matcher = binaryOperator(hasOperands(integerLiteral(equals(1),
58335992
/// integerLiteral(equals(2)))
@@ -5840,7 +5999,7 @@ AST_POLYMORPHIC_MATCHER_P(
58405999
AST_POLYMORPHIC_MATCHER_P2(
58416000
hasOperands,
58426001
AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr,
5843-
CXXRewrittenBinaryOperator),
6002+
CXXFoldExpr, CXXRewrittenBinaryOperator),
58446003
internal::Matcher<Expr>, Matcher1, internal::Matcher<Expr>, Matcher2) {
58456004
return internal::VariadicDynCastAllOfMatcher<Stmt, NodeType>()(
58466005
anyOf(allOf(hasLHS(Matcher1), hasRHS(Matcher2)),

clang/include/clang/ASTMatchers/ASTMatchersInternal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2195,6 +2195,9 @@ inline std::optional<StringRef> getOpName(const CXXOperatorCallExpr &Node) {
21952195
}
21962196
return BinaryOperator::getOpcodeStr(*optBinaryOpcode);
21972197
}
2198+
inline StringRef getOpName(const CXXFoldExpr &Node) {
2199+
return BinaryOperator::getOpcodeStr(Node.getOperator());
2200+
}
21982201

21992202
/// Matches overloaded operators with a specific name.
22002203
///

clang/lib/ASTMatchers/ASTMatchersInternal.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,7 @@ const internal::VariadicDynCastAllOfMatcher<Stmt, CXXOperatorCallExpr>
893893
cxxOperatorCallExpr;
894894
const internal::VariadicDynCastAllOfMatcher<Stmt, CXXRewrittenBinaryOperator>
895895
cxxRewrittenBinaryOperator;
896+
const internal::VariadicDynCastAllOfMatcher<Stmt, CXXFoldExpr> cxxFoldExpr;
896897
const internal::VariadicDynCastAllOfMatcher<Stmt, Expr> expr;
897898
const internal::VariadicDynCastAllOfMatcher<Stmt, DeclRefExpr> declRefExpr;
898899
const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCIvarRefExpr> objcIvarRefExpr;

clang/lib/ASTMatchers/Dynamic/Registry.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ RegistryMaps::RegistryMaps() {
198198
REGISTER_MATCHER(cxxDependentScopeMemberExpr);
199199
REGISTER_MATCHER(cxxDestructorDecl);
200200
REGISTER_MATCHER(cxxDynamicCastExpr);
201+
REGISTER_MATCHER(cxxFoldExpr);
201202
REGISTER_MATCHER(cxxForRangeStmt);
202203
REGISTER_MATCHER(cxxFunctionalCastExpr);
203204
REGISTER_MATCHER(cxxMemberCallExpr);
@@ -319,6 +320,7 @@ RegistryMaps::RegistryMaps() {
319320
REGISTER_MATCHER(hasExplicitSpecifier);
320321
REGISTER_MATCHER(hasExternalFormalLinkage);
321322
REGISTER_MATCHER(hasFalseExpression);
323+
REGISTER_MATCHER(hasFoldInit);
322324
REGISTER_MATCHER(hasGlobalStorage);
323325
REGISTER_MATCHER(hasImplicitDestinationType);
324326
REGISTER_MATCHER(hasInClassInitializer);
@@ -344,6 +346,7 @@ RegistryMaps::RegistryMaps() {
344346
REGISTER_MATCHER(hasOverloadedOperatorName);
345347
REGISTER_MATCHER(hasParameter);
346348
REGISTER_MATCHER(hasParent);
349+
REGISTER_MATCHER(hasPattern);
347350
REGISTER_MATCHER(hasPointeeLoc);
348351
REGISTER_MATCHER(hasQualifier);
349352
REGISTER_MATCHER(hasRHS);
@@ -404,6 +407,7 @@ RegistryMaps::RegistryMaps() {
404407
REGISTER_MATCHER(isAssignmentOperator);
405408
REGISTER_MATCHER(isAtPosition);
406409
REGISTER_MATCHER(isBaseInitializer);
410+
REGISTER_MATCHER(isBinaryFold);
407411
REGISTER_MATCHER(isBitField);
408412
REGISTER_MATCHER(isCatchAll);
409413
REGISTER_MATCHER(isClass);
@@ -447,6 +451,7 @@ RegistryMaps::RegistryMaps() {
447451
REGISTER_MATCHER(isInteger);
448452
REGISTER_MATCHER(isIntegral);
449453
REGISTER_MATCHER(isLambda);
454+
REGISTER_MATCHER(isLeftFold);
450455
REGISTER_MATCHER(isListInitialization);
451456
REGISTER_MATCHER(isMain);
452457
REGISTER_MATCHER(isMemberInitializer);
@@ -460,6 +465,7 @@ RegistryMaps::RegistryMaps() {
460465
REGISTER_MATCHER(isProtected);
461466
REGISTER_MATCHER(isPublic);
462467
REGISTER_MATCHER(isPure);
468+
REGISTER_MATCHER(isRightFold);
463469
REGISTER_MATCHER(isScoped);
464470
REGISTER_MATCHER(isSharedKind);
465471
REGISTER_MATCHER(isSignedInteger);
@@ -469,6 +475,7 @@ RegistryMaps::RegistryMaps() {
469475
REGISTER_MATCHER(isStruct);
470476
REGISTER_MATCHER(isTemplateInstantiation);
471477
REGISTER_MATCHER(isTypeDependent);
478+
REGISTER_MATCHER(isUnaryFold);
472479
REGISTER_MATCHER(isUnion);
473480
REGISTER_MATCHER(isUnsignedInteger);
474481
REGISTER_MATCHER(isUserProvided);

0 commit comments

Comments
 (0)