diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -347,6 +347,16 @@ extern const internal::VariadicDynCastAllOfMatcher decompositionDecl; +/// Matches binding declarations +/// Example matches \c foo and \c bar +/// (matcher = bindingDecl() +/// +/// \code +/// auto [foo, bar] = std::make_pair{42, 42}; +/// \endcode +extern const internal::VariadicDynCastAllOfMatcher + bindingDecl; + /// Matches a declaration of a linkage specification. /// /// Given @@ -7379,6 +7389,30 @@ Expr::NPC_ValueDependentIsNull); } +/// Matches the DecompositionDecl the binding belongs to. +AST_MATCHER_P(BindingDecl, forDecomposition, internal::Matcher, + InnerMatcher) { + if (!Node.getDecomposedDecl()) + return false; + return InnerMatcher.matches(*Node.getDecomposedDecl(), Finder, Builder); +} + +/// Matches the Nth binding of a DecompositionDecl. +AST_MATCHER_P2(DecompositionDecl, hasBinding, unsigned, N, + internal::Matcher, InnerMatcher) { + if (Node.bindings().size() <= N) + return false; + return InnerMatcher.matches(*Node.bindings()[N], Finder, Builder); +} + +/// Matches any binding of a DecompositionDecl. +AST_MATCHER_P(DecompositionDecl, hasAnyBinding, internal::Matcher, + InnerMatcher) { + return llvm::any_of(Node.bindings(), [&](const auto *Binding) { + return InnerMatcher.matches(*Binding, Finder, Builder); + }); +} + /// Matches declaration of the function the statement belongs to /// /// Given: diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -147,6 +147,7 @@ REGISTER_MATCHER(binaryConditionalOperator); REGISTER_MATCHER(binaryOperator); REGISTER_MATCHER(binaryOperation); + REGISTER_MATCHER(bindingDecl); REGISTER_MATCHER(blockDecl); REGISTER_MATCHER(blockExpr); REGISTER_MATCHER(blockPointerType); @@ -229,6 +230,7 @@ REGISTER_MATCHER(exprWithCleanups); REGISTER_MATCHER(fieldDecl); REGISTER_MATCHER(floatLiteral); + REGISTER_MATCHER(forDecomposition); REGISTER_MATCHER(forEach); REGISTER_MATCHER(forEachArgumentWithParam); REGISTER_MATCHER(forEachArgumentWithParamType); @@ -269,6 +271,8 @@ REGISTER_MATCHER(hasAttr); REGISTER_MATCHER(hasAutomaticStorageDuration); REGISTER_MATCHER(hasBase); + REGISTER_MATCHER(hasAnyBinding); + REGISTER_MATCHER(hasBinding); REGISTER_MATCHER(hasBitWidth); REGISTER_MATCHER(hasBody); REGISTER_MATCHER(hasCanonicalType); diff --git a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNodeTest.cpp @@ -2129,6 +2129,28 @@ EXPECT_TRUE(matchesObjC(ObjCString, objcFinallyStmt())); } +TEST(ASTMatchersTest, DecompositionDecl) { + StringRef Code = R"cpp( +void foo() +{ + int arr[3]; + auto &[f, s, t] = arr; + + f = 42; +} + )cpp"; + EXPECT_TRUE(matchesConditionally( + Code, decompositionDecl(hasBinding(0, bindingDecl(hasName("f")))), true, + {"-std=c++17"})); + + EXPECT_TRUE(matchesConditionally( + Code, + bindingDecl(decl().bind("self"), hasName("f"), + forDecomposition(decompositionDecl( + hasAnyBinding(bindingDecl(equalsBoundNode("self")))))), + true, {"-std=c++17"})); +} + TEST(ASTMatchersTestObjC, ObjCAutoreleasePoolStmt) { StringRef ObjCString = "void f() {" "@autoreleasepool {"