diff options
author | Clement Courbet <courbet@google.com> | 2016-06-10 11:54:43 +0000 |
---|---|---|
committer | Clement Courbet <courbet@google.com> | 2016-06-10 11:54:43 +0000 |
commit | 8251ebfac6559739a9238053a74a816dbdc54b21 (patch) | |
tree | 5226b4480583f132c446ddaf419195a6211f77ed | |
parent | b16907f17abaa49469a5a6538a938127f247d31f (diff) | |
download | bcm5719-llvm-8251ebfac6559739a9238053a74a816dbdc54b21.tar.gz bcm5719-llvm-8251ebfac6559739a9238053a74a816dbdc54b21.zip |
[ASTMatchers] New forEachOverriden matcher.
Matches methods overridden by the given method.
llvm-svn: 272386
-rw-r--r-- | clang/docs/LibASTMatchersReference.html | 24 | ||||
-rw-r--r-- | clang/include/clang/AST/ASTContext.h | 3 | ||||
-rw-r--r-- | clang/include/clang/AST/DeclCXX.h | 3 | ||||
-rw-r--r-- | clang/include/clang/ASTMatchers/ASTMatchers.h | 41 | ||||
-rw-r--r-- | clang/lib/AST/ASTContext.cpp | 21 | ||||
-rw-r--r-- | clang/lib/AST/DeclCXX.cpp | 7 | ||||
-rw-r--r-- | clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp | 44 |
7 files changed, 134 insertions, 9 deletions
diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html index 3606367d2fd..5ab35568907 100644 --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -3968,6 +3968,30 @@ matcher, or is a pointer to a type that matches the InnerMatcher. </pre></td></tr> +<tr><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>></td><td class="name" onclick="toggle('forEachOverridden0')"><a name="forEachOverridden0Anchor">forEachOverridden</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>> 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<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXMethodDecl.html">CXXMethodDecl</a>></td><td class="name" onclick="toggle('ofClass0')"><a name="ofClass0Anchor">ofClass</a></td><td>Matcher<<a href="http://clang.llvm.org/doxygen/classclang_1_1CXXRecordDecl.html">CXXRecordDecl</a>> InnerMatcher</td></tr> <tr><td colspan="4" class="doc" id="ofClass0"><pre>Matches the class declaration that the given method declaration belongs to. diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 47f89c63613..e3fc4643445 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -821,6 +821,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. diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 755542c04a1..e540f072e13 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -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" @@ -1828,6 +1829,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. diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index 712eef0d3ca..778b993bc83 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -3770,6 +3770,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 diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index fa46b24e57c..67024e7c391 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1254,34 +1254,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()); diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp index 2a1fac8509e..83c7db88f4d 100644 --- a/clang/lib/AST/DeclCXX.cpp +++ b/clang/lib/AST/DeclCXX.cpp @@ -1635,6 +1635,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*, diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp index cc5cf715a70..aa861f4e4da 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -1997,5 +1997,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"); + }; + constexpr 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"))); + + constexpr 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 |