clang 24.0.0git
RawPtrRefMemberChecker.cpp
Go to the documentation of this file.
1//=======- RawPtrRefMemberChecker.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"
13#include "clang/AST/Decl.h"
14#include "clang/AST/DeclCXX.h"
21#include "llvm/Support/Casting.h"
22#include <optional>
23
24using namespace clang;
25using namespace ento;
26
27namespace {
28
29class RawPtrRefMemberChecker
30 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
31private:
32 BugType Bug;
33 mutable BugReporter *BR;
34 mutable llvm::DenseSet<const ObjCIvarDecl *> IvarDeclsToIgnore;
35
36protected:
37 const std::unique_ptr<PtrRefSafetyModel> Model;
38
39public:
40 RawPtrRefMemberChecker(const char *description,
41 std::unique_ptr<PtrRefSafetyModel> Model)
42 : Bug(this, description, "WebKit coding guidelines"),
43 Model(std::move(Model)) {}
44
45 std::optional<bool> isUnsafePtr(QualType QT, bool IgnoreARC = false) const {
46 return isUnsafePtrForStorage(*Model, QT, IgnoreARC);
47 }
48
49 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
50 BugReporter &BRArg) const {
51 BR = &BRArg;
52
53 // The calls to checkAST* from AnalysisConsumer don't
54 // visit template instantiations or lambda classes. We
55 // want to visit those, so we make our own RecursiveASTVisitor.
56 struct LocalVisitor : ConstDynamicRecursiveASTVisitor {
57 const RawPtrRefMemberChecker *Checker;
58 explicit LocalVisitor(const RawPtrRefMemberChecker *Checker)
59 : Checker(Checker) {
60 assert(Checker);
61 ShouldVisitTemplateInstantiations = true;
62 ShouldVisitImplicitCode = false;
63 }
64
65 bool VisitTypedefDecl(const TypedefDecl *TD) override {
66 if (auto *RTC = Checker->Model->retainTypeChecker())
67 RTC->visitTypedef(TD);
68 return true;
69 }
70
71 bool VisitRecordDecl(const RecordDecl *RD) override {
72 Checker->visitRecordDecl(RD);
73 return true;
74 }
75
76 bool VisitObjCContainerDecl(const ObjCContainerDecl *CD) override {
77 Checker->visitObjCDecl(CD);
78 return true;
79 }
80 };
81
82 LocalVisitor visitor(this);
83 if (auto *RTC = Model->retainTypeChecker())
84 RTC->visitTranslationUnitDecl(TUD);
85 visitor.TraverseDecl(TUD);
86 }
87
88 void visitRecordDecl(const RecordDecl *RD) const {
89 if (shouldSkipDecl(RD))
90 return;
91
92 for (auto *Member : RD->fields())
93 visitMember(Member, RD);
94 }
95
96 void visitMember(const FieldDecl *Member, const RecordDecl *RD) const {
97 auto QT = Member->getType();
98 const Type *MemberType = QT.getTypePtrOrNull();
99
100 while (MemberType) {
101 auto IsUnsafePtr = isUnsafePtr(QT);
102 if (IsUnsafePtr && *IsUnsafePtr)
103 break;
104 if (!MemberType->isPointerType())
105 return;
106 QT = MemberType->getPointeeType();
107 MemberType = QT.getTypePtrOrNull();
108 }
109
110 if (!MemberType)
111 return;
112
113 if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl())
114 reportBug(Member, MemberType, MemberCXXRD, RD);
115 else if (auto *ObjCDecl = getObjCDeclFromObjCPtr(MemberType))
116 reportBug(Member, MemberType, ObjCDecl, RD);
117 }
118
119 void visitObjCDecl(const ObjCContainerDecl *CD) const {
120 if (BR->getSourceManager().isInSystemHeader(CD->getLocation()))
121 return;
122
123 if (auto *ID = dyn_cast<ObjCImplementationDecl>(CD)) {
126 for (auto it : map)
127 visitObjCPropertyDecl(CD, it.second);
128
129 if (auto *Interface = ID->getClassInterface()) {
130 for (auto *Ivar : Interface->ivars())
131 visitIvarDecl(CD, Ivar);
132 }
133 for (auto *PropImpl : ID->property_impls())
134 visitPropImpl(CD, PropImpl);
135 for (auto *Ivar : ID->ivars())
136 visitIvarDecl(CD, Ivar);
137 return;
138 }
139 }
140
141 void visitIvarDecl(const ObjCContainerDecl *CD,
142 const ObjCIvarDecl *Ivar) const {
143 if (BR->getSourceManager().isInSystemHeader(Ivar->getLocation()))
144 return;
145
146 if (IvarDeclsToIgnore.contains(Ivar))
147 return;
148
149 auto QT = Ivar->getType();
150 const Type *IvarType = QT.getTypePtrOrNull();
151 if (!IvarType)
152 return;
153
154 auto IsUnsafePtr = isUnsafePtr(QT);
155 if (!IsUnsafePtr || !*IsUnsafePtr)
156 return;
157
158 IvarDeclsToIgnore.insert(Ivar);
159
160 if (auto *MemberCXXRD = IvarType->getPointeeCXXRecordDecl())
161 reportBug(Ivar, IvarType, MemberCXXRD, CD);
162 else if (auto *ObjCDecl = getObjCDeclFromObjCPtr(IvarType))
163 reportBug(Ivar, IvarType, ObjCDecl, CD);
164 }
165
166 void visitObjCPropertyDecl(const ObjCContainerDecl *CD,
167 const ObjCPropertyDecl *PD) const {
168 if (BR->getSourceManager().isInSystemHeader(PD->getLocation()))
169 return;
170
171 if (const ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(CD)) {
172 auto *RTC = Model->retainTypeChecker();
173 if (!RTC || !RTC->defaultSynthProperties() ||
174 ID->isObjCRequiresPropertyDefs())
175 return;
176 }
177
178 auto [IsUnsafe, PropType] = isPropImplUnsafePtr(PD);
179 if (!IsUnsafe)
180 return;
181
182 if (auto *MemberCXXRD = PropType->getPointeeCXXRecordDecl())
183 reportBug(PD, PropType, MemberCXXRD, CD);
184 else if (auto *ObjCDecl = getObjCDeclFromObjCPtr(PropType))
185 reportBug(PD, PropType, ObjCDecl, CD);
186 }
187
188 void visitPropImpl(const ObjCContainerDecl *CD,
189 const ObjCPropertyImplDecl *PID) const {
190 if (BR->getSourceManager().isInSystemHeader(PID->getLocation()))
191 return;
192
194 return;
195
196 auto *PropDecl = PID->getPropertyDecl();
197 if (auto *IvarDecl = PID->getPropertyIvarDecl()) {
198 if (IvarDeclsToIgnore.contains(IvarDecl))
199 return;
200 IvarDeclsToIgnore.insert(IvarDecl);
201 }
202 auto [IsUnsafe, PropType] = isPropImplUnsafePtr(PropDecl);
203 if (!IsUnsafe)
204 return;
205
206 if (auto *MemberCXXRD = PropType->getPointeeCXXRecordDecl())
207 reportBug(PropDecl, PropType, MemberCXXRD, CD);
208 else if (auto *ObjCDecl = getObjCDeclFromObjCPtr(PropType))
209 reportBug(PropDecl, PropType, ObjCDecl, CD);
210 }
211
212 std::pair<bool, const Type *>
213 isPropImplUnsafePtr(const ObjCPropertyDecl *PD) const {
214 if (!PD)
215 return {false, nullptr};
216
217 auto QT = PD->getType();
218 const Type *PropType = QT.getTypePtrOrNull();
219 if (!PropType)
220 return {false, nullptr};
221
222 // "assign" property doesn't retain even under ARC so treat it as unsafe.
223 bool ignoreARC =
225 bool IsWeak =
227 bool HasSafeAttr = PD->isRetaining() || IsWeak;
228 auto IsUnsafePtr = isUnsafePtr(QT, ignoreARC);
229 return {IsUnsafePtr && *IsUnsafePtr && !HasSafeAttr, PropType};
230 }
231
232 bool shouldSkipDecl(const RecordDecl *RD) const {
234 return true;
235
236 if (RD->isImplicit())
237 return true;
238
239 if (RD->isLambda())
240 return true;
241
242 // If the construct doesn't have a source file, then it's not something
243 // we want to diagnose.
244 const auto RDLocation = RD->getLocation();
245 if (!RDLocation.isValid())
246 return true;
247
248 const auto Kind = RD->getTagKind();
249 if (Kind != TagTypeKind::Struct && Kind != TagTypeKind::Class &&
250 Kind != TagTypeKind::Union)
251 return true;
252
253 // Ignore CXXRecords that come from system headers.
254 if (BR->getSourceManager().isInSystemHeader(RDLocation))
255 return true;
256
257 // Ref-counted smartpointers actually have raw-pointer to uncounted type as
258 // a member but we trust them to handle it correctly.
259 auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
260 if (CXXRD && isSmartPtr(CXXRD))
261 return true;
262
263 return false;
264 }
265
266 template <typename DeclType, typename PointeeType, typename ParentDeclType>
267 void reportBug(const DeclType *Member, const Type *MemberType,
268 const PointeeType *Pointee,
269 const ParentDeclType *ClassCXXRD) const {
270 assert(Member);
271 assert(MemberType);
272 assert(Pointee);
273
274 SmallString<100> Buf;
275 llvm::raw_svector_ostream Os(Buf);
276
277 if (isa<ObjCContainerDecl>(ClassCXXRD)) {
279 Os << "Property ";
280 else
281 Os << "Instance variable ";
282 } else
283 Os << "Member variable ";
285 Os << " (of ";
286 printQuotedQualifiedName(Os, ClassCXXRD);
287 Os << ")";
288 if (Member->getType().getTypePtrOrNull() == MemberType)
289 Os << " is a ";
290 else
291 Os << " contains a ";
292 if (printPointer(Os, MemberType) == PrintDeclKind::Pointer) {
293 auto Typedef = MemberType->getAs<TypedefType>();
294 assert(Typedef);
295 printQuotedQualifiedName(Os, Typedef->getDecl());
296 } else
297 printQuotedQualifiedName(Os, Pointee);
298
299 PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
300 BR->getSourceManager());
301 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
302 Report->addRange(Member->getSourceRange());
303 if (auto *EnclosingCtx = Member->getDeclContext())
304 Report->setDeclWithIssue(Decl::castFromDeclContext(EnclosingCtx));
305 BR->emitReport(std::move(Report));
306 }
307
308 enum class PrintDeclKind { Pointee, Pointer };
309 PrintDeclKind printPointer(llvm::raw_svector_ostream &Os,
310 const Type *T) const {
311 // Retain/OS types are frequently spelled through a typedef (e.g. CFXXXRef);
312 // print the typedef name rather than desugaring to the pointee.
313 if (Model->retainTypeChecker() && !isa<ObjCObjectPointerType>(T) &&
314 T->getAs<TypedefType>()) {
315 Os << Model->typeName() << " ";
316 return PrintDeclKind::Pointer;
317 }
320 Os << (IsPtr ? "raw pointer" : "reference") << " to " << Model->typeName()
321 << " ";
322 return PrintDeclKind::Pointee;
323 }
324};
325
326class NoUncountedMemberChecker final : public RawPtrRefMemberChecker {
327public:
328 NoUncountedMemberChecker()
329 : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
330 "reference-countable type",
332};
333
334class NoUncheckedPtrMemberChecker final : public RawPtrRefMemberChecker {
335public:
336 NoUncheckedPtrMemberChecker()
337 : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
338 "checked-pointer capable type",
340};
341
342class NoUnretainedMemberChecker final : public RawPtrRefMemberChecker {
343public:
344 NoUnretainedMemberChecker()
345 : RawPtrRefMemberChecker("Member variable is a raw-pointer/reference to "
346 "retainable type",
348};
349
350} // namespace
351
352void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
353 Mgr.registerChecker<NoUncountedMemberChecker>();
354}
355
356bool ento::shouldRegisterNoUncountedMemberChecker(const CheckerManager &Mgr) {
357 return true;
358}
359
360void ento::registerNoUncheckedPtrMemberChecker(CheckerManager &Mgr) {
361 Mgr.registerChecker<NoUncheckedPtrMemberChecker>();
362}
363
364bool ento::shouldRegisterNoUncheckedPtrMemberChecker(
365 const CheckerManager &Mgr) {
366 return true;
367}
368
369void ento::registerNoUnretainedMemberChecker(CheckerManager &Mgr) {
370 Mgr.registerChecker<NoUnretainedMemberChecker>();
371}
372
373bool ento::shouldRegisterNoUnretainedMemberChecker(const CheckerManager &Mgr) {
374 return true;
375}
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the SourceManager interface.
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
Definition DeclBase.h:602
static Decl * castFromDeclContext(const DeclContext *)
SourceLocation getLocation() const
Definition DeclBase.h:448
llvm::MapVector< std::pair< IdentifierInfo *, unsigned >, ObjCPropertyDecl * > PropertyMap
Definition DeclObjC.h:1091
virtual void collectPropertiesToImplement(PropertyMap &PM) const
This routine collects list of properties to be implemented in the class.
Definition DeclObjC.h:1100
bool isRetaining() const
isRetaining - Return true if the property retains its value.
Definition DeclObjC.h:854
bool isReadOnly() const
isReadOnly - Return true iff the property has a setter.
Definition DeclObjC.h:844
SetterKind getSetterKind() const
getSetterKind - Return the method used for doing assignment in the property setter.
Definition DeclObjC.h:879
QualType getType() const
Definition DeclObjC.h:810
ObjCPropertyAttribute::Kind getPropertyAttributes() const
Definition DeclObjC.h:821
ObjCIvarDecl * getPropertyIvarDecl() const
Definition DeclObjC.h:2885
Kind getPropertyImplementation() const
Definition DeclObjC.h:2881
ObjCPropertyDecl * getPropertyDecl() const
Definition DeclObjC.h:2876
const Type * getTypePtrOrNull() const
Definition TypeBase.h:8450
bool isLambda() const
Determine whether this record is a class describing a lambda function object.
Definition Decl.cpp:5343
field_range fields() const
Definition Decl.h:4679
bool isThisDeclarationADefinition() const
Return true if this declaration is a completion definition of the type.
Definition Decl.h:3964
TagKind getTagKind() const
Definition Decl.h:4068
bool isPointerType() const
Definition TypeBase.h:8683
const CXXRecordDecl * getPointeeCXXRecordDecl() const
If this is a pointer or reference to a RecordType, return the CXXRecordDecl that the type refers to.
Definition Type.cpp:2080
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:885
const T * getAs() const
Member-template getAs<specific type>'.
Definition TypeBase.h:9299
const Type * getUnqualifiedDesugaredType() const
Return the specified type with any "sugar" removed from the type, removing any typedefs,...
Definition Type.cpp:786
QualType getType() const
Definition Decl.h:725
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
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
std::unique_ptr< PtrRefSafetyModel > makeCheckedPtrSafetyModel()
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
void printQuotedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
DynamicRecursiveASTVisitorBase< true > ConstDynamicRecursiveASTVisitor
std::optional< bool > isUnsafePtrForStorage(const PtrRefSafetyModel &Model, QualType T, bool IgnoreARC=false)
Applies the memory-management exemptions that hold for a variable, member, or lambda capture (but not...
const FunctionProtoType * T
bool isSmartPtr(const CXXRecordDecl *R)
ObjCInterfaceDecl * getObjCDeclFromObjCPtr(const Type *TypePtr)
Definition ASTUtils.cpp:639
std::unique_ptr< PtrRefSafetyModel > makeRefPtrSafetyModel()
@ Interface
The "__interface" keyword introduces the elaborated-type-specifier.
Definition TypeBase.h:6019
std::unique_ptr< PtrRefSafetyModel > makeRetainPtrSafetyModel()