/// FIXME: Add example for accessing it.
template <typename T>
internal::Matcher<T> id(const std::string &ID,
- const internal::Matcher<T> &InnerMatcher) {
- return internal::Matcher<T>(new internal::IdMatcher<T>(ID, InnerMatcher));
+ const internal::BindableMatcher<T> &InnerMatcher) {
+ return InnerMatcher.bind(ID);
}
/// \brief Types of matchers for the top-level classes in the AST class
/// \brief Matches call expressions.
///
-/// Example matches x.y()
+/// Example matches x.y() and y()
/// X x;
/// x.y();
+/// y();
const internal::VariadicDynCastAllOfMatcher<Stmt, CallExpr> call;
+/// \brief Matches member call expressions.
+///
+/// Example matches x.y()
+/// X x;
+/// x.y();
+const internal::VariadicDynCastAllOfMatcher<Stmt, CXXMemberCallExpr> memberCall;
+
/// \brief Matches init list expressions.
///
/// Given
/// \brief Matches if the expression's type either matches the specified
/// matcher, or is a pointer to a type that matches the InnerMatcher.
-inline internal::Matcher<CallExpr> thisPointerType(
+inline internal::Matcher<CXXMemberCallExpr> thisPointerType(
const internal::Matcher<QualType> &InnerMatcher) {
return onImplicitObjectArgument(
anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher))));
}
/// \brief Overloaded to match the type's declaration.
-inline internal::Matcher<CallExpr> thisPointerType(
+inline internal::Matcher<CXXMemberCallExpr> thisPointerType(
const internal::Matcher<Decl> &InnerMatcher) {
return onImplicitObjectArgument(
anyOf(hasType(InnerMatcher), hasType(pointsTo(InnerMatcher))));
BindKind Bind) = 0;
};
-/// \brief Converts a Matcher<T> to a matcher of desired type To by "adapting"
-/// a To into a T.
+/// \brief Converts a \c Matcher<T> to a matcher of desired type \c To by
+/// "adapting" a \c To into a \c T.
///
-/// The ArgumentAdapterT argument specifies how the adaptation is done.
+/// The \c ArgumentAdapterT argument specifies how the adaptation is done.
///
/// For example:
-/// ArgumentAdaptingMatcher<DynCastMatcher, T>(InnerMatcher);
-/// returns a matcher that can be used where a Matcher<To> is required, if
-/// To and T are in the same type hierarchy, and thus dyn_cast can be
-/// called to convert a To to a T.
+/// \c ArgumentAdaptingMatcher<HasMatcher, T>(InnerMatcher);
+/// Given that \c InnerMatcher is of type \c Matcher<T>, this returns a matcher
+/// that is convertible into any matcher of type \c To by constructing
+/// \c HasMatcher<To, T>(InnerMatcher).
///
-/// FIXME: Make sure all our applications of this class actually require
-/// knowledge about the inner type. DynCastMatcher obviously does, but the
-/// Has *matchers require the inner type solely for COMPILE_ASSERT purposes.
+/// If a matcher does not need knowledge about the inner type, prefer to use
+/// PolymorphicMatcherWithParam1.
template <template <typename ToArg, typename FromArg> class ArgumentAdapterT,
typename T>
class ArgumentAdaptingMatcher {
const Matcher<To> InnerMatcher;
};
-/// \brief Enables the user to pass a Matcher<CXXMemberCallExpr> to
-/// Call().
-///
-/// FIXME: Alternatives are using more specific methods than Call, like
-/// MemberCall, or not using VariadicFunction for Call and overloading it.
-template <>
-template <>
-inline Matcher<CXXMemberCallExpr>::
-operator Matcher<CallExpr>() const {
- return makeMatcher(
- new DynCastMatcher<CallExpr, CXXMemberCallExpr>(*this));
-}
-
/// \brief Matcher<T> that wraps an inner Matcher<T> and binds the matched node
/// to an ID if the inner matcher matches on the node.
template <typename T>
const Matcher<T> InnerMatcher;
};
+/// \brief A Matcher that allows binding the node it matches to an id.
+///
+/// BindableMatcher provides a \a bind() method that allows binding the
+/// matched node to an id if the match was successful.
+template <typename T>
+class BindableMatcher : public Matcher<T> {
+public:
+ BindableMatcher(MatcherInterface<T> *Implementation)
+ : Matcher<T>(Implementation) {}
+
+ /// \brief Returns a matcher that will bind the matched node on a match.
+ ///
+ /// The returned matcher is equivalent to this matcher, but will
+ /// bind the matched node on a match.
+ Matcher<T> bind(StringRef ID) const {
+ TOOLING_COMPILE_ASSERT((llvm::is_base_of<Stmt, T>::value ||
+ llvm::is_base_of<Decl, T>::value),
+ trying_to_bind_unsupported_node_type__only_decl_and_stmt_supported);
+ return Matcher<T>(new IdMatcher<T>(ID, *this));
+ }
+};
+
/// \brief Matches nodes of type T that have child nodes of type ChildT for
/// which a specified child matcher matches.
///
/// \brief Creates a Matcher<T> that matches if
/// T is dyn_cast'able into InnerT and all inner matchers match.
+///
+/// Returns BindableMatcher, as matchers that use dyn_cast have
+/// the same object both to match on and to run submatchers on,
+/// so there is no ambiguity with what gets bound.
template<typename T, typename InnerT>
-Matcher<T> makeDynCastAllOfComposite(
+BindableMatcher<T> makeDynCastAllOfComposite(
ArrayRef<const Matcher<InnerT> *> InnerMatchers) {
if (InnerMatchers.empty()) {
- return ArgumentAdaptingMatcher<DynCastMatcher, InnerT>(
- makeMatcher(new TrueMatcher<InnerT>));
+ Matcher<InnerT> InnerMatcher = makeMatcher(new TrueMatcher<InnerT>);
+ return BindableMatcher<T>(new DynCastMatcher<T, InnerT>(InnerMatcher));
}
Matcher<InnerT> InnerMatcher = *InnerMatchers.back();
for (int i = InnerMatchers.size() - 2; i >= 0; --i) {
new AllOfMatcher<InnerT, Matcher<InnerT>, Matcher<InnerT> >(
*InnerMatchers[i], InnerMatcher));
}
- return ArgumentAdaptingMatcher<DynCastMatcher, InnerT>(InnerMatcher);
+ return BindableMatcher<T>(new DynCastMatcher<T, InnerT>(InnerMatcher));
}
/// \brief Matches nodes of type T that have at least one descendant node of
template <typename SourceT, typename TargetT>
class VariadicDynCastAllOfMatcher
: public llvm::VariadicFunction<
- Matcher<SourceT>, Matcher<TargetT>,
+ BindableMatcher<SourceT>, Matcher<TargetT>,
makeDynCastAllOfComposite<SourceT, TargetT> > {
public:
VariadicDynCastAllOfMatcher() {}
EXPECT_TRUE(matches(
"class X {}; class Y : public X {};",
- record(isDerivedFrom(id("test", record(hasName("X")))))));
+ record(isDerivedFrom(record(hasName("X")).bind("test")))));
}
TEST(AllOf, AllOverloadsWork) {
};
TEST(Matcher, BindMatchedNodes) {
- DeclarationMatcher ClassX = has(id("x", record(hasName("X"))));
+ DeclarationMatcher ClassX = has(record(hasName("::X")).bind("x"));
EXPECT_TRUE(matchAndVerifyResultTrue("class X {};",
ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("x")));
ClassX, new VerifyIdIsBoundToDecl<CXXRecordDecl>("other-id")));
TypeMatcher TypeAHasClassB = hasDeclaration(
- record(hasName("A"), has(id("b", record(hasName("B"))))));
+ record(hasName("A"), has(record(hasName("B")).bind("b"))));
EXPECT_TRUE(matchAndVerifyResultTrue("class A { public: A *a; class B {}; };",
TypeAHasClassB,
new VerifyIdIsBoundToDecl<Decl>("b")));
- StatementMatcher MethodX = id("x", call(callee(method(hasName("x")))));
+ StatementMatcher MethodX = call(callee(method(hasName("x")))).bind("x");
EXPECT_TRUE(matchAndVerifyResultTrue("class A { void x() { x(); } };",
MethodX,
TEST(Matcher, BindTheSameNameInAlternatives) {
StatementMatcher matcher = anyOf(
binaryOperator(hasOperatorName("+"),
- hasLHS(id("x", expression())),
+ hasLHS(expression().bind("x")),
hasRHS(integerLiteral(equals(0)))),
binaryOperator(hasOperatorName("+"),
hasLHS(integerLiteral(equals(0))),
- hasRHS(id("x", expression()))));
+ hasRHS(expression().bind("x"))));
EXPECT_TRUE(matchAndVerifyResultTrue(
// The first branch of the matcher binds x to 0 but then fails.
EXPECT_TRUE(matches("class Y { void x() { x(); } };", MethodX));
EXPECT_TRUE(notMatches("class Y { void x() {} };", MethodX));
- StatementMatcher MethodOnY = call(on(hasType(record(hasName("Y")))));
+ StatementMatcher MethodOnY = memberCall(on(hasType(record(hasName("Y")))));
EXPECT_TRUE(
matches("class Y { public: void x(); }; void z() { Y y; y.x(); }",
MethodOnY));
StatementMatcher MethodOnYPointer =
- call(on(hasType(pointsTo(record(hasName("Y"))))));
+ memberCall(on(hasType(pointsTo(record(hasName("Y"))))));
EXPECT_TRUE(
matches("class Y { public: void x(); }; void z() { Y *y; y->x(); }",
TEST(HasType, MatchesAsString) {
EXPECT_TRUE(
matches("class Y { public: void x(); }; void z() {Y* y; y->x(); }",
- call(on(hasType(asString("class Y *"))))));
+ memberCall(on(hasType(asString("class Y *"))))));
EXPECT_TRUE(matches("class X { void x(int x) {} };",
method(hasParameter(0, hasType(asString("int"))))));
EXPECT_TRUE(matches("namespace ns { struct A {}; } struct B { ns::A a; };",
}
TEST(Matcher, ThisPointerType) {
- StatementMatcher MethodOnY = call(thisPointerType(record(hasName("Y"))));
+ StatementMatcher MethodOnY =
+ memberCall(thisPointerType(record(hasName("Y"))));
EXPECT_TRUE(
matches("class Y { public: void x(); }; void z() { Y y; y.x(); }",
StatementMatcher Reference =
declarationReference(to(
variable(hasInitializer(
- call(thisPointerType(record(hasName("Y"))))))));
+ memberCall(thisPointerType(record(hasName("Y"))))))));
EXPECT_TRUE(matches(
"class Y {"
TEST(Matcher, CalledVariable) {
StatementMatcher CallOnVariableY = expression(
- call(on(declarationReference(to(variable(hasName("y")))))));
+ memberCall(on(declarationReference(to(variable(hasName("y")))))));
EXPECT_TRUE(matches(
"class Y { public: void x() { Y y; y.x(); } };", CallOnVariableY));
}
TEST(AstMatcherPMacro, Works) {
- DeclarationMatcher HasClassB = just(has(id("b", record(hasName("B")))));
+ DeclarationMatcher HasClassB = just(has(record(hasName("B")).bind("b")));
EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
}
TEST(AstPolymorphicMatcherPMacro, Works) {
- DeclarationMatcher HasClassB = polymorphicHas(id("b", record(hasName("B"))));
+ DeclarationMatcher HasClassB = polymorphicHas(record(hasName("B")).bind("b"));
EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
HasClassB, new VerifyIdIsBoundToDecl<Decl>("b")));
TEST(ForEach, BindsOneNode) {
EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; };",
- record(hasName("C"), forEach(id("x", field(hasName("x"))))),
+ record(hasName("C"), forEach(field(hasName("x")).bind("x"))),
new VerifyIdIsBoundToDecl<FieldDecl>("x", 1)));
}
TEST(ForEach, BindsMultipleNodes) {
EXPECT_TRUE(matchAndVerifyResultTrue("class C { int x; int y; int z; };",
- record(hasName("C"), forEach(id("f", field()))),
+ record(hasName("C"), forEach(field().bind("f"))),
new VerifyIdIsBoundToDecl<FieldDecl>("f", 3)));
}
TEST(ForEach, BindsRecursiveCombinations) {
EXPECT_TRUE(matchAndVerifyResultTrue(
"class C { class D { int x; int y; }; class E { int y; int z; }; };",
- record(hasName("C"), forEach(record(forEach(id("f", field()))))),
+ record(hasName("C"), forEach(record(forEach(field().bind("f"))))),
new VerifyIdIsBoundToDecl<FieldDecl>("f", 4)));
}
TEST(ForEachDescendant, BindsOneNode) {
EXPECT_TRUE(matchAndVerifyResultTrue("class C { class D { int x; }; };",
- record(hasName("C"), forEachDescendant(id("x", field(hasName("x"))))),
+ record(hasName("C"), forEachDescendant(field(hasName("x")).bind("x"))),
new VerifyIdIsBoundToDecl<FieldDecl>("x", 1)));
}
EXPECT_TRUE(matchAndVerifyResultTrue(
"class C { class D { int x; int y; }; "
" class E { class F { int y; int z; }; }; };",
- record(hasName("C"), forEachDescendant(id("f", field()))),
+ record(hasName("C"), forEachDescendant(field().bind("f"))),
new VerifyIdIsBoundToDecl<FieldDecl>("f", 4)));
}
"class C { class D { "
" class E { class F { class G { int y; int z; }; }; }; }; };",
record(hasName("C"), forEachDescendant(record(
- forEachDescendant(id("f", field()))))),
+ forEachDescendant(field().bind("f"))))),
new VerifyIdIsBoundToDecl<FieldDecl>("f", 8)));
}