1 //=======- RefCntblBaseVirtualDtor.cpp ---------------------------*- C++ -*-==//
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
7 //===----------------------------------------------------------------------===//
9 #include "DiagOutputUtils.h"
10 #include "PtrTypesSemantics.h"
11 #include "clang/AST/CXXInheritance.h"
12 #include "clang/AST/RecursiveASTVisitor.h"
13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
15 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16 #include "clang/StaticAnalyzer/Core/Checker.h"
19 using namespace clang
;
23 class RefCntblBaseVirtualDtorChecker
24 : public Checker
<check::ASTDecl
<TranslationUnitDecl
>> {
27 mutable BugReporter
*BR
;
30 RefCntblBaseVirtualDtorChecker()
32 "Reference-countable base class doesn't have virtual destructor",
33 "WebKit coding guidelines") {}
35 void checkASTDecl(const TranslationUnitDecl
*TUD
, AnalysisManager
&MGR
,
36 BugReporter
&BRArg
) const {
39 // The calls to checkAST* from AnalysisConsumer don't
40 // visit template instantiations or lambda classes. We
41 // want to visit those, so we make our own RecursiveASTVisitor.
42 struct LocalVisitor
: public RecursiveASTVisitor
<LocalVisitor
> {
43 const RefCntblBaseVirtualDtorChecker
*Checker
;
44 explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker
*Checker
)
49 bool shouldVisitTemplateInstantiations() const { return true; }
50 bool shouldVisitImplicitCode() const { return false; }
52 bool VisitCXXRecordDecl(const CXXRecordDecl
*RD
) {
53 Checker
->visitCXXRecordDecl(RD
);
58 LocalVisitor
visitor(this);
59 visitor
.TraverseDecl(const_cast<TranslationUnitDecl
*>(TUD
));
62 void visitCXXRecordDecl(const CXXRecordDecl
*RD
) const {
63 if (shouldSkipDecl(RD
))
69 const CXXBaseSpecifier
*ProblematicBaseSpecifier
= nullptr;
70 const CXXRecordDecl
*ProblematicBaseClass
= nullptr;
72 const auto IsPublicBaseRefCntblWOVirtualDtor
=
73 [RD
, &ProblematicBaseSpecifier
,
74 &ProblematicBaseClass
](const CXXBaseSpecifier
*Base
, CXXBasePath
&) {
75 const auto AccSpec
= Base
->getAccessSpecifier();
76 if (AccSpec
== AS_protected
|| AccSpec
== AS_private
||
77 (AccSpec
== AS_none
&& RD
->isClass()))
80 std::optional
<const CXXRecordDecl
*> RefCntblBaseRD
= isRefCountable(Base
);
81 if (!RefCntblBaseRD
|| !(*RefCntblBaseRD
))
84 const auto *Dtor
= (*RefCntblBaseRD
)->getDestructor();
85 if (!Dtor
|| !Dtor
->isVirtual()) {
86 ProblematicBaseSpecifier
= Base
;
87 ProblematicBaseClass
= *RefCntblBaseRD
;
94 if (RD
->lookupInBases(IsPublicBaseRefCntblWOVirtualDtor
, Paths
,
95 /*LookupInDependent =*/true)) {
96 reportBug(RD
, ProblematicBaseSpecifier
, ProblematicBaseClass
);
100 bool shouldSkipDecl(const CXXRecordDecl
*RD
) const {
101 if (!RD
->isThisDeclarationADefinition())
104 if (RD
->isImplicit())
110 // If the construct doesn't have a source file, then it's not something
111 // we want to diagnose.
112 const auto RDLocation
= RD
->getLocation();
113 if (!RDLocation
.isValid())
116 const auto Kind
= RD
->getTagKind();
117 if (Kind
!= TagTypeKind::Struct
&& Kind
!= TagTypeKind::Class
)
120 // Ignore CXXRecords that come from system headers.
121 if (BR
->getSourceManager().getFileCharacteristic(RDLocation
) !=
128 void reportBug(const CXXRecordDecl
*DerivedClass
,
129 const CXXBaseSpecifier
*BaseSpec
,
130 const CXXRecordDecl
*ProblematicBaseClass
) const {
131 assert(DerivedClass
);
133 assert(ProblematicBaseClass
);
135 SmallString
<100> Buf
;
136 llvm::raw_svector_ostream
Os(Buf
);
138 Os
<< (ProblematicBaseClass
->isClass() ? "Class" : "Struct") << " ";
139 printQuotedQualifiedName(Os
, ProblematicBaseClass
);
141 Os
<< " is used as a base of "
142 << (DerivedClass
->isClass() ? "class" : "struct") << " ";
143 printQuotedQualifiedName(Os
, DerivedClass
);
145 Os
<< " but doesn't have virtual destructor";
147 PathDiagnosticLocation
BSLoc(BaseSpec
->getSourceRange().getBegin(),
148 BR
->getSourceManager());
149 auto Report
= std::make_unique
<BasicBugReport
>(Bug
, Os
.str(), BSLoc
);
150 Report
->addRange(BaseSpec
->getSourceRange());
151 BR
->emitReport(std::move(Report
));
156 void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager
&Mgr
) {
157 Mgr
.registerChecker
<RefCntblBaseVirtualDtorChecker
>();
160 bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
161 const CheckerManager
&mgr
) {