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.
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 
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:
51*0fca6ea1SDimitry Andric   DerefFuncDeleteExprVisitor(const TemplateArgumentList &ArgList,
52*0fca6ea1SDimitry Andric                              const CXXRecordDecl *ClassDecl)
53*0fca6ea1SDimitry Andric       : ArgList(&ArgList), ClassDecl(ClassDecl) {}
54*0fca6ea1SDimitry Andric 
55*0fca6ea1SDimitry Andric   DerefFuncDeleteExprVisitor(const CXXRecordDecl *ClassDecl)
56*0fca6ea1SDimitry Andric       : ClassDecl(ClassDecl) {}
57*0fca6ea1SDimitry Andric 
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 
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 
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 
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.
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:
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 
1365ffd83dbSDimitry 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 
2055ffd83dbSDimitry 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 
2875ffd83dbSDimitry 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 
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>
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 
3555ffd83dbSDimitry 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 
3835ffd83dbSDimitry Andric void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {
3845ffd83dbSDimitry Andric   Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();
3855ffd83dbSDimitry Andric }
3865ffd83dbSDimitry Andric 
3875ffd83dbSDimitry Andric bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
3885ffd83dbSDimitry Andric     const CheckerManager &mgr) {
3895ffd83dbSDimitry Andric   return true;
3905ffd83dbSDimitry Andric }
391