10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Tooling/FixIt.h"
13 #include "llvm/ADT/STLExtras.h"
14 #include "llvm/ADT/SmallVector.h"
24 using BasesVector = llvm::SmallVector<const CXXRecordDecl *, 5>;
27 const CXXRecordDecl &ThisClass) {
28 if (
Parent.getCanonicalDecl() == ThisClass.getCanonicalDecl())
30 const CXXRecordDecl *ParentCanonicalDecl =
Parent.getCanonicalDecl();
31 return ThisClass.bases_end() !=
32 llvm::find_if(ThisClass.bases(), [=](
const CXXBaseSpecifier &
Base) {
33 auto *BaseDecl =
Base.getType()->getAsCXXRecordDecl();
35 return ParentCanonicalDecl == BaseDecl->getCanonicalDecl();
40 const CXXRecordDecl &ThisClass,
41 const CXXMethodDecl &MemberDecl) {
43 for (
const auto &
Base : ThisClass.bases()) {
44 const auto *BaseDecl =
Base.getType()->getAsCXXRecordDecl();
45 const CXXMethodDecl *ActualMemberDecl =
46 MemberDecl.getCorrespondingMethodInClass(BaseDecl);
47 if (!ActualMemberDecl)
52 const Type *TypePtr = ActualMemberDecl->getThisType().getTypePtr();
53 const CXXRecordDecl *RecordDeclType = TypePtr->getPointeeCXXRecordDecl();
54 assert(RecordDeclType &&
"TypePtr is not a pointer to CXXRecordDecl!");
55 if (RecordDeclType->getCanonicalDecl()->isDerivedFrom(&GrandParent))
56 Result.emplace_back(RecordDeclType);
64 llvm::raw_string_ostream
OS(QualName);
65 PrintingPolicy
PP(
Decl->getASTContext().getPrintingPolicy());
66 PP.SuppressUnwrittenScope =
true;
74 clang::ASTContext &AC) {
75 std::string
Text = tooling::fixit::getText(
E, AC).str();
79 [](
char C) {
return llvm::isSpace(static_cast<unsigned char>(C)); }),
84 void ParentVirtualCallCheck::registerMatchers(MatchFinder *Finder) {
87 ast_type_traits::TK_AsIs,
89 callee(memberExpr(hasDescendant(implicitCastExpr(
90 hasImplicitDestinationType(pointsTo(
91 type(anything()).bind(
"castToType"))),
92 hasSourceExpression(cxxThisExpr(hasType(
93 type(anything()).bind(
"thisType")))))))
95 callee(cxxMethodDecl(isVirtual())))),
99 void ParentVirtualCallCheck::check(
const MatchFinder::MatchResult &Result) {
100 const auto *Member = Result.Nodes.getNodeAs<MemberExpr>(
"member");
103 if (!Member->getQualifier())
106 const auto *MemberDecl = cast<CXXMethodDecl>(Member->getMemberDecl());
108 const auto *ThisTypePtr = Result.Nodes.getNodeAs<PointerType>(
"thisType");
111 const auto *ThisType = ThisTypePtr->getPointeeCXXRecordDecl();
114 const auto *CastToTypePtr = Result.Nodes.getNodeAs<
Type>(
"castToType");
115 assert(CastToTypePtr);
117 const auto *CastToType = CastToTypePtr->getAsCXXRecordDecl();
129 std::string ParentsStr;
130 ParentsStr.reserve(30 * Parents.size());
131 for (
const CXXRecordDecl *
Parent : Parents) {
132 if (!ParentsStr.empty())
133 ParentsStr.append(
" or ");
137 assert(Member->getQualifierLoc().getSourceRange().getBegin().isValid());
138 auto Diag = diag(Member->getQualifierLoc().getSourceRange().getBegin(),
139 "qualified name '%0' refers to a member overridden "
140 "in subclass%1; did you mean %2?")
142 << (Parents.size() > 1 ?
"es" :
"") << ParentsStr;
145 if (Parents.size() == 1 &&
147 !isa<ClassTemplateSpecializationDecl>(Parents.front()))
148 Diag << FixItHint::CreateReplacement(
149 Member->getQualifierLoc().getSourceRange(),