Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -821,6 +821,7 @@ overridden_methods_end(const CXXMethodDecl *Method) const; unsigned overridden_methods_size(const CXXMethodDecl *Method) const; + const CXXMethodVector *overridden_methods(const CXXMethodDecl *Method) const; /// \brief Note that the given C++ \p Method overrides the given \p /// Overridden method. Index: include/clang/AST/DeclCXX.h =================================================================== --- include/clang/AST/DeclCXX.h +++ include/clang/AST/DeclCXX.h @@ -23,6 +23,7 @@ #include "clang/AST/LambdaCapture.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/TinyPtrVector.h" #include "llvm/Support/Compiler.h" namespace clang { @@ -1827,6 +1828,7 @@ method_iterator begin_overridden_methods() const; method_iterator end_overridden_methods() const; unsigned size_overridden_methods() const; + const llvm::TinyPtrVector *overridden_methods() const; /// Returns the parent of this method declaration, which /// is the class in which this method is defined. Index: include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- include/clang/ASTMatchers/ASTMatchers.h +++ include/clang/ASTMatchers/ASTMatchers.h @@ -3693,6 +3693,50 @@ 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, InnerMatcher) { + if (Node.overridden_methods() == nullptr) { + return false; + } + 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 Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -1259,32 +1259,30 @@ ASTContext::overridden_cxx_method_iterator ASTContext::overridden_methods_begin(const CXXMethodDecl *Method) const { - llvm::DenseMap::const_iterator Pos - = OverriddenMethods.find(Method->getCanonicalDecl()); - if (Pos == OverriddenMethods.end()) - return nullptr; - - return Pos->second.begin(); + const auto* Overridden = overridden_methods(Method); + return Overridden == nullptr ? nullptr : Overridden->begin(); } ASTContext::overridden_cxx_method_iterator ASTContext::overridden_methods_end(const CXXMethodDecl *Method) const { - llvm::DenseMap::const_iterator Pos - = OverriddenMethods.find(Method->getCanonicalDecl()); - if (Pos == OverriddenMethods.end()) - return nullptr; - - return Pos->second.end(); + const auto* Overridden = overridden_methods(Method); + return Overridden == nullptr ? nullptr : Overridden->end(); } unsigned ASTContext::overridden_methods_size(const CXXMethodDecl *Method) const { + const auto* Overridden = overridden_methods(Method); + return Overridden == nullptr ? 0 : Overridden->size(); +} + +const ASTContext::CXXMethodVector * +ASTContext::overridden_methods(const CXXMethodDecl *Method) const { llvm::DenseMap::const_iterator Pos = OverriddenMethods.find(Method->getCanonicalDecl()); if (Pos == OverriddenMethods.end()) - return 0; + return nullptr; - return Pos->second.size(); + return &Pos->second; } void ASTContext::addOverriddenMethod(const CXXMethodDecl *Method, Index: lib/AST/DeclCXX.cpp =================================================================== --- lib/AST/DeclCXX.cpp +++ lib/AST/DeclCXX.cpp @@ -1622,6 +1622,12 @@ return getASTContext().overridden_methods_size(this); } +const llvm::TinyPtrVector * +CXXMethodDecl::overridden_methods() const { + if (isa(this)) return 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*, Index: lib/ASTMatchers/Dynamic/Registry.cpp =================================================================== --- lib/ASTMatchers/Dynamic/Registry.cpp +++ lib/ASTMatchers/Dynamic/Registry.cpp @@ -182,6 +182,7 @@ REGISTER_MATCHER(forEachArgumentWithParam); REGISTER_MATCHER(forEachConstructorInitializer); REGISTER_MATCHER(forEachDescendant); + REGISTER_MATCHER(forEachOverridden); REGISTER_MATCHER(forEachSwitchCase); REGISTER_MATCHER(forField); REGISTER_MATCHER(forStmt); Index: unittests/ASTMatchers/ASTMatchersTest.cpp =================================================================== --- unittests/ASTMatchers/ASTMatchersTest.cpp +++ unittests/ASTMatchers/ASTMatchersTest.cpp @@ -2085,6 +2085,50 @@ notMatches("class X { virtual void f(); };", cxxMethodDecl(isFinal()))); } +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>("override", "f", 1))); + EXPECT_TRUE(matchAndVerifyResultTrue( + Code1, ForEachOverriddenInClass("C"), + llvm::make_unique>("overridden", "f", + 1))); + // B::f overrides A::f. + EXPECT_TRUE(matchAndVerifyResultTrue( + Code1, ForEachOverriddenInClass("B"), + llvm::make_unique>("override", "f", 1))); + EXPECT_TRUE(matchAndVerifyResultTrue( + Code1, ForEachOverriddenInClass("B"), + llvm::make_unique>("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>("override", "f", 2))); + EXPECT_TRUE(matchAndVerifyResultTrue( + Code2, ForEachOverriddenInClass("B"), + llvm::make_unique>("overridden", "f", + 2))); + // A1::f overrides nothing. + EXPECT_TRUE(notMatches(Code2, ForEachOverriddenInClass("A1"))); +} + TEST(Matcher, MatchesVirtualMethod) { EXPECT_TRUE(matches("class X { virtual int f(); };", cxxMethodDecl(isVirtual(), hasName("::X::f"))));