[ASTMatchers] New forEachOverriden matcher.

Matches methods overridden by the given method.

llvm-svn: 274531
This commit is contained in:
Clement Courbet 2016-07-05 07:49:31 +00:00
parent 4a39add05e
commit 6ecaec83ba
7 changed files with 134 additions and 9 deletions

View File

@ -3994,6 +3994,30 @@ matcher, or is a pointer to a type that matches the InnerMatcher.
</pre></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('forEachOverridden0')"><a name="forEachOverridden0Anchor">forEachOverridden</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="forEachOverridden0"><pre>Matches each method overriden by the given method. This matcher may
produce multiple matches.
Given
class A { virtual void f(); };
class B : public A { void f(); };
class C : public B { void f(); };
cxxMethodDecl(ofClass(hasName("C")),
forEachOverridden(cxxMethodDecl().bind("b"))).bind("d")
matches once, with "b" binding "A::f" and "d" binding "C::f" (Note
that B::f is not overridden by C::f).
The check can produce multiple matches in case of multiple inheritance, e.g.
class A1 { virtual void f(); };
class A2 { virtual void f(); };
class C : public A1, public A2 { void f(); };
cxxMethodDecl(ofClass(hasName("C")),
forEachOverridden(cxxMethodDecl().bind("b"))).bind("d")
matches twice, once with "b" binding "A1::f" and "d" binding "C::f", and
once with "b" binding "A2::f" and "d" binding "C::f".
</pre></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('ofClass0')"><a name="ofClass0Anchor">ofClass</a></td><td>Matcher&lt;<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>&gt; InnerMatcher</td></tr>
<tr><td colspan="4" class="doc" id="ofClass0"><pre>Matches the class declaration that the given method declaration
belongs to.

View File

@ -825,6 +825,9 @@ public:
overridden_methods_end(const CXXMethodDecl *Method) const;
unsigned overridden_methods_size(const CXXMethodDecl *Method) const;
typedef llvm::iterator_range<overridden_cxx_method_iterator>
overridden_method_range;
overridden_method_range overridden_methods(const CXXMethodDecl *Method) const;
/// \brief Note that the given C++ \p Method overrides the given \p
/// Overridden method.

View File

@ -16,6 +16,7 @@
#ifndef LLVM_CLANG_AST_DECLCXX_H
#define LLVM_CLANG_AST_DECLCXX_H
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTUnresolvedSet.h"
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
@ -1829,6 +1830,8 @@ public:
method_iterator begin_overridden_methods() const;
method_iterator end_overridden_methods() const;
unsigned size_overridden_methods() const;
typedef ASTContext::overridden_method_range overridden_method_range;
overridden_method_range overridden_methods() const;
/// Returns the parent of this method declaration, which
/// is the class in which this method is defined.

View File

@ -3809,6 +3809,47 @@ AST_MATCHER_P(CXXMethodDecl, ofClass,
InnerMatcher.matches(*Parent, Finder, Builder));
}
/// \brief Matches each method overriden by the given method. This matcher may
/// produce multiple matches.
///
/// Given
/// \code
/// class A { virtual void f(); };
/// class B : public A { void f(); };
/// class C : public B { void f(); };
/// \endcode
/// cxxMethodDecl(ofClass(hasName("C")),
/// forEachOverridden(cxxMethodDecl().bind("b"))).bind("d")
/// matches once, with "b" binding "A::f" and "d" binding "C::f" (Note
/// that B::f is not overridden by C::f).
///
/// The check can produce multiple matches in case of multiple inheritance, e.g.
/// \code
/// class A1 { virtual void f(); };
/// class A2 { virtual void f(); };
/// class C : public A1, public A2 { void f(); };
/// \endcode
/// cxxMethodDecl(ofClass(hasName("C")),
/// forEachOverridden(cxxMethodDecl().bind("b"))).bind("d")
/// matches twice, once with "b" binding "A1::f" and "d" binding "C::f", and
/// once with "b" binding "A2::f" and "d" binding "C::f".
AST_MATCHER_P(CXXMethodDecl, forEachOverridden,
internal::Matcher<CXXMethodDecl>, InnerMatcher) {
BoundNodesTreeBuilder Result;
bool Matched = false;
for (const auto *Overridden : Node.overridden_methods()) {
BoundNodesTreeBuilder OverriddenBuilder(*Builder);
const bool OverriddenMatched =
InnerMatcher.matches(*Overridden, Finder, &OverriddenBuilder);
if (OverriddenMatched) {
Matched = true;
Result.addMatch(OverriddenBuilder);
}
}
*Builder = std::move(Result);
return Matched;
}
/// \brief Matches if the given method declaration is virtual.
///
/// Given

View File

@ -1262,34 +1262,37 @@ void ASTContext::setInstantiatedFromUnnamedFieldDecl(FieldDecl *Inst,
ASTContext::overridden_cxx_method_iterator
ASTContext::overridden_methods_begin(const CXXMethodDecl *Method) const {
llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector>::const_iterator Pos
= OverriddenMethods.find(Method->getCanonicalDecl());
llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector>::const_iterator Pos =
OverriddenMethods.find(Method->getCanonicalDecl());
if (Pos == OverriddenMethods.end())
return nullptr;
return Pos->second.begin();
}
ASTContext::overridden_cxx_method_iterator
ASTContext::overridden_methods_end(const CXXMethodDecl *Method) const {
llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector>::const_iterator Pos
= OverriddenMethods.find(Method->getCanonicalDecl());
llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector>::const_iterator Pos =
OverriddenMethods.find(Method->getCanonicalDecl());
if (Pos == OverriddenMethods.end())
return nullptr;
return Pos->second.end();
}
unsigned
ASTContext::overridden_methods_size(const CXXMethodDecl *Method) const {
llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector>::const_iterator Pos
= OverriddenMethods.find(Method->getCanonicalDecl());
llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector>::const_iterator Pos =
OverriddenMethods.find(Method->getCanonicalDecl());
if (Pos == OverriddenMethods.end())
return 0;
return Pos->second.size();
}
ASTContext::overridden_method_range
ASTContext::overridden_methods(const CXXMethodDecl *Method) const {
return overridden_method_range(overridden_methods_begin(Method),
overridden_methods_end(Method));
}
void ASTContext::addOverriddenMethod(const CXXMethodDecl *Method,
const CXXMethodDecl *Overridden) {
assert(Method->isCanonicalDecl() && Overridden->isCanonicalDecl());

View File

@ -1651,6 +1651,13 @@ unsigned CXXMethodDecl::size_overridden_methods() const {
return getASTContext().overridden_methods_size(this);
}
CXXMethodDecl::overridden_method_range
CXXMethodDecl::overridden_methods() const {
if (isa<CXXConstructorDecl>(this))
return overridden_method_range(nullptr, nullptr);
return getASTContext().overridden_methods(this);
}
QualType CXXMethodDecl::getThisType(ASTContext &C) const {
// C++ 9.3.2p1: The type of this in a member function of a class X is X*.
// If the member function is declared const, the type of this is const X*,

View File

@ -2007,5 +2007,49 @@ TEST(StatementMatcher, ForFunction) {
EXPECT_TRUE(notMatches(CppString2, returnStmt(forFunction(hasName("F")))));
}
TEST(Matcher, ForEachOverriden) {
const auto ForEachOverriddenInClass = [](const char *ClassName) {
return cxxMethodDecl(ofClass(hasName(ClassName)), isVirtual(),
forEachOverridden(cxxMethodDecl().bind("overridden")))
.bind("override");
};
static const char Code1[] = "class A { virtual void f(); };"
"class B : public A { void f(); };"
"class C : public B { void f(); };";
// C::f overrides A::f.
EXPECT_TRUE(matchAndVerifyResultTrue(
Code1, ForEachOverriddenInClass("C"),
llvm::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("override", "f", 1)));
EXPECT_TRUE(matchAndVerifyResultTrue(
Code1, ForEachOverriddenInClass("C"),
llvm::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("overridden", "f",
1)));
// B::f overrides A::f.
EXPECT_TRUE(matchAndVerifyResultTrue(
Code1, ForEachOverriddenInClass("B"),
llvm::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("override", "f", 1)));
EXPECT_TRUE(matchAndVerifyResultTrue(
Code1, ForEachOverriddenInClass("B"),
llvm::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("overridden", "f",
1)));
// A::f overrides nothing.
EXPECT_TRUE(notMatches(Code1, ForEachOverriddenInClass("A")));
static const char Code2[] =
"class A1 { virtual void f(); };"
"class A2 { virtual void f(); };"
"class B : public A1, public A2 { void f(); };";
// B::f overrides A1::f and A2::f. This produces two matches.
EXPECT_TRUE(matchAndVerifyResultTrue(
Code2, ForEachOverriddenInClass("B"),
llvm::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("override", "f", 2)));
EXPECT_TRUE(matchAndVerifyResultTrue(
Code2, ForEachOverriddenInClass("B"),
llvm::make_unique<VerifyIdIsBoundTo<CXXMethodDecl>>("overridden", "f",
2)));
// A1::f overrides nothing.
EXPECT_TRUE(notMatches(Code2, ForEachOverriddenInClass("A1")));
}
} // namespace ast_matchers
} // namespace clang