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