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"
22using BasesVector = llvm::SmallVector<const CXXRecordDecl *, 5>;
25 const CXXRecordDecl &ThisClass) {
26 if (
Parent.getCanonicalDecl() == ThisClass.getCanonicalDecl())
28 const CXXRecordDecl *ParentCanonicalDecl =
Parent.getCanonicalDecl();
29 return llvm::any_of(ThisClass.bases(), [=](
const CXXBaseSpecifier &Base) {
30 auto *BaseDecl = Base.getType()->getAsCXXRecordDecl();
32 return ParentCanonicalDecl == BaseDecl->getCanonicalDecl();
37 const CXXRecordDecl &ThisClass,
38 const CXXMethodDecl &MemberDecl) {
40 for (
const auto &Base : ThisClass.bases()) {
41 const auto *BaseDecl = Base.getType()->getAsCXXRecordDecl();
42 const CXXMethodDecl *ActualMemberDecl =
43 MemberDecl.getCorrespondingMethodInClass(BaseDecl);
44 if (!ActualMemberDecl)
49 const Type *TypePtr = ActualMemberDecl->getThisType().getTypePtr();
50 const CXXRecordDecl *RecordDeclType = TypePtr->getPointeeCXXRecordDecl();
51 assert(RecordDeclType &&
"TypePtr is not a pointer to CXXRecordDecl!");
52 if (RecordDeclType->getCanonicalDecl()->isDerivedFrom(&GrandParent))
53 Result.emplace_back(RecordDeclType);
61 llvm::raw_string_ostream
OS(QualName);
62 PrintingPolicy
PP(
Decl->getASTContext().getPrintingPolicy());
63 PP.SuppressUnwrittenScope =
true;
71 clang::ASTContext &AC) {
72 std::string
Text = tooling::fixit::getText(
E, AC).str();
73 llvm::erase_if(
Text, [](
char C) {
74 return llvm::isSpace(
static_cast<unsigned char>(
C));
84 callee(memberExpr(hasDescendant(implicitCastExpr(
85 hasImplicitDestinationType(pointsTo(
86 type(anything()).bind(
"castToType"))),
87 hasSourceExpression(cxxThisExpr(hasType(
88 type(anything()).bind(
"thisType")))))))
90 callee(cxxMethodDecl(isVirtual())))),
95 const auto *Member = Result.Nodes.getNodeAs<MemberExpr>(
"member");
98 if (!Member->getQualifier())
101 const auto *MemberDecl = cast<CXXMethodDecl>(Member->getMemberDecl());
103 const auto *ThisTypePtr = Result.Nodes.getNodeAs<PointerType>(
"thisType");
106 const auto *ThisType = ThisTypePtr->getPointeeCXXRecordDecl();
109 const auto *CastToTypePtr = Result.Nodes.getNodeAs<
Type>(
"castToType");
110 assert(CastToTypePtr);
112 const auto *CastToType = CastToTypePtr->getAsCXXRecordDecl();
124 std::string ParentsStr;
125 ParentsStr.reserve(30 * Parents.size());
126 for (
const CXXRecordDecl *
Parent : Parents) {
127 if (!ParentsStr.empty())
128 ParentsStr.append(
" or ");
132 assert(Member->getQualifierLoc().getSourceRange().getBegin().isValid());
133 auto Diag =
diag(Member->getQualifierLoc().getSourceRange().getBegin(),
134 "qualified name '%0' refers to a member overridden "
135 "in %plural{1:subclass|:subclasses}1; did you mean %2?")
137 <<
static_cast<unsigned>(Parents.size()) << ParentsStr;
140 if (Parents.size() == 1 &&
142 !isa<ClassTemplateSpecializationDecl>(Parents.front()))
143 Diag << FixItHint::CreateReplacement(
144 Member->getQualifierLoc().getSourceRange(),
const FunctionDecl * Decl
llvm::raw_string_ostream OS
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static std::string getNameAsString(const NamedDecl *Decl)
llvm::SmallVector< const CXXRecordDecl *, 5 > BasesVector
static bool isParentOf(const CXXRecordDecl &Parent, const CXXRecordDecl &ThisClass)
static std::string getExprAsString(const clang::Expr &E, clang::ASTContext &AC)
static BasesVector getParentsByGrandParent(const CXXRecordDecl &GrandParent, const CXXRecordDecl &ThisClass, const CXXMethodDecl &MemberDecl)