Author: Rihan Yang Date: 2020-01-08T14:10:11-05:00 New Revision: 2823e91d55891e33a7a8b9a4016db4ec9e2765ae
URL: https://github.com/llvm/llvm-project/commit/2823e91d55891e33a7a8b9a4016db4ec9e2765ae DIFF: https://github.com/llvm/llvm-project/commit/2823e91d55891e33a7a8b9a4016db4ec9e2765ae.diff LOG: Add a new AST matcher 'optionally'. This matcher matches any node and at the same time executes all its inner matchers to produce any possbile result bindings. This is useful when a user wants certain supplementary information that's not always present along with the main match result. Added: Modified: 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 Removed: ################################################################################ diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html index 338399976f4f..05562c543c47 100644 --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -4689,6 +4689,32 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2> </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 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2> <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 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2> <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 @@ <h2 id="traversal-matchers">AST Traversal Matchers</h2> <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. diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index 54ccaabadbe4..71cb85264bc2 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -2540,6 +2540,36 @@ extern const internal::VariadicOperatorMatcherFunc< 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 diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h index 11b49820476c..c4b449fa9434 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -363,6 +363,10 @@ class DynTypedMatcher { /// 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 diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp index 75846ab2d4b1..199a6d839e2e 100644 --- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp +++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp @@ -68,6 +68,11 @@ bool AnyOfVariadicOperator(const ast_type_traits::DynTypedNode &DynNode, 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()); @@ -184,6 +189,11 @@ DynTypedMatcher DynTypedMatcher::constructVariadic( 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. @@ -347,6 +357,20 @@ bool AnyOfVariadicOperator(const ast_type_traits::DynTypedNode &DynNode, 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; @@ -797,6 +821,9 @@ const internal::VariadicOperatorMatcherFunc< 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 = {}; diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp index 78b52d6fa2fd..475818bc3ae5 100644 --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -456,6 +456,7 @@ RegistryMaps::RegistryMaps() { REGISTER_MATCHER(on); REGISTER_MATCHER(onImplicitObjectArgument); REGISTER_MATCHER(opaqueValueExpr); + REGISTER_MATCHER(optionally); REGISTER_MATCHER(parameterCountIs); REGISTER_MATCHER(parenExpr); REGISTER_MATCHER(parenListExpr); diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index e7f9232e968f..92678a309196 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -1866,6 +1866,27 @@ TEST(EachOf, BehavesLikeAnyOfUnlessBothMatch) { 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). _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits