20#include "llvm/ADT/DenseSet.h"
21#include "llvm/ADT/SetVector.h"
29class DerefFuncDeleteExprVisitor
32 bool VisitChildren(
const Stmt *S) {
33 for (
const Stmt *Child : S->
children()) {
34 if (Child && Visit(Child))
40 bool VisitBody(
const Stmt *Body) {
44 auto [It, IsNew] = VisitedBody.insert(Body);
52 DerefFuncDeleteExprVisitor(
const TemplateArgumentList &ArgList,
53 const CXXRecordDecl *ClassDecl)
54 : ArgList(&ArgList), ClassDecl(ClassDecl) {}
56 DerefFuncDeleteExprVisitor(
const CXXRecordDecl *ClassDecl)
57 : ClassDecl(ClassDecl) {}
59 std::optional<bool> HasSpecializedDelete(CXXMethodDecl *Decl) {
60 if (
auto *Body =
Decl->getBody())
61 return VisitBody(Body);
62 if (
Decl->getTemplateInstantiationPattern())
67 bool VisitCallExpr(
const CallExpr *CE) {
69 if (Stmt *Body; D && (Body = D->
getBody()))
70 return VisitBody(Body);
73 if (name ==
"ensureOnMainThread" || name ==
"ensureOnMainRunLoop") {
74 for (
unsigned i = 0; i < CE->
getNumArgs(); ++i) {
76 if (VisitLambdaArgument(Arg))
84 bool VisitLambdaArgument(
const Expr *E) {
86 if (
auto *TempE = dyn_cast<CXXBindTemporaryExpr>(E))
87 E = TempE->getSubExpr();
89 if (
auto *Ref = dyn_cast<DeclRefExpr>(E)) {
90 if (
auto *VD = dyn_cast_or_null<VarDecl>(Ref->getDecl()))
91 return VisitLambdaArgument(VD->getInit());
94 if (
auto *Lambda = dyn_cast<LambdaExpr>(E)) {
95 if (VisitBody(Lambda->getBody()))
98 if (
auto *ConstructE = dyn_cast<CXXConstructExpr>(E)) {
99 for (
unsigned i = 0; i < ConstructE->getNumArgs(); ++i) {
100 if (VisitLambdaArgument(ConstructE->getArg(i)))
107 bool VisitCXXDeleteExpr(
const CXXDeleteExpr *E) {
110 if (
auto *
Paren = dyn_cast<ParenExpr>(Arg))
111 Arg =
Paren->getSubExpr();
112 else if (
auto *Cast = dyn_cast<CastExpr>(Arg)) {
113 Arg =
Cast->getSubExpr();
115 if (
auto *PtrType = dyn_cast<PointerType>(
CastType)) {
116 auto PointeeType = PtrType->getPointeeType();
117 if (
auto *ParmType = dyn_cast<TemplateTypeParmType>(PointeeType)) {
119 auto ParmIndex = ParmType->getIndex();
120 auto Type = ArgList->get(ParmIndex).getAsType();
121 if (
Type->getAsCXXRecordDecl() == ClassDecl)
124 }
else if (
auto *RD = dyn_cast<RecordType>(PointeeType)) {
127 }
else if (
auto *ST =
128 dyn_cast<SubstTemplateTypeParmType>(PointeeType)) {
129 auto Type = ST->getReplacementType();
130 if (
auto *RD = dyn_cast<RecordType>(
Type)) {
142 bool VisitStmt(
const Stmt *S) {
return VisitChildren(S); }
146 bool VisitLambdaExpr(
const LambdaExpr *) {
return false; }
149 const TemplateArgumentList *ArgList{
nullptr};
150 const CXXRecordDecl *ClassDecl;
151 llvm::DenseSet<const Stmt *> VisitedBody;
154class RefCntblBaseVirtualDtorChecker
155 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
158 mutable BugReporter *BR;
161 RefCntblBaseVirtualDtorChecker()
163 "Reference-countable base class doesn't have virtual destructor",
164 "WebKit coding guidelines") {}
166 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
167 BugReporter &BRArg)
const {
174 const RefCntblBaseVirtualDtorChecker *Checker;
175 explicit LocalVisitor(
const RefCntblBaseVirtualDtorChecker *Checker)
178 ShouldVisitTemplateInstantiations =
true;
179 ShouldVisitImplicitCode =
false;
182 bool VisitCXXRecordDecl(CXXRecordDecl *RD)
override {
188 for (
auto &Base : RD->
bases()) {
189 const auto AccSpec =
Base.getAccessSpecifier();
194 QualType
T =
Base.getType();
203 T.getAsString() ==
"NoVirtualDestructorBase";
204 if (isExempt || ExemptDecls.contains(
C)) {
205 ExemptDecls.insert(RD);
209 if (
auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(
C)) {
210 for (
auto &Arg : CTSD->getTemplateArgs().asArray()) {
213 auto TemplT = Arg.getAsType();
217 bool IsCRTP = TemplT->getAsCXXRecordDecl() == RD;
228 llvm::SetVector<const CXXRecordDecl *> Decls;
229 llvm::DenseSet<const CXXRecordDecl *> CRTPs;
230 llvm::DenseSet<const CXXRecordDecl *> ExemptDecls;
233 LocalVisitor visitor(
this);
234 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
235 for (
auto *RD : visitor.Decls) {
236 if (visitor.CRTPs.contains(RD) || visitor.ExemptDecls.contains(RD))
238 visitCXXRecordDecl(RD);
242 void visitCXXRecordDecl(
const CXXRecordDecl *RD)
const {
243 if (shouldSkipDecl(RD))
246 for (
auto &Base : RD->
bases()) {
247 const auto AccSpec =
Base.getAccessSpecifier();
255 bool hasRef = hasRefInBase && *hasRefInBase !=
nullptr;
256 bool hasDeref = hasDerefInBase && *hasDerefInBase !=
nullptr;
258 QualType
T =
Base.getType();
266 bool AnyInconclusiveBase =
false;
267 const auto hasPublicRefInBase =
268 [&AnyInconclusiveBase](
const CXXBaseSpecifier *
Base, CXXBasePath &) {
271 AnyInconclusiveBase =
true;
274 return (*hasRefInBase) !=
nullptr;
276 const auto hasPublicDerefInBase =
277 [&AnyInconclusiveBase](
const CXXBaseSpecifier *
Base, CXXBasePath &) {
279 if (!hasDerefInBase) {
280 AnyInconclusiveBase =
true;
283 return (*hasDerefInBase) !=
nullptr;
287 hasRef = hasRef ||
C->lookupInBases(hasPublicRefInBase, Paths,
289 hasDeref = hasDeref ||
C->lookupInBases(hasPublicDerefInBase, Paths,
291 if (AnyInconclusiveBase || !hasRef || !hasDeref)
294 auto HasSpecializedDelete = isClassWithSpecializedDelete(
C, RD);
295 if (!HasSpecializedDelete || *HasSpecializedDelete)
297 if (
C->lookupInBases(
298 [&](
const CXXBaseSpecifier *Base, CXXBasePath &) {
299 auto *T = Base->getType().getTypePtrOrNull();
302 auto *R = T->getAsCXXRecordDecl();
305 auto Result = isClassWithSpecializedDelete(R, RD);
307 AnyInconclusiveBase = true;
308 return Result && *Result;
312 if (AnyInconclusiveBase)
315 const auto *
Dtor =
C->getDestructor();
316 if (!Dtor || !
Dtor->isVirtual()) {
317 auto *ProblematicBaseSpecifier = &
Base;
318 auto *ProblematicBaseClass =
C;
319 reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
324 bool shouldSkipDecl(
const CXXRecordDecl *RD)
const {
337 if (!RDLocation.isValid())
341 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
345 if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=
352 static bool isRefCountedClass(
const CXXRecordDecl *D) {
360 StringRef ClsName = ClsNameStr;
361 return NamespaceName ==
"WTF" &&
362 (ClsName.ends_with(
"RefCounted") ||
363 ClsName ==
"ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr");
366 static std::optional<bool>
367 isClassWithSpecializedDelete(
const CXXRecordDecl *
C,
368 const CXXRecordDecl *DerivedClass) {
369 if (
auto *ClsTmplSpDecl = dyn_cast<ClassTemplateSpecializationDecl>(
C)) {
370 for (
auto *MethodDecl :
C->methods()) {
372 DerefFuncDeleteExprVisitor Visitor(ClsTmplSpDecl->getTemplateArgs(),
374 auto Result = Visitor.HasSpecializedDelete(MethodDecl);
381 for (
auto *MethodDecl :
C->methods()) {
383 DerefFuncDeleteExprVisitor Visitor(DerivedClass);
384 auto Result = Visitor.HasSpecializedDelete(MethodDecl);
392 void reportBug(
const CXXRecordDecl *DerivedClass,
393 const CXXBaseSpecifier *BaseSpec,
394 const CXXRecordDecl *ProblematicBaseClass)
const {
395 assert(DerivedClass);
397 assert(ProblematicBaseClass);
399 SmallString<100> Buf;
400 llvm::raw_svector_ostream Os(Buf);
402 Os << (ProblematicBaseClass->
isClass() ?
"Class" :
"Struct") <<
" ";
405 Os <<
" is used as a base of "
406 << (DerivedClass->
isClass() ?
"class" :
"struct") <<
" ";
409 Os <<
" but doesn't have virtual destructor";
412 BR->getSourceManager());
413 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
415 BR->emitReport(std::move(
Report));
420void ento::registerRefCntblBaseVirtualDtorChecker(
CheckerManager &Mgr) {
424bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
Defines the SourceManager interface.
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...
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.
Top level wrappers for InstallAPI frontend operations.
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)
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