forked from OSchip/llvm-project
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.
This commit is contained in:
parent
b675a7628c
commit
2823e91d55
|
@ -4689,6 +4689,32 @@ Usable as: Any Matcher
|
|||
</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 @@ 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 > 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 @@ 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 > 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 @@ 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 > 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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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).
|
||||
|
|
Loading…
Reference in New Issue