15ffd83dbSDimitry Andric //=======- RefCntblBaseVirtualDtor.cpp ---------------------------*- C++ -*-==//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric
9*0fca6ea1SDimitry Andric #include "ASTUtils.h"
105ffd83dbSDimitry Andric #include "DiagOutputUtils.h"
115ffd83dbSDimitry Andric #include "PtrTypesSemantics.h"
125ffd83dbSDimitry Andric #include "clang/AST/CXXInheritance.h"
135ffd83dbSDimitry Andric #include "clang/AST/RecursiveASTVisitor.h"
14*0fca6ea1SDimitry Andric #include "clang/AST/StmtVisitor.h"
155ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
165ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
175ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
185ffd83dbSDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
19*0fca6ea1SDimitry Andric #include "llvm/ADT/DenseSet.h"
20*0fca6ea1SDimitry Andric #include "llvm/ADT/SetVector.h"
21bdd1243dSDimitry Andric #include <optional>
225ffd83dbSDimitry Andric
235ffd83dbSDimitry Andric using namespace clang;
245ffd83dbSDimitry Andric using namespace ento;
255ffd83dbSDimitry Andric
265ffd83dbSDimitry Andric namespace {
27*0fca6ea1SDimitry Andric
28*0fca6ea1SDimitry Andric class DerefFuncDeleteExprVisitor
29*0fca6ea1SDimitry Andric : public ConstStmtVisitor<DerefFuncDeleteExprVisitor, bool> {
30*0fca6ea1SDimitry Andric // Returns true if any of child statements return true.
VisitChildren(const Stmt * S)31*0fca6ea1SDimitry Andric bool VisitChildren(const Stmt *S) {
32*0fca6ea1SDimitry Andric for (const Stmt *Child : S->children()) {
33*0fca6ea1SDimitry Andric if (Child && Visit(Child))
34*0fca6ea1SDimitry Andric return true;
35*0fca6ea1SDimitry Andric }
36*0fca6ea1SDimitry Andric return false;
37*0fca6ea1SDimitry Andric }
38*0fca6ea1SDimitry Andric
VisitBody(const Stmt * Body)39*0fca6ea1SDimitry Andric bool VisitBody(const Stmt *Body) {
40*0fca6ea1SDimitry Andric if (!Body)
41*0fca6ea1SDimitry Andric return false;
42*0fca6ea1SDimitry Andric
43*0fca6ea1SDimitry Andric auto [It, IsNew] = VisitedBody.insert(Body);
44*0fca6ea1SDimitry Andric if (!IsNew) // This body is recursive
45*0fca6ea1SDimitry Andric return false;
46*0fca6ea1SDimitry Andric
47*0fca6ea1SDimitry Andric return Visit(Body);
48*0fca6ea1SDimitry Andric }
49*0fca6ea1SDimitry Andric
50*0fca6ea1SDimitry Andric public:
DerefFuncDeleteExprVisitor(const TemplateArgumentList & ArgList,const CXXRecordDecl * ClassDecl)51*0fca6ea1SDimitry Andric DerefFuncDeleteExprVisitor(const TemplateArgumentList &ArgList,
52*0fca6ea1SDimitry Andric const CXXRecordDecl *ClassDecl)
53*0fca6ea1SDimitry Andric : ArgList(&ArgList), ClassDecl(ClassDecl) {}
54*0fca6ea1SDimitry Andric
DerefFuncDeleteExprVisitor(const CXXRecordDecl * ClassDecl)55*0fca6ea1SDimitry Andric DerefFuncDeleteExprVisitor(const CXXRecordDecl *ClassDecl)
56*0fca6ea1SDimitry Andric : ClassDecl(ClassDecl) {}
57*0fca6ea1SDimitry Andric
HasSpecializedDelete(CXXMethodDecl * Decl)58*0fca6ea1SDimitry Andric std::optional<bool> HasSpecializedDelete(CXXMethodDecl *Decl) {
59*0fca6ea1SDimitry Andric if (auto *Body = Decl->getBody())
60*0fca6ea1SDimitry Andric return VisitBody(Body);
61*0fca6ea1SDimitry Andric if (Decl->getTemplateInstantiationPattern())
62*0fca6ea1SDimitry Andric return std::nullopt; // Indeterminate. There was no concrete instance.
63*0fca6ea1SDimitry Andric return false;
64*0fca6ea1SDimitry Andric }
65*0fca6ea1SDimitry Andric
VisitCallExpr(const CallExpr * CE)66*0fca6ea1SDimitry Andric bool VisitCallExpr(const CallExpr *CE) {
67*0fca6ea1SDimitry Andric const Decl *D = CE->getCalleeDecl();
68*0fca6ea1SDimitry Andric if (D && D->hasBody())
69*0fca6ea1SDimitry Andric return VisitBody(D->getBody());
70*0fca6ea1SDimitry Andric return false;
71*0fca6ea1SDimitry Andric }
72*0fca6ea1SDimitry Andric
VisitCXXDeleteExpr(const CXXDeleteExpr * E)73*0fca6ea1SDimitry Andric bool VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
74*0fca6ea1SDimitry Andric auto *Arg = E->getArgument();
75*0fca6ea1SDimitry Andric while (Arg) {
76*0fca6ea1SDimitry Andric if (auto *Paren = dyn_cast<ParenExpr>(Arg))
77*0fca6ea1SDimitry Andric Arg = Paren->getSubExpr();
78*0fca6ea1SDimitry Andric else if (auto *Cast = dyn_cast<CastExpr>(Arg)) {
79*0fca6ea1SDimitry Andric Arg = Cast->getSubExpr();
80*0fca6ea1SDimitry Andric auto CastType = Cast->getType();
81*0fca6ea1SDimitry Andric if (auto *PtrType = dyn_cast<PointerType>(CastType)) {
82*0fca6ea1SDimitry Andric auto PointeeType = PtrType->getPointeeType();
83*0fca6ea1SDimitry Andric while (auto *ET = dyn_cast<ElaboratedType>(PointeeType)) {
84*0fca6ea1SDimitry Andric if (ET->isSugared())
85*0fca6ea1SDimitry Andric PointeeType = ET->desugar();
86*0fca6ea1SDimitry Andric }
87*0fca6ea1SDimitry Andric if (auto *ParmType = dyn_cast<TemplateTypeParmType>(PointeeType)) {
88*0fca6ea1SDimitry Andric if (ArgList) {
89*0fca6ea1SDimitry Andric auto ParmIndex = ParmType->getIndex();
90*0fca6ea1SDimitry Andric auto Type = ArgList->get(ParmIndex).getAsType();
91*0fca6ea1SDimitry Andric if (Type->getAsCXXRecordDecl() == ClassDecl)
92*0fca6ea1SDimitry Andric return true;
93*0fca6ea1SDimitry Andric }
94*0fca6ea1SDimitry Andric } else if (auto *RD = dyn_cast<RecordType>(PointeeType)) {
95*0fca6ea1SDimitry Andric if (RD->getDecl() == ClassDecl)
96*0fca6ea1SDimitry Andric return true;
97*0fca6ea1SDimitry Andric } else if (auto *ST =
98*0fca6ea1SDimitry Andric dyn_cast<SubstTemplateTypeParmType>(PointeeType)) {
99*0fca6ea1SDimitry Andric auto Type = ST->getReplacementType();
100*0fca6ea1SDimitry Andric if (auto *RD = dyn_cast<RecordType>(Type)) {
101*0fca6ea1SDimitry Andric if (RD->getDecl() == ClassDecl)
102*0fca6ea1SDimitry Andric return true;
103*0fca6ea1SDimitry Andric }
104*0fca6ea1SDimitry Andric }
105*0fca6ea1SDimitry Andric }
106*0fca6ea1SDimitry Andric } else
107*0fca6ea1SDimitry Andric break;
108*0fca6ea1SDimitry Andric }
109*0fca6ea1SDimitry Andric return false;
110*0fca6ea1SDimitry Andric }
111*0fca6ea1SDimitry Andric
VisitStmt(const Stmt * S)112*0fca6ea1SDimitry Andric bool VisitStmt(const Stmt *S) { return VisitChildren(S); }
113*0fca6ea1SDimitry Andric
114*0fca6ea1SDimitry Andric // Return false since the contents of lambda isn't necessarily executed.
115*0fca6ea1SDimitry Andric // If it is executed, VisitCallExpr above will visit its body.
VisitLambdaExpr(const LambdaExpr *)116*0fca6ea1SDimitry Andric bool VisitLambdaExpr(const LambdaExpr *) { return false; }
117*0fca6ea1SDimitry Andric
118*0fca6ea1SDimitry Andric private:
119*0fca6ea1SDimitry Andric const TemplateArgumentList *ArgList{nullptr};
120*0fca6ea1SDimitry Andric const CXXRecordDecl *ClassDecl;
121*0fca6ea1SDimitry Andric llvm::DenseSet<const Stmt *> VisitedBody;
122*0fca6ea1SDimitry Andric };
123*0fca6ea1SDimitry Andric
1245ffd83dbSDimitry Andric class RefCntblBaseVirtualDtorChecker
1255ffd83dbSDimitry Andric : public Checker<check::ASTDecl<TranslationUnitDecl>> {
1265ffd83dbSDimitry Andric private:
1275ffd83dbSDimitry Andric BugType Bug;
1285ffd83dbSDimitry Andric mutable BugReporter *BR;
1295ffd83dbSDimitry Andric
1305ffd83dbSDimitry Andric public:
RefCntblBaseVirtualDtorChecker()1315ffd83dbSDimitry Andric RefCntblBaseVirtualDtorChecker()
1325ffd83dbSDimitry Andric : Bug(this,
1335ffd83dbSDimitry Andric "Reference-countable base class doesn't have virtual destructor",
1345ffd83dbSDimitry Andric "WebKit coding guidelines") {}
1355ffd83dbSDimitry Andric
checkASTDecl(const TranslationUnitDecl * TUD,AnalysisManager & MGR,BugReporter & BRArg) const1365ffd83dbSDimitry Andric void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
1375ffd83dbSDimitry Andric BugReporter &BRArg) const {
1385ffd83dbSDimitry Andric BR = &BRArg;
1395ffd83dbSDimitry Andric
1405ffd83dbSDimitry Andric // The calls to checkAST* from AnalysisConsumer don't
1415ffd83dbSDimitry Andric // visit template instantiations or lambda classes. We
1425ffd83dbSDimitry Andric // want to visit those, so we make our own RecursiveASTVisitor.
1435ffd83dbSDimitry Andric struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
1445ffd83dbSDimitry Andric const RefCntblBaseVirtualDtorChecker *Checker;
1455ffd83dbSDimitry Andric explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker)
1465ffd83dbSDimitry Andric : Checker(Checker) {
1475ffd83dbSDimitry Andric assert(Checker);
1485ffd83dbSDimitry Andric }
1495ffd83dbSDimitry Andric
1505ffd83dbSDimitry Andric bool shouldVisitTemplateInstantiations() const { return true; }
1515ffd83dbSDimitry Andric bool shouldVisitImplicitCode() const { return false; }
1525ffd83dbSDimitry Andric
1535ffd83dbSDimitry Andric bool VisitCXXRecordDecl(const CXXRecordDecl *RD) {
154*0fca6ea1SDimitry Andric if (!RD->hasDefinition())
155*0fca6ea1SDimitry Andric return true;
156*0fca6ea1SDimitry Andric
157*0fca6ea1SDimitry Andric Decls.insert(RD);
158*0fca6ea1SDimitry Andric
159*0fca6ea1SDimitry Andric for (auto &Base : RD->bases()) {
160*0fca6ea1SDimitry Andric const auto AccSpec = Base.getAccessSpecifier();
161*0fca6ea1SDimitry Andric if (AccSpec == AS_protected || AccSpec == AS_private ||
162*0fca6ea1SDimitry Andric (AccSpec == AS_none && RD->isClass()))
163*0fca6ea1SDimitry Andric continue;
164*0fca6ea1SDimitry Andric
165*0fca6ea1SDimitry Andric QualType T = Base.getType();
166*0fca6ea1SDimitry Andric if (T.isNull())
167*0fca6ea1SDimitry Andric continue;
168*0fca6ea1SDimitry Andric
169*0fca6ea1SDimitry Andric const CXXRecordDecl *C = T->getAsCXXRecordDecl();
170*0fca6ea1SDimitry Andric if (!C)
171*0fca6ea1SDimitry Andric continue;
172*0fca6ea1SDimitry Andric
173*0fca6ea1SDimitry Andric if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(C)) {
174*0fca6ea1SDimitry Andric for (auto &Arg : CTSD->getTemplateArgs().asArray()) {
175*0fca6ea1SDimitry Andric if (Arg.getKind() != TemplateArgument::Type)
176*0fca6ea1SDimitry Andric continue;
177*0fca6ea1SDimitry Andric auto TemplT = Arg.getAsType();
178*0fca6ea1SDimitry Andric if (TemplT.isNull())
179*0fca6ea1SDimitry Andric continue;
180*0fca6ea1SDimitry Andric
181*0fca6ea1SDimitry Andric bool IsCRTP = TemplT->getAsCXXRecordDecl() == RD;
182*0fca6ea1SDimitry Andric if (!IsCRTP)
183*0fca6ea1SDimitry Andric continue;
184*0fca6ea1SDimitry Andric CRTPs.insert(C);
185*0fca6ea1SDimitry Andric }
186*0fca6ea1SDimitry Andric }
187*0fca6ea1SDimitry Andric }
188*0fca6ea1SDimitry Andric
1895ffd83dbSDimitry Andric return true;
1905ffd83dbSDimitry Andric }
191*0fca6ea1SDimitry Andric
192*0fca6ea1SDimitry Andric llvm::SetVector<const CXXRecordDecl *> Decls;
193*0fca6ea1SDimitry Andric llvm::DenseSet<const CXXRecordDecl *> CRTPs;
1945ffd83dbSDimitry Andric };
1955ffd83dbSDimitry Andric
1965ffd83dbSDimitry Andric LocalVisitor visitor(this);
1975ffd83dbSDimitry Andric visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
198*0fca6ea1SDimitry Andric for (auto *RD : visitor.Decls) {
199*0fca6ea1SDimitry Andric if (visitor.CRTPs.contains(RD))
200*0fca6ea1SDimitry Andric continue;
201*0fca6ea1SDimitry Andric visitCXXRecordDecl(RD);
202*0fca6ea1SDimitry Andric }
2035ffd83dbSDimitry Andric }
2045ffd83dbSDimitry Andric
visitCXXRecordDecl(const CXXRecordDecl * RD) const2055ffd83dbSDimitry Andric void visitCXXRecordDecl(const CXXRecordDecl *RD) const {
2065ffd83dbSDimitry Andric if (shouldSkipDecl(RD))
2075ffd83dbSDimitry Andric return;
2085ffd83dbSDimitry Andric
209*0fca6ea1SDimitry Andric for (auto &Base : RD->bases()) {
210*0fca6ea1SDimitry Andric const auto AccSpec = Base.getAccessSpecifier();
2115ffd83dbSDimitry Andric if (AccSpec == AS_protected || AccSpec == AS_private ||
2125ffd83dbSDimitry Andric (AccSpec == AS_none && RD->isClass()))
213*0fca6ea1SDimitry Andric continue;
2145ffd83dbSDimitry Andric
215*0fca6ea1SDimitry Andric auto hasRefInBase = clang::hasPublicMethodInBase(&Base, "ref");
216*0fca6ea1SDimitry Andric auto hasDerefInBase = clang::hasPublicMethodInBase(&Base, "deref");
2175f757f3fSDimitry Andric
2185f757f3fSDimitry Andric bool hasRef = hasRefInBase && *hasRefInBase != nullptr;
2195f757f3fSDimitry Andric bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr;
2205f757f3fSDimitry Andric
221*0fca6ea1SDimitry Andric QualType T = Base.getType();
2225f757f3fSDimitry Andric if (T.isNull())
223*0fca6ea1SDimitry Andric continue;
2245ffd83dbSDimitry Andric
2255f757f3fSDimitry Andric const CXXRecordDecl *C = T->getAsCXXRecordDecl();
2265f757f3fSDimitry Andric if (!C)
227*0fca6ea1SDimitry Andric continue;
228*0fca6ea1SDimitry Andric
2295f757f3fSDimitry Andric bool AnyInconclusiveBase = false;
2305f757f3fSDimitry Andric const auto hasPublicRefInBase =
231*0fca6ea1SDimitry Andric [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
2325f757f3fSDimitry Andric auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
2335f757f3fSDimitry Andric if (!hasRefInBase) {
2345f757f3fSDimitry Andric AnyInconclusiveBase = true;
2355f757f3fSDimitry Andric return false;
2365f757f3fSDimitry Andric }
2375f757f3fSDimitry Andric return (*hasRefInBase) != nullptr;
2385f757f3fSDimitry Andric };
239*0fca6ea1SDimitry Andric const auto hasPublicDerefInBase =
240*0fca6ea1SDimitry Andric [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
2415f757f3fSDimitry Andric auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
2425f757f3fSDimitry Andric if (!hasDerefInBase) {
2435f757f3fSDimitry Andric AnyInconclusiveBase = true;
2445f757f3fSDimitry Andric return false;
2455f757f3fSDimitry Andric }
2465f757f3fSDimitry Andric return (*hasDerefInBase) != nullptr;
2475f757f3fSDimitry Andric };
2485f757f3fSDimitry Andric CXXBasePaths Paths;
2495f757f3fSDimitry Andric Paths.setOrigin(C);
2505f757f3fSDimitry Andric hasRef = hasRef || C->lookupInBases(hasPublicRefInBase, Paths,
2515f757f3fSDimitry Andric /*LookupInDependent =*/true);
2525f757f3fSDimitry Andric hasDeref = hasDeref || C->lookupInBases(hasPublicDerefInBase, Paths,
2535f757f3fSDimitry Andric /*LookupInDependent =*/true);
2545f757f3fSDimitry Andric if (AnyInconclusiveBase || !hasRef || !hasDeref)
255*0fca6ea1SDimitry Andric continue;
256*0fca6ea1SDimitry Andric
257*0fca6ea1SDimitry Andric auto HasSpecializedDelete = isClassWithSpecializedDelete(C, RD);
258*0fca6ea1SDimitry Andric if (!HasSpecializedDelete || *HasSpecializedDelete)
259*0fca6ea1SDimitry Andric continue;
260*0fca6ea1SDimitry Andric if (C->lookupInBases(
261*0fca6ea1SDimitry Andric [&](const CXXBaseSpecifier *Base, CXXBasePath &) {
262*0fca6ea1SDimitry Andric auto *T = Base->getType().getTypePtrOrNull();
263*0fca6ea1SDimitry Andric if (!T)
2645f757f3fSDimitry Andric return false;
265*0fca6ea1SDimitry Andric auto *R = T->getAsCXXRecordDecl();
266*0fca6ea1SDimitry Andric if (!R)
267*0fca6ea1SDimitry Andric return false;
268*0fca6ea1SDimitry Andric auto Result = isClassWithSpecializedDelete(R, RD);
269*0fca6ea1SDimitry Andric if (!Result)
270*0fca6ea1SDimitry Andric AnyInconclusiveBase = true;
271*0fca6ea1SDimitry Andric return Result && *Result;
272*0fca6ea1SDimitry Andric },
273*0fca6ea1SDimitry Andric Paths, /*LookupInDependent =*/true))
274*0fca6ea1SDimitry Andric continue;
275*0fca6ea1SDimitry Andric if (AnyInconclusiveBase)
276*0fca6ea1SDimitry Andric continue;
2775f757f3fSDimitry Andric
2785f757f3fSDimitry Andric const auto *Dtor = C->getDestructor();
2795ffd83dbSDimitry Andric if (!Dtor || !Dtor->isVirtual()) {
280*0fca6ea1SDimitry Andric auto *ProblematicBaseSpecifier = &Base;
281*0fca6ea1SDimitry Andric auto *ProblematicBaseClass = C;
2825ffd83dbSDimitry Andric reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
2835ffd83dbSDimitry Andric }
2845ffd83dbSDimitry Andric }
285*0fca6ea1SDimitry Andric }
2865ffd83dbSDimitry Andric
shouldSkipDecl(const CXXRecordDecl * RD) const2875ffd83dbSDimitry Andric bool shouldSkipDecl(const CXXRecordDecl *RD) const {
2885ffd83dbSDimitry Andric if (!RD->isThisDeclarationADefinition())
2895ffd83dbSDimitry Andric return true;
2905ffd83dbSDimitry Andric
2915ffd83dbSDimitry Andric if (RD->isImplicit())
2925ffd83dbSDimitry Andric return true;
2935ffd83dbSDimitry Andric
2945ffd83dbSDimitry Andric if (RD->isLambda())
2955ffd83dbSDimitry Andric return true;
2965ffd83dbSDimitry Andric
2975ffd83dbSDimitry Andric // If the construct doesn't have a source file, then it's not something
2985ffd83dbSDimitry Andric // we want to diagnose.
2995ffd83dbSDimitry Andric const auto RDLocation = RD->getLocation();
3005ffd83dbSDimitry Andric if (!RDLocation.isValid())
3015ffd83dbSDimitry Andric return true;
3025ffd83dbSDimitry Andric
3035ffd83dbSDimitry Andric const auto Kind = RD->getTagKind();
3045f757f3fSDimitry Andric if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
3055ffd83dbSDimitry Andric return true;
3065ffd83dbSDimitry Andric
3075ffd83dbSDimitry Andric // Ignore CXXRecords that come from system headers.
3085ffd83dbSDimitry Andric if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=
3095ffd83dbSDimitry Andric SrcMgr::C_User)
3105ffd83dbSDimitry Andric return true;
3115ffd83dbSDimitry Andric
3125ffd83dbSDimitry Andric return false;
3135ffd83dbSDimitry Andric }
3145ffd83dbSDimitry Andric
isRefCountedClass(const CXXRecordDecl * D)315*0fca6ea1SDimitry Andric static bool isRefCountedClass(const CXXRecordDecl *D) {
316*0fca6ea1SDimitry Andric if (!D->getTemplateInstantiationPattern())
317*0fca6ea1SDimitry Andric return false;
318*0fca6ea1SDimitry Andric auto *NsDecl = D->getParent();
319*0fca6ea1SDimitry Andric if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
320*0fca6ea1SDimitry Andric return false;
321*0fca6ea1SDimitry Andric auto NamespaceName = safeGetName(NsDecl);
322*0fca6ea1SDimitry Andric auto ClsNameStr = safeGetName(D);
323*0fca6ea1SDimitry Andric StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
324*0fca6ea1SDimitry Andric return NamespaceName == "WTF" &&
325*0fca6ea1SDimitry Andric (ClsName.ends_with("RefCounted") ||
326*0fca6ea1SDimitry Andric ClsName == "ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr");
327*0fca6ea1SDimitry Andric }
328*0fca6ea1SDimitry Andric
329*0fca6ea1SDimitry Andric static std::optional<bool>
isClassWithSpecializedDelete(const CXXRecordDecl * C,const CXXRecordDecl * DerivedClass)330*0fca6ea1SDimitry Andric isClassWithSpecializedDelete(const CXXRecordDecl *C,
331*0fca6ea1SDimitry Andric const CXXRecordDecl *DerivedClass) {
332*0fca6ea1SDimitry Andric if (auto *ClsTmplSpDecl = dyn_cast<ClassTemplateSpecializationDecl>(C)) {
333*0fca6ea1SDimitry Andric for (auto *MethodDecl : C->methods()) {
334*0fca6ea1SDimitry Andric if (safeGetName(MethodDecl) == "deref") {
335*0fca6ea1SDimitry Andric DerefFuncDeleteExprVisitor Visitor(ClsTmplSpDecl->getTemplateArgs(),
336*0fca6ea1SDimitry Andric DerivedClass);
337*0fca6ea1SDimitry Andric auto Result = Visitor.HasSpecializedDelete(MethodDecl);
338*0fca6ea1SDimitry Andric if (!Result || *Result)
339*0fca6ea1SDimitry Andric return Result;
340*0fca6ea1SDimitry Andric }
341*0fca6ea1SDimitry Andric }
342*0fca6ea1SDimitry Andric return false;
343*0fca6ea1SDimitry Andric }
344*0fca6ea1SDimitry Andric for (auto *MethodDecl : C->methods()) {
345*0fca6ea1SDimitry Andric if (safeGetName(MethodDecl) == "deref") {
346*0fca6ea1SDimitry Andric DerefFuncDeleteExprVisitor Visitor(DerivedClass);
347*0fca6ea1SDimitry Andric auto Result = Visitor.HasSpecializedDelete(MethodDecl);
348*0fca6ea1SDimitry Andric if (!Result || *Result)
349*0fca6ea1SDimitry Andric return Result;
350*0fca6ea1SDimitry Andric }
351*0fca6ea1SDimitry Andric }
352*0fca6ea1SDimitry Andric return false;
353*0fca6ea1SDimitry Andric }
354*0fca6ea1SDimitry Andric
reportBug(const CXXRecordDecl * DerivedClass,const CXXBaseSpecifier * BaseSpec,const CXXRecordDecl * ProblematicBaseClass) const3555ffd83dbSDimitry Andric void reportBug(const CXXRecordDecl *DerivedClass,
3565ffd83dbSDimitry Andric const CXXBaseSpecifier *BaseSpec,
3575ffd83dbSDimitry Andric const CXXRecordDecl *ProblematicBaseClass) const {
3585ffd83dbSDimitry Andric assert(DerivedClass);
3595ffd83dbSDimitry Andric assert(BaseSpec);
3605ffd83dbSDimitry Andric assert(ProblematicBaseClass);
3615ffd83dbSDimitry Andric
3625ffd83dbSDimitry Andric SmallString<100> Buf;
3635ffd83dbSDimitry Andric llvm::raw_svector_ostream Os(Buf);
3645ffd83dbSDimitry Andric
3655ffd83dbSDimitry Andric Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " ";
3665ffd83dbSDimitry Andric printQuotedQualifiedName(Os, ProblematicBaseClass);
3675ffd83dbSDimitry Andric
3685ffd83dbSDimitry Andric Os << " is used as a base of "
3695ffd83dbSDimitry Andric << (DerivedClass->isClass() ? "class" : "struct") << " ";
3705ffd83dbSDimitry Andric printQuotedQualifiedName(Os, DerivedClass);
3715ffd83dbSDimitry Andric
3725ffd83dbSDimitry Andric Os << " but doesn't have virtual destructor";
3735ffd83dbSDimitry Andric
3745ffd83dbSDimitry Andric PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(),
3755ffd83dbSDimitry Andric BR->getSourceManager());
3765ffd83dbSDimitry Andric auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
3775ffd83dbSDimitry Andric Report->addRange(BaseSpec->getSourceRange());
3785ffd83dbSDimitry Andric BR->emitReport(std::move(Report));
3795ffd83dbSDimitry Andric }
3805ffd83dbSDimitry Andric };
3815ffd83dbSDimitry Andric } // namespace
3825ffd83dbSDimitry Andric
registerRefCntblBaseVirtualDtorChecker(CheckerManager & Mgr)3835ffd83dbSDimitry Andric void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {
3845ffd83dbSDimitry Andric Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();
3855ffd83dbSDimitry Andric }
3865ffd83dbSDimitry Andric
shouldRegisterRefCntblBaseVirtualDtorChecker(const CheckerManager & mgr)3875ffd83dbSDimitry Andric bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
3885ffd83dbSDimitry Andric const CheckerManager &mgr) {
3895ffd83dbSDimitry Andric return true;
3905ffd83dbSDimitry Andric }
391