Index: clang/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -2529,6 +2529,16 @@ 2, std::numeric_limits::max()> allOf; +/// Matches any node regardless of the submatchers. +/// +/// However, \c optionally will generate a result binding for each matching +/// submatchers. +/// +/// Usable as: Any Matcher +extern const internal::VariadicOperatorMatcherFunc< + 1, std::numeric_limits::max()> + optionally; + /// Matches sizeof (C99), alignof (C++11) and vec_step (OpenCL) /// /// Given Index: clang/include/clang/ASTMatchers/ASTMatchersInternal.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -363,6 +363,10 @@ /// matches, but doesn't stop at the first match. VO_EachOf, + /// Matches any node but executes all inner matchers to find result + /// bindings. + VO_Optionally, + /// Matches nodes that do not match the provided matcher. /// /// Uses the variadic matcher interface, but fails if Index: clang/lib/ASTMatchers/ASTMatchersInternal.cpp =================================================================== --- clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -68,6 +68,11 @@ BoundNodesTreeBuilder *Builder, ArrayRef InnerMatchers); +bool OptionallyVariadicOperator(const ast_type_traits::DynTypedNode &DynNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder, + ArrayRef InnerMatchers); + void BoundNodesTreeBuilder::visitMatches(Visitor *ResultVisitor) { if (Bindings.empty()) Bindings.push_back(BoundNodesMap()); @@ -179,6 +184,11 @@ SupportedKind, RestrictKind, new VariadicMatcher(std::move(InnerMatchers))); + case VO_Optionally: + return DynTypedMatcher( + SupportedKind, RestrictKind, + new VariadicMatcher(std::move(InnerMatchers))); + case VO_UnaryNot: // FIXME: Implement the Not operator to take a single matcher instead of a // vector. @@ -342,6 +352,21 @@ return false; } +bool OptionallyVariadicOperator(const ast_type_traits::DynTypedNode &DynNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder, + ArrayRef InnerMatchers) { + BoundNodesTreeBuilder Result; + for (const DynTypedMatcher &InnerMatcher : InnerMatchers) { + BoundNodesTreeBuilder BuilderInner(*Builder); + if (InnerMatcher.matches(DynNode, Finder, &BuilderInner)) { + Result.addMatch(BuilderInner); + } + } + *Builder = std::move(Result); + return true; +} + inline static std::vector vectorFromRefs(ArrayRef NameRefs) { std::vector Names; @@ -792,6 +817,9 @@ const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits::max()> allOf = {internal::DynTypedMatcher::VO_AllOf}; +const internal::VariadicOperatorMatcherFunc< + 1, std::numeric_limits::max()> + optionally = {internal::DynTypedMatcher::VO_Optionally}; const internal::VariadicFunction, StringRef, internal::hasAnyNameFunc> hasAnyName = {}; Index: clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp =================================================================== --- clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -1866,6 +1866,27 @@ has(fieldDecl(hasName("b")).bind("v")))))); } +TEST(Optionally, SubmatchersDoNotMatch) { + EXPECT_TRUE(matchAndVerifyResultFalse( + "class A { int a; int b; };", + recordDecl(optionally(has(fieldDecl(hasName("c")).bind("v")), + has(fieldDecl(hasName("d")).bind("v")))), + std::make_unique>("v"))); +} + +TEST(Optionally, SubmatchersMatch) { + EXPECT_TRUE(matchAndVerifyResultTrue( + "class A { int a; int c; };", + recordDecl(optionally(has(fieldDecl(hasName("a")).bind("v")), + has(fieldDecl(hasName("b")).bind("v")))), + std::make_unique>("v", 1))); + EXPECT_TRUE(matchAndVerifyResultTrue( + "class A { int c; int b; };", + recordDecl(optionally(has(fieldDecl(hasName("c")).bind("v")), + has(fieldDecl(hasName("b")).bind("v")))), + std::make_unique>("v", 2))); +} + TEST(IsTemplateInstantiation, MatchesImplicitClassTemplateInstantiation) { // Make sure that we can both match the class by name (::X) and by the type // the template was instantiated with (via a field).