New ASTMatchers and enhancement to hasOverloadedOperatorName

Added two new narrowing matchers:
* hasMethod: aplies a matcher to a CXXRecordDecl's methods until a match is made
  or there are no more methods.
* hasCanonicalType: applies a matcher to a QualType's canonicalType.

Enhanced hasOverloadedOperatorName to work on CXXMethodDecl as well as
CXXOperatorCallExpr.

Updated tests and docs.

Reviewers: klimek, gribozavr
llvm-svn: 176556
This commit is contained in:
Edwin Vane 2013-03-06 17:02:57 +00:00
parent 063dfe3244
commit 0a4836ed0e
4 changed files with 176 additions and 16 deletions

View File

@ -1393,17 +1393,43 @@ constructorDecl(hasAnyConstructorInitializer(isWritten()))
</pre></td></tr>
<tr><td>Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>&gt;</td><td class="name" onclick="toggle('hasOverloadedOperatorName0')"><a name="hasOverloadedOperatorName0Anchor">hasOverloadedOperatorName</a></td><td>std::string Name</td></tr>
<tr><td>Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>&gt;</td><td class="name" onclick="toggle('hasOverloadedOperatorName0')"><a name="hasOverloadedOperatorName0Anchor">hasOverloadedOperatorName</a></td><td>StringRef Name</td></tr>
<tr><td colspan="4" class="doc" id="hasOverloadedOperatorName0"><pre>Matches overloaded operator names.
Matches overloaded operator names specified in strings without the
"operator" prefix, such as "&lt;&lt;", for OverloadedOperatorCall's.
"operator" prefix: e.g. "&lt;&lt;".
Example matches a &lt;&lt; b
(matcher == operatorCallExpr(hasOverloadedOperatorName("&lt;&lt;")))
a &lt;&lt; b;
c &amp;&amp; d; assuming both operator&lt;&lt;
and operator&amp;&amp; are overloaded somewhere.
Given:
class A { int operator*(); };
const A &amp;operator&lt;&lt;(const A &amp;a, const A &amp;b);
A a;
a &lt;&lt; a; &lt;-- This matches
operatorCallExpr(hasOverloadedOperatorName("&lt;&lt;"))) matches the specified
line and recordDecl(hasMethod(hasOverloadedOperatorName("*"))) matches
the declaration of A.
Usable as: Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>&gt;, Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>&gt;
</pre></td></tr>
<tr><td>Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>&gt;</td><td class="name" onclick="toggle('hasOverloadedOperatorName1')"><a name="hasOverloadedOperatorName1Anchor">hasOverloadedOperatorName</a></td><td>StringRef Name</td></tr>
<tr><td colspan="4" class="doc" id="hasOverloadedOperatorName1"><pre>Matches overloaded operator names.
Matches overloaded operator names specified in strings without the
"operator" prefix: e.g. "&lt;&lt;".
Given:
class A { int operator*(); };
const A &amp;operator&lt;&lt;(const A &amp;a, const A &amp;b);
A a;
a &lt;&lt; a; &lt;-- This matches
operatorCallExpr(hasOverloadedOperatorName("&lt;&lt;"))) matches the specified
line and recordDecl(hasMethod(hasOverloadedOperatorName("*"))) matches
the declaration of A.
Usable as: Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXOperatorCallExpr.html">CXXOperatorCallExpr</a>&gt;, Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>&gt;
</pre></td></tr>
@ -2230,6 +2256,18 @@ Example matches A() in the last line
</pre></td></tr>
<tr><td>Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt;</td><td class="name" onclick="toggle('hasMethod0')"><a name="hasMethod0Anchor">hasMethod</a></td><td>Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasMethod0"><pre>Matches the first method of a class or struct that satisfies InnerMatcher.
Given:
class A { void func(); };
class B { void member(); };
recordDecl(hasMethod(hasName("func"))) matches the declaration of A
but not B.
</pre></td></tr>
<tr><td>Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt;</td><td class="name" onclick="toggle('isDerivedFrom0')"><a name="isDerivedFrom0Anchor">isDerivedFrom</a></td><td>Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1NamedDecl.html">NamedDecl</a>&gt; Base</td></tr>
<tr><td colspan="4" class="doc" id="isDerivedFrom0"><pre>Matches C++ classes that are directly or indirectly derived from
a class matching Base.
@ -2909,6 +2947,19 @@ Usable as: Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1BlockP
</pre></td></tr>
<tr><td>Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>&gt;</td><td class="name" onclick="toggle('hasCanonicalType0')"><a name="hasCanonicalType0Anchor">hasCanonicalType</a></td><td>Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasCanonicalType0"><pre>Matches QualTypes whose canonical type matches InnerMatcher.
Given:
typedef int &amp;int_ref;
int a;
int_ref b = a;
varDecl(hasType(qualType(referenceType()))))) will not match the
declaration of b but varDecl(hasType(qualType(hasCanonicalType(referenceType())))))) does.
</pre></td></tr>
<tr><td>Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1QualType.html">QualType</a>&gt;</td><td class="name" onclick="toggle('hasDeclaration5')"><a name="hasDeclaration5Anchor">hasDeclaration</a></td><td>Matcher&lt<a href="http://clang.llvm.org/doxygen/classclang_1_1Decl.html">Decl</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="hasDeclaration5"><pre>Matches a type if the declaration of the type matches the given
matcher.

View File

@ -1384,18 +1384,26 @@ AST_MATCHER_P(NamedDecl, matchesName, std::string, RegExp) {
/// \brief Matches overloaded operator names.
///
/// Matches overloaded operator names specified in strings without the
/// "operator" prefix, such as "<<", for OverloadedOperatorCall's.
/// "operator" prefix: e.g. "<<".
///
/// Example matches a << b
/// (matcher == operatorCallExpr(hasOverloadedOperatorName("<<")))
/// Given:
/// \code
/// a << b;
/// c && d; // assuming both operator<<
/// // and operator&& are overloaded somewhere.
/// class A { int operator*(); };
/// const A &operator<<(const A &a, const A &b);
/// A a;
/// a << a; // <-- This matches
/// \endcode
AST_MATCHER_P(CXXOperatorCallExpr,
hasOverloadedOperatorName, std::string, Name) {
return getOperatorSpelling(Node.getOperator()) == Name;
///
/// \c operatorCallExpr(hasOverloadedOperatorName("<<"))) matches the specified
/// line and \c recordDecl(hasMethod(hasOverloadedOperatorName("*"))) matches
/// the declaration of \c A.
///
/// Usable as: Matcher<CXXOperatorCallExpr>, Matcher<CXXMethodDecl>
inline internal::PolymorphicMatcherWithParam1<
internal::HasOverloadedOperatorNameMatcher, StringRef>
hasOverloadedOperatorName(const StringRef Name) {
return internal::PolymorphicMatcherWithParam1<
internal::HasOverloadedOperatorNameMatcher, StringRef>(Name);
}
/// \brief Matches C++ classes that are directly or indirectly derived from
@ -1445,6 +1453,27 @@ inline internal::Matcher<CXXRecordDecl> isSameOrDerivedFrom(
return isSameOrDerivedFrom(hasName(BaseName));
}
/// \brief Matches the first method of a class or struct that satisfies \c
/// InnerMatcher.
///
/// Given:
/// \code
/// class A { void func(); };
/// class B { void member(); };
/// \code
///
/// \c recordDecl(hasMethod(hasName("func"))) matches the declaration of \c A
/// but not \c B.
AST_MATCHER_P(CXXRecordDecl, hasMethod, internal::Matcher<CXXMethodDecl>,
InnerMatcher) {
for (CXXRecordDecl::method_iterator I = Node.method_begin(),
E = Node.method_end();
I != E; ++I)
if (InnerMatcher.matches(**I, Finder, Builder))
return true;
return false;
}
/// \brief Matches AST nodes that have child AST nodes that match the
/// provided matcher.
///
@ -1786,6 +1815,23 @@ AST_MATCHER_P(QualType, references, internal::Matcher<QualType>,
InnerMatcher.matches(Node->getPointeeType(), Finder, Builder));
}
/// \brief Matches QualTypes whose canonical type matches InnerMatcher.
///
/// Given:
/// \code
/// typedef int &int_ref;
/// int a;
/// int_ref b = a;
/// \code
///
/// \c varDecl(hasType(qualType(referenceType()))))) will not match the
/// declaration of b but \c
/// varDecl(hasType(qualType(hasCanonicalType(referenceType())))))) does.
AST_MATCHER_P(QualType, hasCanonicalType, internal::Matcher<QualType>,
InnerMatcher) {
return InnerMatcher.matches(Node.getCanonicalType(), Finder, Builder);
}
/// \brief Overloaded to match the referenced type's declaration.
inline internal::Matcher<QualType> references(
const internal::Matcher<Decl> &InnerMatcher) {

View File

@ -370,6 +370,44 @@ template <typename T> struct has_getDecl {
static bool const value = sizeof(f<Derived>(0)) == 2;
};
/// \brief Matches overloaded operators with a specific name.
///
/// The type argument ArgT is not used by this matcher but is used by
/// PolymorphicMatcherWithParam1 and should be StringRef.
template <typename T, typename ArgT>
class HasOverloadedOperatorNameMatcher : public SingleNodeMatcherInterface<T> {
TOOLING_COMPILE_ASSERT((llvm::is_same<T, CXXOperatorCallExpr>::value ||
llvm::is_same<T, CXXMethodDecl>::value),
unsupported_class_for_matcher);
TOOLING_COMPILE_ASSERT((llvm::is_same<ArgT, StringRef>::value),
argument_type_must_be_StringRef);
public:
explicit HasOverloadedOperatorNameMatcher(const StringRef Name)
: SingleNodeMatcherInterface<T>(), Name(Name) {}
virtual bool matchesNode(const T &Node) const LLVM_OVERRIDE {
return matchesSpecialized(Node);
}
private:
/// \brief CXXOperatorCallExpr exist only for calls to overloaded operators
/// so this function returns true if the call is to an operator of the given
/// name.
bool matchesSpecialized(const CXXOperatorCallExpr &Node) const {
return getOperatorSpelling(Node.getOperator()) == Name;
}
/// \brief Returns true only if CXXMethodDecl represents an overloaded
/// operator and has the given operator name.
bool matchesSpecialized(const CXXMethodDecl &Node) const {
return Node.isOverloadedOperator() &&
getOperatorSpelling(Node.getOverloadedOperator()) == Name;
}
std::string Name;
};
/// \brief Matches declarations for QualType and CallExpr.
///
/// Type argument DeclMatcherT is required by PolymorphicMatcherWithParam1 but

View File

@ -313,6 +313,13 @@ TEST(DeclarationMatcher, ClassIsDerived) {
recordDecl(isDerivedFrom(recordDecl(hasName("X")).bind("test")))));
}
TEST(DeclarationMatcher, hasMethod) {
EXPECT_TRUE(matches("class A { void func(); };",
recordDecl(hasMethod(hasName("func")))));
EXPECT_TRUE(notMatches("class A { void func(); };",
recordDecl(hasMethod(isPublic()))));
}
TEST(DeclarationMatcher, ClassDerivedFromDependentTemplateSpecialization) {
EXPECT_TRUE(matches(
"template <typename T> struct A {"
@ -1022,6 +1029,12 @@ TEST(Matcher, HasOperatorNameForOverloadedOperatorCall) {
"bool operator&&(Y x, Y y) { return true; }; "
"Y a; Y b; bool c = a && b;",
OpCallLessLess));
DeclarationMatcher ClassWithOpStar =
recordDecl(hasMethod(hasOverloadedOperatorName("*")));
EXPECT_TRUE(matches("class Y { int operator*(); };",
ClassWithOpStar));
EXPECT_TRUE(notMatches("class Y { void myOperator(); };",
ClassWithOpStar)) ;
}
TEST(Matcher, NestedOverloadedOperatorCalls) {
@ -1329,6 +1342,18 @@ TEST(Matcher, References) {
notMatches("class X {}; void y(X *y) { X *&x = y; }", ReferenceClassX));
}
TEST(QualType, hasCanonicalType) {
EXPECT_TRUE(notMatches("typedef int &int_ref;"
"int a;"
"int_ref b = a;",
varDecl(hasType(qualType(referenceType())))));
EXPECT_TRUE(
matches("typedef int &int_ref;"
"int a;"
"int_ref b = a;",
varDecl(hasType(qualType(hasCanonicalType(referenceType()))))));
}
TEST(HasParameter, CallsInnerMatcher) {
EXPECT_TRUE(matches("class X { void x(int) {} };",
methodDecl(hasParameter(0, varDecl()))));