clang 19.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 "DiagOutputUtils.h"
10#include "PtrTypesSemantics.h"
17#include <optional>
18
19using namespace clang;
20using namespace ento;
21
22namespace {
23class RefCntblBaseVirtualDtorChecker
24 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
25private:
26 BugType Bug;
27 mutable BugReporter *BR;
28
29public:
30 RefCntblBaseVirtualDtorChecker()
31 : Bug(this,
32 "Reference-countable base class doesn't have virtual destructor",
33 "WebKit coding guidelines") {}
34
35 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
36 BugReporter &BRArg) const {
37 BR = &BRArg;
38
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)
45 : Checker(Checker) {
46 assert(Checker);
47 }
48
49 bool shouldVisitTemplateInstantiations() const { return true; }
50 bool shouldVisitImplicitCode() const { return false; }
51
52 bool VisitCXXRecordDecl(const CXXRecordDecl *RD) {
53 Checker->visitCXXRecordDecl(RD);
54 return true;
55 }
56 };
57
58 LocalVisitor visitor(this);
59 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
60 }
61
62 void visitCXXRecordDecl(const CXXRecordDecl *RD) const {
63 if (shouldSkipDecl(RD))
64 return;
65
66 CXXBasePaths Paths;
67 Paths.setOrigin(RD);
68
69 const CXXBaseSpecifier *ProblematicBaseSpecifier = nullptr;
70 const CXXRecordDecl *ProblematicBaseClass = nullptr;
71
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()))
78 return false;
79
80 auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
81 auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
82
83 bool hasRef = hasRefInBase && *hasRefInBase != nullptr;
84 bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr;
85
86 QualType T = Base->getType();
87 if (T.isNull())
88 return false;
89
91 if (!C)
92 return false;
93 bool AnyInconclusiveBase = false;
94 const auto hasPublicRefInBase =
95 [&AnyInconclusiveBase](const CXXBaseSpecifier *Base,
96 CXXBasePath &) {
97 auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
98 if (!hasRefInBase) {
99 AnyInconclusiveBase = true;
100 return false;
101 }
102 return (*hasRefInBase) != nullptr;
103 };
104 const auto hasPublicDerefInBase = [&AnyInconclusiveBase](
105 const CXXBaseSpecifier *Base,
106 CXXBasePath &) {
107 auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
108 if (!hasDerefInBase) {
109 AnyInconclusiveBase = true;
110 return false;
111 }
112 return (*hasDerefInBase) != nullptr;
113 };
114 CXXBasePaths Paths;
115 Paths.setOrigin(C);
116 hasRef = hasRef || C->lookupInBases(hasPublicRefInBase, Paths,
117 /*LookupInDependent =*/true);
118 hasDeref = hasDeref || C->lookupInBases(hasPublicDerefInBase, Paths,
119 /*LookupInDependent =*/true);
120 if (AnyInconclusiveBase || !hasRef || !hasDeref)
121 return false;
122
123 const auto *Dtor = C->getDestructor();
124 if (!Dtor || !Dtor->isVirtual()) {
125 ProblematicBaseSpecifier = Base;
126 ProblematicBaseClass = C;
127 return true;
128 }
129
130 return false;
131 };
132
133 if (RD->lookupInBases(IsPublicBaseRefCntblWOVirtualDtor, Paths,
134 /*LookupInDependent =*/true)) {
135 reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
136 }
137 }
138
139 bool shouldSkipDecl(const CXXRecordDecl *RD) const {
141 return true;
142
143 if (RD->isImplicit())
144 return true;
145
146 if (RD->isLambda())
147 return true;
148
149 // If the construct doesn't have a source file, then it's not something
150 // we want to diagnose.
151 const auto RDLocation = RD->getLocation();
152 if (!RDLocation.isValid())
153 return true;
154
155 const auto Kind = RD->getTagKind();
156 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class)
157 return true;
158
159 // Ignore CXXRecords that come from system headers.
160 if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=
162 return true;
163
164 return false;
165 }
166
167 void reportBug(const CXXRecordDecl *DerivedClass,
168 const CXXBaseSpecifier *BaseSpec,
169 const CXXRecordDecl *ProblematicBaseClass) const {
170 assert(DerivedClass);
171 assert(BaseSpec);
172 assert(ProblematicBaseClass);
173
175 llvm::raw_svector_ostream Os(Buf);
176
177 Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " ";
178 printQuotedQualifiedName(Os, ProblematicBaseClass);
179
180 Os << " is used as a base of "
181 << (DerivedClass->isClass() ? "class" : "struct") << " ";
182 printQuotedQualifiedName(Os, DerivedClass);
183
184 Os << " but doesn't have virtual destructor";
185
187 BR->getSourceManager());
188 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
189 Report->addRange(BaseSpec->getSourceRange());
190 BR->emitReport(std::move(Report));
191 }
192};
193} // namespace
194
195void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {
196 Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();
197}
198
199bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
200 const CheckerManager &mgr) {
201 return true;
202}
Represents a path from a specific derived class (which is not represented as part of the path) to a p...
BasePaths - Represents the set of paths from a derived class to one of its (direct or indirect) bases...
Represents a base class of a C++ class.
Definition: DeclCXX.h:146
SourceRange getSourceRange() const LLVM_READONLY
Retrieves the source range that contains the entire base specifier.
Definition: DeclCXX.h:193
Represents a C++ struct/union/class.
Definition: DeclCXX.h:258
bool isLambda() const
Determine whether this class describes a lambda function object.
Definition: DeclCXX.h:1021
bool lookupInBases(BaseMatchesCallback BaseMatches, CXXBasePaths &Paths, bool LookupInDependent=false) const
Look for entities within the base classes of this C++ class, transitively searching all base class su...
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
Definition: DeclBase.h:598
SourceLocation getLocation() const
Definition: DeclBase.h:444
A (possibly-)qualified type.
Definition: Type.h:738
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition: Type.h:805
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
SrcMgr::CharacteristicKind getFileCharacteristic(SourceLocation Loc) const
Return the file characteristic of the specified source location, indicating whether this is a normal ...
SourceLocation getBegin() const
bool isThisDeclarationADefinition() const
Return true if this declaration is a completion definition of the type.
Definition: Decl.h:3647
bool isClass() const
Definition: Decl.h:3754
TagKind getTagKind() const
Definition: Decl.h:3744
The top declaration context.
Definition: Decl.h:84
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition: Type.cpp:1862
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:585
const SourceManager & getSourceManager()
Definition: BugReporter.h:623
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
The JSON file list parser is used to communicate input to InstallAPI.
std::optional< const clang::CXXRecordDecl * > hasPublicMethodInBase(const CXXBaseSpecifier *Base, const char *NameToMatch)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
@ AS_protected
Definition: Specifiers.h:122
@ AS_none
Definition: Specifiers.h:124
@ AS_private
Definition: Specifiers.h:123