Index: include/clang/ASTMatchers/ASTMatchers.h =================================================================== --- include/clang/ASTMatchers/ASTMatchers.h +++ include/clang/ASTMatchers/ASTMatchers.h @@ -3693,6 +3693,48 @@ 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) { + BoundNodesTreeBuilder Result; + bool Matched = false; + for (auto It = Node.begin_overridden_methods(); + It != Node.end_overridden_methods(); ++It) { + BoundNodesTreeBuilder OverriddenBuilder(*Builder); + const bool OverriddenMatched = + InnerMatcher.matches(**It, 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/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 @@ -2069,6 +2069,47 @@ 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"), + new VerifyIdIsBoundTo("override", "f", 1))); + EXPECT_TRUE(matchAndVerifyResultTrue( + Code1, ForEachOverriddenInClass("C"), + new VerifyIdIsBoundTo("overridden", "f", 1))); + // B::f overrides A::f. + EXPECT_TRUE(matchAndVerifyResultTrue( + Code1, ForEachOverriddenInClass("B"), + new VerifyIdIsBoundTo("override", "f", 1))); + EXPECT_TRUE(matchAndVerifyResultTrue( + Code1, ForEachOverriddenInClass("B"), + new VerifyIdIsBoundTo("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"), + new VerifyIdIsBoundTo("override", "f", 2))); + EXPECT_TRUE(matchAndVerifyResultTrue( + Code2, ForEachOverriddenInClass("B"), + new VerifyIdIsBoundTo("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"))));