air20 updated this revision to Diff 236513. air20 added a comment. Included base commits that was missing in the previous diff.
CHANGES SINCE LAST ACTION https://reviews.llvm.org/D72233/new/ https://reviews.llvm.org/D72233 Files: clang/docs/LibASTMatchersReference.html clang/include/clang/ASTMatchers/ASTMatchers.h clang/include/clang/ASTMatchers/ASTMatchersInternal.h clang/lib/ASTMatchers/ASTMatchersInternal.cpp clang/lib/ASTMatchers/Dynamic/Registry.cpp clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp
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<VerifyIdIsBoundTo<FieldDecl>>("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<VerifyIdIsBoundTo<FieldDecl>>("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<VerifyIdIsBoundTo<FieldDecl>>("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). Index: clang/lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -278,8 +278,8 @@ REGISTER_MATCHER(hasIncrement); REGISTER_MATCHER(hasIndex); REGISTER_MATCHER(hasInit); - REGISTER_MATCHER(hasInitializer); REGISTER_MATCHER(hasInitStatement); + REGISTER_MATCHER(hasInitializer); REGISTER_MATCHER(hasKeywordSelector); REGISTER_MATCHER(hasLHS); REGISTER_MATCHER(hasLocalQualifiers); @@ -455,6 +455,7 @@ REGISTER_MATCHER(on); REGISTER_MATCHER(onImplicitObjectArgument); REGISTER_MATCHER(opaqueValueExpr); + REGISTER_MATCHER(optionally); REGISTER_MATCHER(parameterCountIs); REGISTER_MATCHER(parenExpr); REGISTER_MATCHER(parenListExpr); Index: clang/lib/ASTMatchers/ASTMatchersInternal.cpp =================================================================== --- clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -68,6 +68,11 @@ BoundNodesTreeBuilder *Builder, ArrayRef<DynTypedMatcher> InnerMatchers); +bool OptionallyVariadicOperator(const ast_type_traits::DynTypedNode &DynNode, + ASTMatchFinder *Finder, + BoundNodesTreeBuilder *Builder, + ArrayRef<DynTypedMatcher> InnerMatchers); + void BoundNodesTreeBuilder::visitMatches(Visitor *ResultVisitor) { if (Bindings.empty()) Bindings.push_back(BoundNodesMap()); @@ -179,6 +184,11 @@ SupportedKind, RestrictKind, new VariadicMatcher<EachOfVariadicOperator>(std::move(InnerMatchers))); + case VO_Optionally: + return DynTypedMatcher(SupportedKind, RestrictKind, + new VariadicMatcher<OptionallyVariadicOperator>( + 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<DynTypedMatcher> 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<std::string> vectorFromRefs(ArrayRef<const StringRef *> NameRefs) { std::vector<std::string> Names; @@ -792,6 +817,9 @@ const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits<unsigned>::max()> allOf = {internal::DynTypedMatcher::VO_AllOf}; +const internal::VariadicOperatorMatcherFunc< + 1, std::numeric_limits<unsigned>::max()> + optionally = {internal::DynTypedMatcher::VO_Optionally}; const internal::VariadicFunction<internal::Matcher<NamedDecl>, StringRef, internal::hasAnyNameFunc> hasAnyName = {}; 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/include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- clang/include/clang/ASTMatchers/ASTMatchers.h +++ clang/include/clang/ASTMatchers/ASTMatchers.h @@ -2529,6 +2529,36 @@ 2, std::numeric_limits<unsigned>::max()> allOf; +/// Matches any node regardless of the submatchers. +/// +/// However, \c optionally will generate a result binding for each matching +/// submatcher. +/// +/// Useful when additional information which may or may not present about a +/// main matching node is desired. +/// +/// For example, in: +/// \code +/// class Foo { +/// int bar; +/// } +/// \endcode +/// The matcher: +/// \code +/// cxxRecordDecl( +/// optionally(has( +/// fieldDecl(hasName("bar")).bind("var") +/// ))).bind("record") +/// \endcode +/// will produce a result binding for both "record" and "var". +/// The matcher will produce a "record" binding for even if there is no data +/// member named "bar" in that class. +/// +/// Usable as: Any Matcher +extern const internal::VariadicOperatorMatcherFunc< + 1, std::numeric_limits<unsigned>::max()> + optionally; + /// Matches sizeof (C99), alignof (C++11) and vec_step (OpenCL) /// /// Given Index: clang/docs/LibASTMatchersReference.html =================================================================== --- clang/docs/LibASTMatchersReference.html +++ clang/docs/LibASTMatchersReference.html @@ -4689,6 +4689,32 @@ </pre></td></tr> +<tr><td>Matcher<*></td><td class="name" onclick="toggle('optionally0')"><a name="optionally0Anchor">optionally</a></td><td>Matcher<*>, ..., Matcher<*></td></tr> +<tr><td colspan="4" class="doc" id="optionally0"><pre>Matches any node regardless of the submatchers. + +However, optionally will generate a result binding for each matching +submatcher. + +Useful when additional information which may or may not present about a +main matching node is desired. + +For example, in: + class Foo { + int bar; + } +The matcher: + cxxRecordDecl( + optionally(has( + fieldDecl(hasName("bar")).bind("var") + ))).bind("record") +will produce a result binding for both "record" and "var". +The matcher will produce a "record" binding for even if there is no data +member named "bar" in that class. + +Usable as: Any Matcher +</pre></td></tr> + + <tr><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1AbstractConditionalOperator.html">AbstractConditionalOperator</a>></td><td class="name" onclick="toggle('hasCondition5')"><a name="hasCondition5Anchor">hasCondition</a></td><td>Matcher<<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>> InnerMatcher</td></tr> <tr><td colspan="4" class="doc" id="hasCondition5"><pre>Matches the condition expression of an if statement, for loop, switch statement or conditional operator. @@ -5098,15 +5124,15 @@ <tr><td colspan="4" class="doc" id="hasInitStatement2"><pre>Matches selection statements with initializer. Given: - void foo() { + void foo() { if (int i = foobar(); i > 0) {} switch (int i = foobar(); i) {} - for (auto& a = get_range(); auto& x : a) {} + for (auto& a = get_range(); auto& x : a) {} } - void bar() { + void bar() { if (foobar() > 0) {} switch (foobar()) {} - for (auto& x : get_range()) {} + for (auto& x : get_range()) {} } ifStmt(hasInitStatement(anything())) matches the if statement in foo but not in bar. @@ -6245,15 +6271,15 @@ <tr><td colspan="4" class="doc" id="hasInitStatement0"><pre>Matches selection statements with initializer. Given: - void foo() { + void foo() { if (int i = foobar(); i > 0) {} switch (int i = foobar(); i) {} - for (auto& a = get_range(); auto& x : a) {} + for (auto& a = get_range(); auto& x : a) {} } - void bar() { + void bar() { if (foobar() > 0) {} switch (foobar()) {} - for (auto& x : get_range()) {} + for (auto& x : get_range()) {} } ifStmt(hasInitStatement(anything())) matches the if statement in foo but not in bar. @@ -7005,15 +7031,15 @@ <tr><td colspan="4" class="doc" id="hasInitStatement1"><pre>Matches selection statements with initializer. Given: - void foo() { + void foo() { if (int i = foobar(); i > 0) {} switch (int i = foobar(); i) {} - for (auto& a = get_range(); auto& x : a) {} + for (auto& a = get_range(); auto& x : a) {} } - void bar() { + void bar() { if (foobar() > 0) {} switch (foobar()) {} - for (auto& x : get_range()) {} + for (auto& x : get_range()) {} } ifStmt(hasInitStatement(anything())) matches the if statement in foo but not in bar.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits