19#include "llvm/ADT/DenseSet.h"
20#include "llvm/ADT/SetVector.h"
28class DerefFuncDeleteExprVisitor
31 bool VisitChildren(
const Stmt *S) {
32 for (
const Stmt *Child : S->
children()) {
33 if (Child && Visit(Child))
39 bool VisitBody(
const Stmt *Body) {
43 auto [It, IsNew] = VisitedBody.insert(Body);
51 DerefFuncDeleteExprVisitor(
const TemplateArgumentList &ArgList,
52 const CXXRecordDecl *ClassDecl)
53 : ArgList(&ArgList), ClassDecl(ClassDecl) {}
55 DerefFuncDeleteExprVisitor(
const CXXRecordDecl *ClassDecl)
56 : ClassDecl(ClassDecl) {}
58 std::optional<bool> HasSpecializedDelete(CXXMethodDecl *Decl) {
59 if (
auto *Body =
Decl->getBody())
60 return VisitBody(Body);
61 if (
Decl->getTemplateInstantiationPattern())
66 bool VisitCallExpr(
const CallExpr *CE) {
72 if (name ==
"ensureOnMainThread" || name ==
"ensureOnMainRunLoop") {
73 for (
unsigned i = 0; i < CE->
getNumArgs(); ++i) {
75 if (VisitLambdaArgument(Arg))
83 bool VisitLambdaArgument(
const Expr *E) {
85 if (
auto *TempE = dyn_cast<CXXBindTemporaryExpr>(E))
86 E = TempE->getSubExpr();
88 if (
auto *Ref = dyn_cast<DeclRefExpr>(E)) {
89 if (
auto *VD = dyn_cast_or_null<VarDecl>(Ref->getDecl()))
90 return VisitLambdaArgument(VD->getInit());
93 if (
auto *Lambda = dyn_cast<LambdaExpr>(E)) {
94 if (VisitBody(Lambda->getBody()))
97 if (
auto *ConstructE = dyn_cast<CXXConstructExpr>(E)) {
98 for (
unsigned i = 0; i < ConstructE->getNumArgs(); ++i) {
99 if (VisitLambdaArgument(ConstructE->getArg(i)))
106 bool VisitCXXDeleteExpr(
const CXXDeleteExpr *E) {
109 if (
auto *
Paren = dyn_cast<ParenExpr>(Arg))
110 Arg =
Paren->getSubExpr();
111 else if (
auto *Cast = dyn_cast<CastExpr>(Arg)) {
112 Arg =
Cast->getSubExpr();
114 if (
auto *PtrType = dyn_cast<PointerType>(
CastType)) {
115 auto PointeeType = PtrType->getPointeeType();
116 if (
auto *ParmType = dyn_cast<TemplateTypeParmType>(PointeeType)) {
118 auto ParmIndex = ParmType->getIndex();
119 auto Type = ArgList->get(ParmIndex).getAsType();
120 if (
Type->getAsCXXRecordDecl() == ClassDecl)
123 }
else if (
auto *RD = dyn_cast<RecordType>(PointeeType)) {
126 }
else if (
auto *ST =
127 dyn_cast<SubstTemplateTypeParmType>(PointeeType)) {
128 auto Type = ST->getReplacementType();
129 if (
auto *RD = dyn_cast<RecordType>(
Type)) {
141 bool VisitStmt(
const Stmt *S) {
return VisitChildren(S); }
145 bool VisitLambdaExpr(
const LambdaExpr *) {
return false; }
148 const TemplateArgumentList *ArgList{
nullptr};
149 const CXXRecordDecl *ClassDecl;
150 llvm::DenseSet<const Stmt *> VisitedBody;
153class RefCntblBaseVirtualDtorChecker
154 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
157 mutable BugReporter *BR;
160 RefCntblBaseVirtualDtorChecker()
162 "Reference-countable base class doesn't have virtual destructor",
163 "WebKit coding guidelines") {}
165 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
166 BugReporter &BRArg)
const {
173 const RefCntblBaseVirtualDtorChecker *Checker;
174 explicit LocalVisitor(
const RefCntblBaseVirtualDtorChecker *Checker)
177 ShouldVisitTemplateInstantiations =
true;
178 ShouldVisitImplicitCode =
false;
181 bool VisitCXXRecordDecl(CXXRecordDecl *RD)
override {
187 for (
auto &Base : RD->
bases()) {
188 const auto AccSpec =
Base.getAccessSpecifier();
193 QualType
T =
Base.getType();
201 bool isExempt =
T.getAsString() ==
"NoVirtualDestructorBase" &&
203 if (isExempt || ExemptDecls.contains(
C)) {
204 ExemptDecls.insert(RD);
208 if (
auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(
C)) {
209 for (
auto &Arg : CTSD->getTemplateArgs().asArray()) {
212 auto TemplT = Arg.getAsType();
216 bool IsCRTP = TemplT->getAsCXXRecordDecl() == RD;
227 llvm::SetVector<const CXXRecordDecl *> Decls;
228 llvm::DenseSet<const CXXRecordDecl *> CRTPs;
229 llvm::DenseSet<const CXXRecordDecl *> ExemptDecls;
232 LocalVisitor visitor(
this);
233 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
234 for (
auto *RD : visitor.Decls) {
235 if (visitor.CRTPs.contains(RD) || visitor.ExemptDecls.contains(RD))
237 visitCXXRecordDecl(RD);
241 void visitCXXRecordDecl(
const CXXRecordDecl *RD)
const {
242 if (shouldSkipDecl(RD))
245 for (
auto &Base : RD->
bases()) {
246 const auto AccSpec =
Base.getAccessSpecifier();
254 bool hasRef = hasRefInBase && *hasRefInBase !=
nullptr;
255 bool hasDeref = hasDerefInBase && *hasDerefInBase !=
nullptr;
257 QualType
T =
Base.getType();
265 bool AnyInconclusiveBase =
false;
266 const auto hasPublicRefInBase =
267 [&AnyInconclusiveBase](
const CXXBaseSpecifier *
Base, CXXBasePath &) {
270 AnyInconclusiveBase =
true;
273 return (*hasRefInBase) !=
nullptr;
275 const auto hasPublicDerefInBase =
276 [&AnyInconclusiveBase](
const CXXBaseSpecifier *
Base, CXXBasePath &) {
278 if (!hasDerefInBase) {
279 AnyInconclusiveBase =
true;
282 return (*hasDerefInBase) !=
nullptr;
286 hasRef = hasRef ||
C->lookupInBases(hasPublicRefInBase, Paths,
288 hasDeref = hasDeref ||
C->lookupInBases(hasPublicDerefInBase, Paths,
290 if (AnyInconclusiveBase || !hasRef || !hasDeref)
293 auto HasSpecializedDelete = isClassWithSpecializedDelete(
C, RD);
294 if (!HasSpecializedDelete || *HasSpecializedDelete)
296 if (
C->lookupInBases(
297 [&](
const CXXBaseSpecifier *Base, CXXBasePath &) {
298 auto *T = Base->getType().getTypePtrOrNull();
301 auto *R = T->getAsCXXRecordDecl();
304 auto Result = isClassWithSpecializedDelete(R, RD);
306 AnyInconclusiveBase = true;
307 return Result && *Result;
311 if (AnyInconclusiveBase)
314 const auto *Dtor =
C->getDestructor();
315 if (!Dtor || !Dtor->isVirtual()) {
316 auto *ProblematicBaseSpecifier = &
Base;
317 auto *ProblematicBaseClass =
C;
318 reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
323 bool shouldSkipDecl(
const CXXRecordDecl *RD)
const {
336 if (!RDLocation.isValid())
340 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
344 if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=
351 static bool isRefCountedClass(
const CXXRecordDecl *D) {
359 StringRef ClsName = ClsNameStr;
360 return NamespaceName ==
"WTF" &&
361 (ClsName.ends_with(
"RefCounted") ||
362 ClsName ==
"ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr");
365 static std::optional<bool>
366 isClassWithSpecializedDelete(
const CXXRecordDecl *
C,
367 const CXXRecordDecl *DerivedClass) {
368 if (
auto *ClsTmplSpDecl = dyn_cast<ClassTemplateSpecializationDecl>(
C)) {
369 for (
auto *MethodDecl :
C->methods()) {
371 DerefFuncDeleteExprVisitor Visitor(ClsTmplSpDecl->getTemplateArgs(),
373 auto Result = Visitor.HasSpecializedDelete(MethodDecl);
380 for (
auto *MethodDecl :
C->methods()) {
382 DerefFuncDeleteExprVisitor Visitor(DerivedClass);
383 auto Result = Visitor.HasSpecializedDelete(MethodDecl);
391 void reportBug(
const CXXRecordDecl *DerivedClass,
392 const CXXBaseSpecifier *BaseSpec,
393 const CXXRecordDecl *ProblematicBaseClass)
const {
394 assert(DerivedClass);
396 assert(ProblematicBaseClass);
398 SmallString<100> Buf;
399 llvm::raw_svector_ostream Os(Buf);
401 Os << (ProblematicBaseClass->
isClass() ?
"Class" :
"Struct") <<
" ";
404 Os <<
" is used as a base of "
405 << (DerivedClass->
isClass() ?
"class" :
"struct") <<
" ";
408 Os <<
" but doesn't have virtual destructor";
411 BR->getSourceManager());
412 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
414 BR->emitReport(std::move(
Report));
419void ento::registerRefCntblBaseVirtualDtorChecker(
CheckerManager &Mgr) {
423bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
void setOrigin(const CXXRecordDecl *Rec)
SourceRange getSourceRange() const LLVM_READONLY
Retrieves the source range that contains the entire base specifier.
bool isLambda() const
Determine whether this class describes a lambda function object.
const CXXRecordDecl * getTemplateInstantiationPattern() const
Retrieve the record declaration from which this record could be instantiated.
bool hasDefinition() const
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
virtual bool hasBody() const
Returns true if this Decl represents a declaration for a body of code, such as a function or method d...
SourceLocation getLocation() const
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
SourceLocation getBegin() const
bool isThisDeclarationADefinition() const
Return true if this declaration is a completion definition of the type.
TagKind getTagKind() const
@ Type
The template argument is a type.
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
bool Cast(InterpState &S, CodePtr OpPC)
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
@ Result
The result type of a method or function.
const FunctionProtoType * T
std::optional< const clang::CXXRecordDecl * > hasPublicMethodInBase(const CXXBaseSpecifier *Base, StringRef NameToMatch)
@ Type
The name was classified as a type.
std::string safeGetName(const T *ASTNode)
bool declaresSameEntity(const Decl *D1, const Decl *D2)
Determine whether two declarations declare the same entity.
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor