clang 24.0.0git
RefCntblBaseVirtualDtorChecker.cpp
Go to the documentation of this file.
1//=======- RefCntblBaseVirtualDtor.cpp ---------------------------*- C++ -*-==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "ASTUtils.h"
10#include "DiagOutputUtils.h"
11#include "PtrTypesSemantics.h"
20#include "llvm/ADT/DenseSet.h"
21#include "llvm/ADT/SetVector.h"
22#include <optional>
23
24using namespace clang;
25using namespace ento;
26
27namespace {
28
29class DerefFuncDeleteExprVisitor
30 : public ConstStmtVisitor<DerefFuncDeleteExprVisitor, bool> {
31 // Returns true if any of child statements return true.
32 bool VisitChildren(const Stmt *S) {
33 for (const Stmt *Child : S->children()) {
34 if (Child && Visit(Child))
35 return true;
36 }
37 return false;
38 }
39
40 bool VisitBody(const Stmt *Body) {
41 if (!Body)
42 return false;
43
44 auto [It, IsNew] = VisitedBody.insert(Body);
45 if (!IsNew) // This body is recursive
46 return false;
47
48 return Visit(Body);
49 }
50
51public:
52 DerefFuncDeleteExprVisitor(const TemplateArgumentList &ArgList,
53 const CXXRecordDecl *ClassDecl)
54 : ArgList(&ArgList), ClassDecl(ClassDecl) {}
55
56 DerefFuncDeleteExprVisitor(const CXXRecordDecl *ClassDecl)
57 : ClassDecl(ClassDecl) {}
58
59 std::optional<bool> HasSpecializedDelete(CXXMethodDecl *Decl) {
60 if (auto *Body = Decl->getBody())
61 return VisitBody(Body);
62 if (Decl->getTemplateInstantiationPattern())
63 return std::nullopt; // Indeterminate. There was no concrete instance.
64 return false;
65 }
66
67 bool VisitCallExpr(const CallExpr *CE) {
68 const Decl *D = CE->getCalleeDecl();
69 if (Stmt *Body; D && (Body = D->getBody()))
70 return VisitBody(Body);
71 else {
72 auto name = safeGetName(D);
73 if (name == "ensureOnMainThread" || name == "ensureOnMainRunLoop") {
74 for (unsigned i = 0; i < CE->getNumArgs(); ++i) {
75 auto *Arg = CE->getArg(i);
76 if (VisitLambdaArgument(Arg))
77 return true;
78 }
79 }
80 }
81 return false;
82 }
83
84 bool VisitLambdaArgument(const Expr *E) {
85 E = E->IgnoreParenCasts();
86 if (auto *TempE = dyn_cast<CXXBindTemporaryExpr>(E))
87 E = TempE->getSubExpr();
88 E = E->IgnoreParenCasts();
89 if (auto *Ref = dyn_cast<DeclRefExpr>(E)) {
90 if (auto *VD = dyn_cast_or_null<VarDecl>(Ref->getDecl()))
91 return VisitLambdaArgument(VD->getInit());
92 return false;
93 }
94 if (auto *Lambda = dyn_cast<LambdaExpr>(E)) {
95 if (VisitBody(Lambda->getBody()))
96 return true;
97 }
98 if (auto *ConstructE = dyn_cast<CXXConstructExpr>(E)) {
99 for (unsigned i = 0; i < ConstructE->getNumArgs(); ++i) {
100 if (VisitLambdaArgument(ConstructE->getArg(i)))
101 return true;
102 }
103 }
104 return false;
105 }
106
107 bool VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
108 auto *Arg = E->getArgument();
109 while (Arg) {
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();
114 auto CastType = Cast->getType();
115 if (auto *PtrType = dyn_cast<PointerType>(CastType)) {
116 auto PointeeType = PtrType->getPointeeType();
117 if (auto *ParmType = dyn_cast<TemplateTypeParmType>(PointeeType)) {
118 if (ArgList) {
119 auto ParmIndex = ParmType->getIndex();
120 auto Type = ArgList->get(ParmIndex).getAsType();
121 if (Type->getAsCXXRecordDecl() == ClassDecl)
122 return true;
123 }
124 } else if (auto *RD = dyn_cast<RecordType>(PointeeType)) {
125 if (declaresSameEntity(RD->getDecl(), ClassDecl))
126 return true;
127 } else if (auto *ST =
128 dyn_cast<SubstTemplateTypeParmType>(PointeeType)) {
129 auto Type = ST->getReplacementType();
130 if (auto *RD = dyn_cast<RecordType>(Type)) {
131 if (declaresSameEntity(RD->getDecl(), ClassDecl))
132 return true;
133 }
134 }
135 }
136 } else
137 break;
138 }
139 return false;
140 }
141
142 bool VisitStmt(const Stmt *S) { return VisitChildren(S); }
143
144 // Return false since the contents of lambda isn't necessarily executed.
145 // If it is executed, VisitCallExpr above will visit its body.
146 bool VisitLambdaExpr(const LambdaExpr *) { return false; }
147
148private:
149 const TemplateArgumentList *ArgList{nullptr};
150 const CXXRecordDecl *ClassDecl;
151 llvm::DenseSet<const Stmt *> VisitedBody;
152};
153
154class RefCntblBaseVirtualDtorChecker
155 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
156private:
157 BugType Bug;
158 mutable BugReporter *BR;
159
160public:
161 RefCntblBaseVirtualDtorChecker()
162 : Bug(this,
163 "Reference-countable base class doesn't have virtual destructor",
164 "WebKit coding guidelines") {}
165
166 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
167 BugReporter &BRArg) const {
168 BR = &BRArg;
169
170 // The calls to checkAST* from AnalysisConsumer don't
171 // visit template instantiations or lambda classes. We
172 // want to visit those, so we make our own RecursiveASTVisitor.
173 struct LocalVisitor : DynamicRecursiveASTVisitor {
174 const RefCntblBaseVirtualDtorChecker *Checker;
175 explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker)
176 : Checker(Checker) {
177 assert(Checker);
178 ShouldVisitTemplateInstantiations = true;
179 ShouldVisitImplicitCode = false;
180 }
181
182 bool VisitCXXRecordDecl(CXXRecordDecl *RD) override {
183 if (!RD->hasDefinition())
184 return true;
185
186 Decls.insert(RD);
187
188 for (auto &Base : RD->bases()) {
189 const auto AccSpec = Base.getAccessSpecifier();
190 if (AccSpec == AS_protected || AccSpec == AS_private ||
191 (AccSpec == AS_none && RD->isClass()))
192 continue;
193
194 QualType T = Base.getType();
195 if (T.isNull())
196 continue;
197
198 const CXXRecordDecl *C = T->getAsCXXRecordDecl();
199 if (!C)
200 continue;
201
202 bool isExempt = safeGetName(C->getParent()) == "WTF" &&
203 T.getAsString() == "NoVirtualDestructorBase";
204 if (isExempt || ExemptDecls.contains(C)) {
205 ExemptDecls.insert(RD);
206 continue;
207 }
208
209 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(C)) {
210 for (auto &Arg : CTSD->getTemplateArgs().asArray()) {
211 if (Arg.getKind() != TemplateArgument::Type)
212 continue;
213 auto TemplT = Arg.getAsType();
214 if (TemplT.isNull())
215 continue;
216
217 bool IsCRTP = TemplT->getAsCXXRecordDecl() == RD;
218 if (!IsCRTP)
219 continue;
220 CRTPs.insert(C);
221 }
222 }
223 }
224
225 return true;
226 }
227
228 llvm::SetVector<const CXXRecordDecl *> Decls;
229 llvm::DenseSet<const CXXRecordDecl *> CRTPs;
230 llvm::DenseSet<const CXXRecordDecl *> ExemptDecls;
231 };
232
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))
237 continue;
238 visitCXXRecordDecl(RD);
239 }
240 }
241
242 void visitCXXRecordDecl(const CXXRecordDecl *RD) const {
243 if (shouldSkipDecl(RD))
244 return;
245
246 for (auto &Base : RD->bases()) {
247 const auto AccSpec = Base.getAccessSpecifier();
248 if (AccSpec == AS_protected || AccSpec == AS_private ||
249 (AccSpec == AS_none && RD->isClass()))
250 continue;
251
252 auto hasRefInBase = clang::hasPublicMethodInBase(&Base, "ref");
253 auto hasDerefInBase = clang::hasPublicMethodInBase(&Base, "deref");
254
255 bool hasRef = hasRefInBase && *hasRefInBase != nullptr;
256 bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr;
257
258 QualType T = Base.getType();
259 if (T.isNull())
260 continue;
261
262 const CXXRecordDecl *C = T->getAsCXXRecordDecl();
263 if (!C)
264 continue;
265
266 bool AnyInconclusiveBase = false;
267 const auto hasPublicRefInBase =
268 [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
269 auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
270 if (!hasRefInBase) {
271 AnyInconclusiveBase = true;
272 return false;
273 }
274 return (*hasRefInBase) != nullptr;
275 };
276 const auto hasPublicDerefInBase =
277 [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
278 auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
279 if (!hasDerefInBase) {
280 AnyInconclusiveBase = true;
281 return false;
282 }
283 return (*hasDerefInBase) != nullptr;
284 };
285 CXXBasePaths Paths;
286 Paths.setOrigin(C);
287 hasRef = hasRef || C->lookupInBases(hasPublicRefInBase, Paths,
288 /*LookupInDependent =*/true);
289 hasDeref = hasDeref || C->lookupInBases(hasPublicDerefInBase, Paths,
290 /*LookupInDependent =*/true);
291 if (AnyInconclusiveBase || !hasRef || !hasDeref)
292 continue;
293
294 auto HasSpecializedDelete = isClassWithSpecializedDelete(C, RD);
295 if (!HasSpecializedDelete || *HasSpecializedDelete)
296 continue;
297 if (C->lookupInBases(
298 [&](const CXXBaseSpecifier *Base, CXXBasePath &) {
299 auto *T = Base->getType().getTypePtrOrNull();
300 if (!T)
301 return false;
302 auto *R = T->getAsCXXRecordDecl();
303 if (!R)
304 return false;
305 auto Result = isClassWithSpecializedDelete(R, RD);
306 if (!Result)
307 AnyInconclusiveBase = true;
308 return Result && *Result;
309 },
310 Paths, /*LookupInDependent =*/true))
311 continue;
312 if (AnyInconclusiveBase)
313 continue;
314
315 const auto *Dtor = C->getDestructor();
316 if (!Dtor || !Dtor->isVirtual()) {
317 auto *ProblematicBaseSpecifier = &Base;
318 auto *ProblematicBaseClass = C;
319 reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
320 }
321 }
322 }
323
324 bool shouldSkipDecl(const CXXRecordDecl *RD) const {
326 return true;
327
328 if (RD->isImplicit())
329 return true;
330
331 if (RD->isLambda())
332 return true;
333
334 // If the construct doesn't have a source file, then it's not something
335 // we want to diagnose.
336 const auto RDLocation = RD->getLocation();
337 if (!RDLocation.isValid())
338 return true;
339
340 const auto Kind = RD->getTagKind();
341 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
342 return true;
343
344 // Ignore CXXRecords that come from system headers.
345 if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=
347 return true;
348
349 return false;
350 }
351
352 static bool isRefCountedClass(const CXXRecordDecl *D) {
354 return false;
355 auto *NsDecl = D->getParent();
356 if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
357 return false;
358 auto NamespaceName = safeGetName(NsDecl);
359 auto ClsNameStr = safeGetName(D);
360 StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
361 return NamespaceName == "WTF" &&
362 (ClsName.ends_with("RefCounted") ||
363 ClsName == "ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr");
364 }
365
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()) {
371 if (safeGetName(MethodDecl) == "deref") {
372 DerefFuncDeleteExprVisitor Visitor(ClsTmplSpDecl->getTemplateArgs(),
373 DerivedClass);
374 auto Result = Visitor.HasSpecializedDelete(MethodDecl);
375 if (!Result || *Result)
376 return Result;
377 }
378 }
379 return false;
380 }
381 for (auto *MethodDecl : C->methods()) {
382 if (safeGetName(MethodDecl) == "deref") {
383 DerefFuncDeleteExprVisitor Visitor(DerivedClass);
384 auto Result = Visitor.HasSpecializedDelete(MethodDecl);
385 if (!Result || *Result)
386 return Result;
387 }
388 }
389 return false;
390 }
391
392 void reportBug(const CXXRecordDecl *DerivedClass,
393 const CXXBaseSpecifier *BaseSpec,
394 const CXXRecordDecl *ProblematicBaseClass) const {
395 assert(DerivedClass);
396 assert(BaseSpec);
397 assert(ProblematicBaseClass);
398
399 SmallString<100> Buf;
400 llvm::raw_svector_ostream Os(Buf);
401
402 Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " ";
403 printQuotedQualifiedName(Os, ProblematicBaseClass);
404
405 Os << " is used as a base of "
406 << (DerivedClass->isClass() ? "class" : "struct") << " ";
407 printQuotedQualifiedName(Os, DerivedClass);
408
409 Os << " but doesn't have virtual destructor";
410
411 PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(),
412 BR->getSourceManager());
413 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
414 Report->addRange(BaseSpec->getSourceRange());
415 BR->emitReport(std::move(Report));
416 }
417};
418} // namespace
419
420void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {
421 Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();
422}
423
424bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
425 const CheckerManager &mgr) {
426 return true;
427}
CastType
Definition SemaCast.cpp:51
Defines the SourceManager interface.
void setOrigin(const CXXRecordDecl *Rec)
SourceRange getSourceRange() const LLVM_READONLY
Retrieves the source range that contains the entire base specifier.
Definition DeclCXX.h:193
base_class_range bases()
Definition DeclCXX.h:609
bool isLambda() const
Determine whether this class describes a lambda function object.
Definition DeclCXX.h:1028
const CXXRecordDecl * getTemplateInstantiationPattern() const
Retrieve the record declaration from which this record could be instantiated.
Definition DeclCXX.cpp:2087
bool hasDefinition() const
Definition DeclCXX.h:562
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3216
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition Expr.h:3203
Decl * getCalleeDecl()
Definition Expr.h:3189
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition DeclBase.h:2108
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
Definition DeclBase.h:602
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Definition DeclBase.h:1105
SourceLocation getLocation() const
Definition DeclBase.h:448
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition Expr.cpp:3134
SourceLocation getBegin() const
child_range children()
Definition Stmt.cpp:304
bool isThisDeclarationADefinition() const
Return true if this declaration is a completion definition of the type.
Definition Decl.h:3964
bool isClass() const
Definition Decl.h:4078
TagKind getTagKind() const
Definition Decl.h:4068
@ 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...
Definition Type.h:26
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.
Definition Checker.h:565
bool Cast(InterpState &S, CodePtr OpPC)
Definition Interp.h:2834
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.
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
@ AS_protected
Definition Specifiers.h:129
@ AS_none
Definition Specifiers.h:131
@ AS_private
Definition Specifiers.h:130
@ Result
The result type of a method or function.
Definition TypeBase.h:906
const FunctionProtoType * T
std::optional< const clang::CXXRecordDecl * > hasPublicMethodInBase(const CXXBaseSpecifier *Base, StringRef NameToMatch)
std::string safeGetName(const T *ASTNode)
Definition ASTUtils.h:109
bool declaresSameEntity(const Decl *D1, const Decl *D2)
Determine whether two declarations declare the same entity.
Definition DeclBase.h:1306
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor