[TableGen] Add TreePatternNode::children and use it in for loops (NFC) (#119877)
[llvm-project.git] / clang / lib / StaticAnalyzer / Checkers / WebKit / RefCntblBaseVirtualDtorChecker.cpp
blob77520f1f731c1fee0f1c4a6fc8f7beeb141c0d69
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 //===----------------------------------------------------------------------===//
9 #include "ASTUtils.h"
10 #include "DiagOutputUtils.h"
11 #include "PtrTypesSemantics.h"
12 #include "clang/AST/CXXInheritance.h"
13 #include "clang/AST/DynamicRecursiveASTVisitor.h"
14 #include "clang/AST/StmtVisitor.h"
15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
17 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18 #include "clang/StaticAnalyzer/Core/Checker.h"
19 #include "llvm/ADT/DenseSet.h"
20 #include "llvm/ADT/SetVector.h"
21 #include <optional>
23 using namespace clang;
24 using namespace ento;
26 namespace {
28 class DerefFuncDeleteExprVisitor
29 : public ConstStmtVisitor<DerefFuncDeleteExprVisitor, bool> {
30 // Returns true if any of child statements return true.
31 bool VisitChildren(const Stmt *S) {
32 for (const Stmt *Child : S->children()) {
33 if (Child && Visit(Child))
34 return true;
36 return false;
39 bool VisitBody(const Stmt *Body) {
40 if (!Body)
41 return false;
43 auto [It, IsNew] = VisitedBody.insert(Body);
44 if (!IsNew) // This body is recursive
45 return false;
47 return Visit(Body);
50 public:
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())
62 return std::nullopt; // Indeterminate. There was no concrete instance.
63 return false;
66 bool VisitCallExpr(const CallExpr *CE) {
67 const Decl *D = CE->getCalleeDecl();
68 if (D && D->hasBody())
69 return VisitBody(D->getBody());
70 else {
71 auto name = safeGetName(D);
72 if (name == "ensureOnMainThread" || name == "ensureOnMainRunLoop") {
73 for (unsigned i = 0; i < CE->getNumArgs(); ++i) {
74 auto *Arg = CE->getArg(i);
75 if (VisitLambdaArgument(Arg))
76 return true;
80 return false;
83 bool VisitLambdaArgument(const Expr *E) {
84 E = E->IgnoreParenCasts();
85 if (auto *TempE = dyn_cast<CXXBindTemporaryExpr>(E))
86 E = TempE->getSubExpr();
87 E = E->IgnoreParenCasts();
88 if (auto *Ref = dyn_cast<DeclRefExpr>(E)) {
89 if (auto *VD = dyn_cast_or_null<VarDecl>(Ref->getDecl()))
90 return VisitLambdaArgument(VD->getInit());
91 return false;
93 if (auto *Lambda = dyn_cast<LambdaExpr>(E)) {
94 if (VisitBody(Lambda->getBody()))
95 return true;
97 if (auto *ConstructE = dyn_cast<CXXConstructExpr>(E)) {
98 for (unsigned i = 0; i < ConstructE->getNumArgs(); ++i) {
99 if (VisitLambdaArgument(ConstructE->getArg(i)))
100 return true;
103 return false;
106 bool VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
107 auto *Arg = E->getArgument();
108 while (Arg) {
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();
113 auto CastType = Cast->getType();
114 if (auto *PtrType = dyn_cast<PointerType>(CastType)) {
115 auto PointeeType = PtrType->getPointeeType();
116 while (auto *ET = dyn_cast<ElaboratedType>(PointeeType)) {
117 if (ET->isSugared())
118 PointeeType = ET->desugar();
120 if (auto *ParmType = dyn_cast<TemplateTypeParmType>(PointeeType)) {
121 if (ArgList) {
122 auto ParmIndex = ParmType->getIndex();
123 auto Type = ArgList->get(ParmIndex).getAsType();
124 if (Type->getAsCXXRecordDecl() == ClassDecl)
125 return true;
127 } else if (auto *RD = dyn_cast<RecordType>(PointeeType)) {
128 if (RD->getDecl() == ClassDecl)
129 return true;
130 } else if (auto *ST =
131 dyn_cast<SubstTemplateTypeParmType>(PointeeType)) {
132 auto Type = ST->getReplacementType();
133 if (auto *RD = dyn_cast<RecordType>(Type)) {
134 if (RD->getDecl() == ClassDecl)
135 return true;
139 } else
140 break;
142 return false;
145 bool VisitStmt(const Stmt *S) { return VisitChildren(S); }
147 // Return false since the contents of lambda isn't necessarily executed.
148 // If it is executed, VisitCallExpr above will visit its body.
149 bool VisitLambdaExpr(const LambdaExpr *) { return false; }
151 private:
152 const TemplateArgumentList *ArgList{nullptr};
153 const CXXRecordDecl *ClassDecl;
154 llvm::DenseSet<const Stmt *> VisitedBody;
157 class RefCntblBaseVirtualDtorChecker
158 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
159 private:
160 BugType Bug;
161 mutable BugReporter *BR;
163 public:
164 RefCntblBaseVirtualDtorChecker()
165 : Bug(this,
166 "Reference-countable base class doesn't have virtual destructor",
167 "WebKit coding guidelines") {}
169 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
170 BugReporter &BRArg) const {
171 BR = &BRArg;
173 // The calls to checkAST* from AnalysisConsumer don't
174 // visit template instantiations or lambda classes. We
175 // want to visit those, so we make our own RecursiveASTVisitor.
176 struct LocalVisitor : DynamicRecursiveASTVisitor {
177 const RefCntblBaseVirtualDtorChecker *Checker;
178 explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker)
179 : Checker(Checker) {
180 assert(Checker);
181 ShouldVisitTemplateInstantiations = true;
182 ShouldVisitImplicitCode = false;
185 bool VisitCXXRecordDecl(CXXRecordDecl *RD) override {
186 if (!RD->hasDefinition())
187 return true;
189 Decls.insert(RD);
191 for (auto &Base : RD->bases()) {
192 const auto AccSpec = Base.getAccessSpecifier();
193 if (AccSpec == AS_protected || AccSpec == AS_private ||
194 (AccSpec == AS_none && RD->isClass()))
195 continue;
197 QualType T = Base.getType();
198 if (T.isNull())
199 continue;
201 const CXXRecordDecl *C = T->getAsCXXRecordDecl();
202 if (!C)
203 continue;
205 if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(C)) {
206 for (auto &Arg : CTSD->getTemplateArgs().asArray()) {
207 if (Arg.getKind() != TemplateArgument::Type)
208 continue;
209 auto TemplT = Arg.getAsType();
210 if (TemplT.isNull())
211 continue;
213 bool IsCRTP = TemplT->getAsCXXRecordDecl() == RD;
214 if (!IsCRTP)
215 continue;
216 CRTPs.insert(C);
221 return true;
224 llvm::SetVector<const CXXRecordDecl *> Decls;
225 llvm::DenseSet<const CXXRecordDecl *> CRTPs;
228 LocalVisitor visitor(this);
229 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
230 for (auto *RD : visitor.Decls) {
231 if (visitor.CRTPs.contains(RD))
232 continue;
233 visitCXXRecordDecl(RD);
237 void visitCXXRecordDecl(const CXXRecordDecl *RD) const {
238 if (shouldSkipDecl(RD))
239 return;
241 for (auto &Base : RD->bases()) {
242 const auto AccSpec = Base.getAccessSpecifier();
243 if (AccSpec == AS_protected || AccSpec == AS_private ||
244 (AccSpec == AS_none && RD->isClass()))
245 continue;
247 auto hasRefInBase = clang::hasPublicMethodInBase(&Base, "ref");
248 auto hasDerefInBase = clang::hasPublicMethodInBase(&Base, "deref");
250 bool hasRef = hasRefInBase && *hasRefInBase != nullptr;
251 bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr;
253 QualType T = Base.getType();
254 if (T.isNull())
255 continue;
257 const CXXRecordDecl *C = T->getAsCXXRecordDecl();
258 if (!C)
259 continue;
261 bool AnyInconclusiveBase = false;
262 const auto hasPublicRefInBase =
263 [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
264 auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
265 if (!hasRefInBase) {
266 AnyInconclusiveBase = true;
267 return false;
269 return (*hasRefInBase) != nullptr;
271 const auto hasPublicDerefInBase =
272 [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
273 auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
274 if (!hasDerefInBase) {
275 AnyInconclusiveBase = true;
276 return false;
278 return (*hasDerefInBase) != nullptr;
280 CXXBasePaths Paths;
281 Paths.setOrigin(C);
282 hasRef = hasRef || C->lookupInBases(hasPublicRefInBase, Paths,
283 /*LookupInDependent =*/true);
284 hasDeref = hasDeref || C->lookupInBases(hasPublicDerefInBase, Paths,
285 /*LookupInDependent =*/true);
286 if (AnyInconclusiveBase || !hasRef || !hasDeref)
287 continue;
289 auto HasSpecializedDelete = isClassWithSpecializedDelete(C, RD);
290 if (!HasSpecializedDelete || *HasSpecializedDelete)
291 continue;
292 if (C->lookupInBases(
293 [&](const CXXBaseSpecifier *Base, CXXBasePath &) {
294 auto *T = Base->getType().getTypePtrOrNull();
295 if (!T)
296 return false;
297 auto *R = T->getAsCXXRecordDecl();
298 if (!R)
299 return false;
300 auto Result = isClassWithSpecializedDelete(R, RD);
301 if (!Result)
302 AnyInconclusiveBase = true;
303 return Result && *Result;
305 Paths, /*LookupInDependent =*/true))
306 continue;
307 if (AnyInconclusiveBase)
308 continue;
310 const auto *Dtor = C->getDestructor();
311 if (!Dtor || !Dtor->isVirtual()) {
312 auto *ProblematicBaseSpecifier = &Base;
313 auto *ProblematicBaseClass = C;
314 reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
319 bool shouldSkipDecl(const CXXRecordDecl *RD) const {
320 if (!RD->isThisDeclarationADefinition())
321 return true;
323 if (RD->isImplicit())
324 return true;
326 if (RD->isLambda())
327 return true;
329 // If the construct doesn't have a source file, then it's not something
330 // we want to diagnose.
331 const auto RDLocation = RD->getLocation();
332 if (!RDLocation.isValid())
333 return true;
335 const auto Kind = RD->getTagKind();
336 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
337 return true;
339 // Ignore CXXRecords that come from system headers.
340 if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=
341 SrcMgr::C_User)
342 return true;
344 return false;
347 static bool isRefCountedClass(const CXXRecordDecl *D) {
348 if (!D->getTemplateInstantiationPattern())
349 return false;
350 auto *NsDecl = D->getParent();
351 if (!NsDecl || !isa<NamespaceDecl>(NsDecl))
352 return false;
353 auto NamespaceName = safeGetName(NsDecl);
354 auto ClsNameStr = safeGetName(D);
355 StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef.
356 return NamespaceName == "WTF" &&
357 (ClsName.ends_with("RefCounted") ||
358 ClsName == "ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr");
361 static std::optional<bool>
362 isClassWithSpecializedDelete(const CXXRecordDecl *C,
363 const CXXRecordDecl *DerivedClass) {
364 if (auto *ClsTmplSpDecl = dyn_cast<ClassTemplateSpecializationDecl>(C)) {
365 for (auto *MethodDecl : C->methods()) {
366 if (safeGetName(MethodDecl) == "deref") {
367 DerefFuncDeleteExprVisitor Visitor(ClsTmplSpDecl->getTemplateArgs(),
368 DerivedClass);
369 auto Result = Visitor.HasSpecializedDelete(MethodDecl);
370 if (!Result || *Result)
371 return Result;
374 return false;
376 for (auto *MethodDecl : C->methods()) {
377 if (safeGetName(MethodDecl) == "deref") {
378 DerefFuncDeleteExprVisitor Visitor(DerivedClass);
379 auto Result = Visitor.HasSpecializedDelete(MethodDecl);
380 if (!Result || *Result)
381 return Result;
384 return false;
387 void reportBug(const CXXRecordDecl *DerivedClass,
388 const CXXBaseSpecifier *BaseSpec,
389 const CXXRecordDecl *ProblematicBaseClass) const {
390 assert(DerivedClass);
391 assert(BaseSpec);
392 assert(ProblematicBaseClass);
394 SmallString<100> Buf;
395 llvm::raw_svector_ostream Os(Buf);
397 Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " ";
398 printQuotedQualifiedName(Os, ProblematicBaseClass);
400 Os << " is used as a base of "
401 << (DerivedClass->isClass() ? "class" : "struct") << " ";
402 printQuotedQualifiedName(Os, DerivedClass);
404 Os << " but doesn't have virtual destructor";
406 PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(),
407 BR->getSourceManager());
408 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
409 Report->addRange(BaseSpec->getSourceRange());
410 BR->emitReport(std::move(Report));
413 } // namespace
415 void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {
416 Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();
419 bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
420 const CheckerManager &mgr) {
421 return true;