summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/docs/LibASTMatchersReference.html50
-rw-r--r--clang/include/clang/ASTMatchers/ASTMatchers.h30
-rw-r--r--clang/include/clang/ASTMatchers/ASTMatchersInternal.h4
-rw-r--r--clang/lib/ASTMatchers/ASTMatchersInternal.cpp27
-rw-r--r--clang/lib/ASTMatchers/Dynamic/Registry.cpp1
-rw-r--r--clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp21
6 files changed, 121 insertions, 12 deletions
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html
index 338399976f4..05562c543c4 100644
--- a/clang/docs/LibASTMatchersReference.html
+++ b/clang/docs/LibASTMatchersReference.html
@@ -4689,6 +4689,32 @@ Usable as: Any Matcher
</pre></td></tr>
+<tr><td>Matcher&lt;*&gt;</td><td class="name" onclick="toggle('optionally0')"><a name="optionally0Anchor">optionally</a></td><td>Matcher&lt;*&gt;, ..., Matcher&lt;*&gt;</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&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1AbstractConditionalOperator.html">AbstractConditionalOperator</a>&gt;</td><td class="name" onclick="toggle('hasCondition5')"><a name="hasCondition5Anchor">hasCondition</a></td><td>Matcher&lt;<a href="https://clang.llvm.org/doxygen/classclang_1_1Expr.html">Expr</a>&gt; 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 @@ with compoundStmt()
<tr><td colspan="4" class="doc" id="hasInitStatement2"><pre>Matches selection statements with initializer.
Given:
- void foo() {
+ void foo() {
if (int i = foobar(); i &gt; 0) {}
switch (int i = foobar(); i) {}
- for (auto&amp; a = get_range(); auto&amp; x : a) {}
+ for (auto&amp; a = get_range(); auto&amp; x : a) {}
}
- void bar() {
+ void bar() {
if (foobar() &gt; 0) {}
switch (foobar()) {}
- for (auto&amp; x : get_range()) {}
+ for (auto&amp; x : get_range()) {}
}
ifStmt(hasInitStatement(anything()))
matches the if statement in foo but not in bar.
@@ -6245,15 +6271,15 @@ Examples matches the if statement
<tr><td colspan="4" class="doc" id="hasInitStatement0"><pre>Matches selection statements with initializer.
Given:
- void foo() {
+ void foo() {
if (int i = foobar(); i &gt; 0) {}
switch (int i = foobar(); i) {}
- for (auto&amp; a = get_range(); auto&amp; x : a) {}
+ for (auto&amp; a = get_range(); auto&amp; x : a) {}
}
- void bar() {
+ void bar() {
if (foobar() &gt; 0) {}
switch (foobar()) {}
- for (auto&amp; x : get_range()) {}
+ for (auto&amp; x : get_range()) {}
}
ifStmt(hasInitStatement(anything()))
matches the if statement in foo but not in bar.
@@ -7005,15 +7031,15 @@ Example matches true (matcher = hasCondition(cxxBoolLiteral(equals(true))))
<tr><td colspan="4" class="doc" id="hasInitStatement1"><pre>Matches selection statements with initializer.
Given:
- void foo() {
+ void foo() {
if (int i = foobar(); i &gt; 0) {}
switch (int i = foobar(); i) {}
- for (auto&amp; a = get_range(); auto&amp; x : a) {}
+ for (auto&amp; a = get_range(); auto&amp; x : a) {}
}
- void bar() {
+ void bar() {
if (foobar() &gt; 0) {}
switch (foobar()) {}
- for (auto&amp; x : get_range()) {}
+ for (auto&amp; 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 54ccaabadbe..71cb85264bc 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 11b49820476..c4b449fa943 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -363,6 +363,10 @@ public:
/// 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 75846ab2d4b..199a6d839e2 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 78b52d6fa2f..475818bc3ae 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 e7f9232e968..92678a30919 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).
OpenPOWER on IntegriCloud