1 //===- IvarInvalidationChecker.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 // This checker implements annotation driven invalidation checking. If a class
10 // contains a method annotated with 'objc_instance_variable_invalidator',
12 // __attribute__((annotate("objc_instance_variable_invalidator")));
13 // all the "ivalidatable" instance variables of this class should be
14 // invalidated. We call an instance variable ivalidatable if it is an object of
15 // a class which contains an invalidation method. There could be multiple
16 // methods annotated with such annotations per class, either one can be used
17 // to invalidate the ivar. An ivar or property are considered to be
18 // invalidated if they are being assigned 'nil' or an invalidation method has
19 // been called on them. An invalidation method should either invalidate all
20 // the ivars or call another invalidation method (on self).
22 // Partial invalidor annotation allows to address cases when ivars are
23 // invalidated by other methods, which might or might not be called from
24 // the invalidation method. The checker checks that each invalidation
25 // method and all the partial methods cumulatively invalidate all ivars.
26 // __attribute__((annotate("objc_instance_variable_invalidator_partial")));
28 //===----------------------------------------------------------------------===//
30 #include "clang/AST/Attr.h"
31 #include "clang/AST/DeclObjC.h"
32 #include "clang/AST/StmtVisitor.h"
33 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
34 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
35 #include "clang/StaticAnalyzer/Core/Checker.h"
36 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
37 #include "llvm/ADT/DenseMap.h"
38 #include "llvm/ADT/STLExtras.h"
39 #include "llvm/ADT/SetVector.h"
40 #include "llvm/ADT/SmallString.h"
42 using namespace clang
;
47 /// Check for missing invalidation method declarations.
48 bool check_MissingInvalidationMethod
= false;
49 /// Check that all ivars are invalidated.
50 bool check_InstanceVariableInvalidation
= false;
52 CheckerNameRef checkName_MissingInvalidationMethod
;
53 CheckerNameRef checkName_InstanceVariableInvalidation
;
56 class IvarInvalidationCheckerImpl
{
57 typedef llvm::SmallSetVector
<const ObjCMethodDecl
*, 2> MethodSet
;
58 typedef llvm::DenseMap
<const ObjCMethodDecl
*,
59 const ObjCIvarDecl
*> MethToIvarMapTy
;
60 typedef llvm::DenseMap
<const ObjCPropertyDecl
*,
61 const ObjCIvarDecl
*> PropToIvarMapTy
;
62 typedef llvm::DenseMap
<const ObjCIvarDecl
*,
63 const ObjCPropertyDecl
*> IvarToPropMapTy
;
65 struct InvalidationInfo
{
66 /// Has the ivar been invalidated?
67 bool IsInvalidated
= false;
69 /// The methods which can be used to invalidate the ivar.
70 MethodSet InvalidationMethods
;
72 InvalidationInfo() = default;
73 void addInvalidationMethod(const ObjCMethodDecl
*MD
) {
74 InvalidationMethods
.insert(MD
);
77 bool needsInvalidation() const {
78 return !InvalidationMethods
.empty();
81 bool hasMethod(const ObjCMethodDecl
*MD
) {
84 for (const ObjCMethodDecl
*Curr
: InvalidationMethods
) {
94 typedef llvm::DenseMap
<const ObjCIvarDecl
*, InvalidationInfo
> IvarSet
;
96 /// Statement visitor, which walks the method body and flags the ivars
97 /// referenced in it (either directly or via property).
98 class MethodCrawler
: public ConstStmtVisitor
<MethodCrawler
> {
99 /// The set of Ivars which need to be invalidated.
102 /// Flag is set as the result of a message send to another
103 /// invalidation method.
104 bool &CalledAnotherInvalidationMethod
;
106 /// Property setter to ivar mapping.
107 const MethToIvarMapTy
&PropertySetterToIvarMap
;
109 /// Property getter to ivar mapping.
110 const MethToIvarMapTy
&PropertyGetterToIvarMap
;
112 /// Property to ivar mapping.
113 const PropToIvarMapTy
&PropertyToIvarMap
;
115 /// The invalidation method being currently processed.
116 const ObjCMethodDecl
*InvalidationMethod
;
120 /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
121 const Expr
*peel(const Expr
*E
) const;
123 /// Does this expression represent zero: '0'?
124 bool isZero(const Expr
*E
) const;
126 /// Mark the given ivar as invalidated.
127 void markInvalidated(const ObjCIvarDecl
*Iv
);
129 /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
131 void checkObjCIvarRefExpr(const ObjCIvarRefExpr
*IvarRef
);
133 /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
134 /// it as invalidated.
135 void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr
*PA
);
137 /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
138 /// if yes, marks it as invalidated.
139 void checkObjCMessageExpr(const ObjCMessageExpr
*ME
);
141 /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
142 void check(const Expr
*E
);
145 MethodCrawler(IvarSet
&InIVars
,
146 bool &InCalledAnotherInvalidationMethod
,
147 const MethToIvarMapTy
&InPropertySetterToIvarMap
,
148 const MethToIvarMapTy
&InPropertyGetterToIvarMap
,
149 const PropToIvarMapTy
&InPropertyToIvarMap
,
152 CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod
),
153 PropertySetterToIvarMap(InPropertySetterToIvarMap
),
154 PropertyGetterToIvarMap(InPropertyGetterToIvarMap
),
155 PropertyToIvarMap(InPropertyToIvarMap
),
156 InvalidationMethod(nullptr),
159 void VisitStmt(const Stmt
*S
) { VisitChildren(S
); }
161 void VisitBinaryOperator(const BinaryOperator
*BO
);
163 void VisitObjCMessageExpr(const ObjCMessageExpr
*ME
);
165 void VisitChildren(const Stmt
*S
) {
166 for (const auto *Child
: S
->children()) {
169 if (CalledAnotherInvalidationMethod
)
175 /// Check if the any of the methods inside the interface are annotated with
176 /// the invalidation annotation, update the IvarInfo accordingly.
177 /// \param LookForPartial is set when we are searching for partial
179 static void containsInvalidationMethod(const ObjCContainerDecl
*D
,
180 InvalidationInfo
&Out
,
181 bool LookForPartial
);
183 /// Check if ivar should be tracked and add to TrackedIvars if positive.
184 /// Returns true if ivar should be tracked.
185 static bool trackIvar(const ObjCIvarDecl
*Iv
, IvarSet
&TrackedIvars
,
186 const ObjCIvarDecl
**FirstIvarDecl
);
188 /// Given the property declaration, and the list of tracked ivars, finds
189 /// the ivar backing the property when possible. Returns '0' when no such
190 /// ivar could be found.
191 static const ObjCIvarDecl
*findPropertyBackingIvar(
192 const ObjCPropertyDecl
*Prop
,
193 const ObjCInterfaceDecl
*InterfaceD
,
194 IvarSet
&TrackedIvars
,
195 const ObjCIvarDecl
**FirstIvarDecl
);
197 /// Print ivar name or the property if the given ivar backs a property.
198 static void printIvar(llvm::raw_svector_ostream
&os
,
199 const ObjCIvarDecl
*IvarDecl
,
200 const IvarToPropMapTy
&IvarToPopertyMap
);
202 void reportNoInvalidationMethod(CheckerNameRef CheckName
,
203 const ObjCIvarDecl
*FirstIvarDecl
,
204 const IvarToPropMapTy
&IvarToPopertyMap
,
205 const ObjCInterfaceDecl
*InterfaceD
,
206 bool MissingDeclaration
) const;
208 void reportIvarNeedsInvalidation(const ObjCIvarDecl
*IvarD
,
209 const IvarToPropMapTy
&IvarToPopertyMap
,
210 const ObjCMethodDecl
*MethodD
) const;
212 AnalysisManager
& Mgr
;
214 /// Filter on the checks performed.
215 const ChecksFilter
&Filter
;
218 IvarInvalidationCheckerImpl(AnalysisManager
& InMgr
,
220 const ChecksFilter
&InFilter
) :
221 Mgr (InMgr
), BR(InBR
), Filter(InFilter
) {}
223 void visit(const ObjCImplementationDecl
*D
) const;
226 static bool isInvalidationMethod(const ObjCMethodDecl
*M
, bool LookForPartial
) {
227 for (const auto *Ann
: M
->specific_attrs
<AnnotateAttr
>()) {
228 if (!LookForPartial
&&
229 Ann
->getAnnotation() == "objc_instance_variable_invalidator")
231 if (LookForPartial
&&
232 Ann
->getAnnotation() == "objc_instance_variable_invalidator_partial")
238 void IvarInvalidationCheckerImpl::containsInvalidationMethod(
239 const ObjCContainerDecl
*D
, InvalidationInfo
&OutInfo
, bool Partial
) {
244 assert(!isa
<ObjCImplementationDecl
>(D
));
245 // TODO: Cache the results.
247 // Check all methods.
248 for (const auto *MDI
: D
->methods())
249 if (isInvalidationMethod(MDI
, Partial
))
250 OutInfo
.addInvalidationMethod(
251 cast
<ObjCMethodDecl
>(MDI
->getCanonicalDecl()));
253 // If interface, check all parent protocols and super.
254 if (const ObjCInterfaceDecl
*InterfD
= dyn_cast
<ObjCInterfaceDecl
>(D
)) {
256 // Visit all protocols.
257 for (const auto *I
: InterfD
->protocols())
258 containsInvalidationMethod(I
->getDefinition(), OutInfo
, Partial
);
260 // Visit all categories in case the invalidation method is declared in
262 for (const auto *Ext
: InterfD
->visible_extensions())
263 containsInvalidationMethod(Ext
, OutInfo
, Partial
);
265 containsInvalidationMethod(InterfD
->getSuperClass(), OutInfo
, Partial
);
269 // If protocol, check all parent protocols.
270 if (const ObjCProtocolDecl
*ProtD
= dyn_cast
<ObjCProtocolDecl
>(D
)) {
271 for (const auto *I
: ProtD
->protocols()) {
272 containsInvalidationMethod(I
->getDefinition(), OutInfo
, Partial
);
278 bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl
*Iv
,
279 IvarSet
&TrackedIvars
,
280 const ObjCIvarDecl
**FirstIvarDecl
) {
281 QualType IvQTy
= Iv
->getType();
282 const ObjCObjectPointerType
*IvTy
= IvQTy
->getAs
<ObjCObjectPointerType
>();
285 const ObjCInterfaceDecl
*IvInterf
= IvTy
->getInterfaceDecl();
287 InvalidationInfo Info
;
288 containsInvalidationMethod(IvInterf
, Info
, /*LookForPartial*/ false);
289 if (Info
.needsInvalidation()) {
290 const ObjCIvarDecl
*I
= cast
<ObjCIvarDecl
>(Iv
->getCanonicalDecl());
291 TrackedIvars
[I
] = Info
;
299 const ObjCIvarDecl
*IvarInvalidationCheckerImpl::findPropertyBackingIvar(
300 const ObjCPropertyDecl
*Prop
,
301 const ObjCInterfaceDecl
*InterfaceD
,
302 IvarSet
&TrackedIvars
,
303 const ObjCIvarDecl
**FirstIvarDecl
) {
304 const ObjCIvarDecl
*IvarD
= nullptr;
306 // Lookup for the synthesized case.
307 IvarD
= Prop
->getPropertyIvarDecl();
308 // We only track the ivars/properties that are defined in the current
309 // class (not the parent).
310 if (IvarD
&& IvarD
->getContainingInterface() == InterfaceD
) {
311 if (TrackedIvars
.count(IvarD
)) {
314 // If the ivar is synthesized we still want to track it.
315 if (trackIvar(IvarD
, TrackedIvars
, FirstIvarDecl
))
319 // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
320 StringRef PropName
= Prop
->getIdentifier()->getName();
321 for (const ObjCIvarDecl
*Iv
: llvm::make_first_range(TrackedIvars
)) {
322 StringRef IvarName
= Iv
->getName();
324 if (IvarName
== PropName
)
327 SmallString
<128> PropNameWithUnderscore
;
329 llvm::raw_svector_ostream
os(PropNameWithUnderscore
);
330 os
<< '_' << PropName
;
332 if (IvarName
== PropNameWithUnderscore
)
336 // Note, this is a possible source of false positives. We could look at the
337 // getter implementation to find the ivar when its name is not derived from
338 // the property name.
342 void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream
&os
,
343 const ObjCIvarDecl
*IvarDecl
,
344 const IvarToPropMapTy
&IvarToPopertyMap
) {
345 if (IvarDecl
->getSynthesize()) {
346 const ObjCPropertyDecl
*PD
= IvarToPopertyMap
.lookup(IvarDecl
);
347 assert(PD
&&"Do we synthesize ivars for something other than properties?");
348 os
<< "Property "<< PD
->getName() << " ";
350 os
<< "Instance variable "<< IvarDecl
->getName() << " ";
354 // Check that the invalidatable interfaces with ivars/properties implement the
355 // invalidation methods.
356 void IvarInvalidationCheckerImpl::
357 visit(const ObjCImplementationDecl
*ImplD
) const {
358 // Collect all ivars that need cleanup.
360 // Record the first Ivar needing invalidation; used in reporting when only
361 // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
362 // deterministic output.
363 const ObjCIvarDecl
*FirstIvarDecl
= nullptr;
364 const ObjCInterfaceDecl
*InterfaceD
= ImplD
->getClassInterface();
366 // Collect ivars declared in this class, its extensions and its implementation
367 ObjCInterfaceDecl
*IDecl
= const_cast<ObjCInterfaceDecl
*>(InterfaceD
);
368 for (const ObjCIvarDecl
*Iv
= IDecl
->all_declared_ivar_begin(); Iv
;
369 Iv
= Iv
->getNextIvar())
370 trackIvar(Iv
, Ivars
, &FirstIvarDecl
);
372 // Construct Property/Property Accessor to Ivar maps to assist checking if an
373 // ivar which is backing a property has been reset.
374 MethToIvarMapTy PropSetterToIvarMap
;
375 MethToIvarMapTy PropGetterToIvarMap
;
376 PropToIvarMapTy PropertyToIvarMap
;
377 IvarToPropMapTy IvarToPopertyMap
;
379 ObjCInterfaceDecl::PropertyMap PropMap
;
380 InterfaceD
->collectPropertiesToImplement(PropMap
);
382 for (const ObjCPropertyDecl
*PD
: llvm::make_second_range(PropMap
)) {
383 if (PD
->isClassProperty())
386 const ObjCIvarDecl
*ID
= findPropertyBackingIvar(PD
, InterfaceD
, Ivars
,
391 // Store the mappings.
392 PD
= cast
<ObjCPropertyDecl
>(PD
->getCanonicalDecl());
393 PropertyToIvarMap
[PD
] = ID
;
394 IvarToPopertyMap
[ID
] = PD
;
396 // Find the setter and the getter.
397 const ObjCMethodDecl
*SetterD
= PD
->getSetterMethodDecl();
399 SetterD
= SetterD
->getCanonicalDecl();
400 PropSetterToIvarMap
[SetterD
] = ID
;
403 const ObjCMethodDecl
*GetterD
= PD
->getGetterMethodDecl();
405 GetterD
= GetterD
->getCanonicalDecl();
406 PropGetterToIvarMap
[GetterD
] = ID
;
410 // If no ivars need invalidation, there is nothing to check here.
414 // Find all partial invalidation methods.
415 InvalidationInfo PartialInfo
;
416 containsInvalidationMethod(InterfaceD
, PartialInfo
, /*LookForPartial*/ true);
418 // Remove ivars invalidated by the partial invalidation methods. They do not
419 // need to be invalidated in the regular invalidation methods.
420 bool AtImplementationContainsAtLeastOnePartialInvalidationMethod
= false;
421 for (const ObjCMethodDecl
*InterfD
: PartialInfo
.InvalidationMethods
) {
422 // Get the corresponding method in the @implementation.
423 const ObjCMethodDecl
*D
= ImplD
->getMethod(InterfD
->getSelector(),
424 InterfD
->isInstanceMethod());
425 if (D
&& D
->hasBody()) {
426 AtImplementationContainsAtLeastOnePartialInvalidationMethod
= true;
428 bool CalledAnotherInvalidationMethod
= false;
429 // The MethodCrowler is going to remove the invalidated ivars.
431 CalledAnotherInvalidationMethod
,
435 BR
.getContext()).VisitStmt(D
->getBody());
436 // If another invalidation method was called, trust that full invalidation
438 if (CalledAnotherInvalidationMethod
)
443 // If all ivars have been invalidated by partial invalidators, there is
444 // nothing to check here.
448 // Find all invalidation methods in this @interface declaration and parents.
449 InvalidationInfo Info
;
450 containsInvalidationMethod(InterfaceD
, Info
, /*LookForPartial*/ false);
452 // Report an error in case none of the invalidation methods are declared.
453 if (!Info
.needsInvalidation() && !PartialInfo
.needsInvalidation()) {
454 if (Filter
.check_MissingInvalidationMethod
)
455 reportNoInvalidationMethod(Filter
.checkName_MissingInvalidationMethod
,
456 FirstIvarDecl
, IvarToPopertyMap
, InterfaceD
,
457 /*MissingDeclaration*/ true);
458 // If there are no invalidation methods, there is no ivar validation work
463 // Only check if Ivars are invalidated when InstanceVariableInvalidation
464 // has been requested.
465 if (!Filter
.check_InstanceVariableInvalidation
)
468 // Check that all ivars are invalidated by the invalidation methods.
469 bool AtImplementationContainsAtLeastOneInvalidationMethod
= false;
470 for (const ObjCMethodDecl
*InterfD
: Info
.InvalidationMethods
) {
471 // Get the corresponding method in the @implementation.
472 const ObjCMethodDecl
*D
= ImplD
->getMethod(InterfD
->getSelector(),
473 InterfD
->isInstanceMethod());
474 if (D
&& D
->hasBody()) {
475 AtImplementationContainsAtLeastOneInvalidationMethod
= true;
477 // Get a copy of ivars needing invalidation.
478 IvarSet IvarsI
= Ivars
;
480 bool CalledAnotherInvalidationMethod
= false;
481 MethodCrawler(IvarsI
,
482 CalledAnotherInvalidationMethod
,
486 BR
.getContext()).VisitStmt(D
->getBody());
487 // If another invalidation method was called, trust that full invalidation
489 if (CalledAnotherInvalidationMethod
)
492 // Warn on the ivars that were not invalidated by the method.
493 for (const ObjCIvarDecl
*Ivar
: llvm::make_first_range(IvarsI
))
494 reportIvarNeedsInvalidation(Ivar
, IvarToPopertyMap
, D
);
498 // Report an error in case none of the invalidation methods are implemented.
499 if (!AtImplementationContainsAtLeastOneInvalidationMethod
) {
500 if (AtImplementationContainsAtLeastOnePartialInvalidationMethod
) {
501 // Warn on the ivars that were not invalidated by the prrtial
502 // invalidation methods.
503 for (const ObjCIvarDecl
*Ivar
: llvm::make_first_range(Ivars
))
504 reportIvarNeedsInvalidation(Ivar
, IvarToPopertyMap
, nullptr);
506 // Otherwise, no invalidation methods were implemented.
507 reportNoInvalidationMethod(Filter
.checkName_InstanceVariableInvalidation
,
508 FirstIvarDecl
, IvarToPopertyMap
, InterfaceD
,
509 /*MissingDeclaration*/ false);
514 void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
515 CheckerNameRef CheckName
, const ObjCIvarDecl
*FirstIvarDecl
,
516 const IvarToPropMapTy
&IvarToPopertyMap
,
517 const ObjCInterfaceDecl
*InterfaceD
, bool MissingDeclaration
) const {
518 SmallString
<128> sbuf
;
519 llvm::raw_svector_ostream
os(sbuf
);
520 assert(FirstIvarDecl
);
521 printIvar(os
, FirstIvarDecl
, IvarToPopertyMap
);
522 os
<< "needs to be invalidated; ";
523 if (MissingDeclaration
)
524 os
<< "no invalidation method is declared for ";
526 os
<< "no invalidation method is defined in the @implementation for ";
527 os
<< InterfaceD
->getName();
529 PathDiagnosticLocation IvarDecLocation
=
530 PathDiagnosticLocation::createBegin(FirstIvarDecl
, BR
.getSourceManager());
532 BR
.EmitBasicReport(FirstIvarDecl
, CheckName
, "Incomplete invalidation",
533 categories::CoreFoundationObjectiveC
, os
.str(),
537 void IvarInvalidationCheckerImpl::
538 reportIvarNeedsInvalidation(const ObjCIvarDecl
*IvarD
,
539 const IvarToPropMapTy
&IvarToPopertyMap
,
540 const ObjCMethodDecl
*MethodD
) const {
541 SmallString
<128> sbuf
;
542 llvm::raw_svector_ostream
os(sbuf
);
543 printIvar(os
, IvarD
, IvarToPopertyMap
);
544 os
<< "needs to be invalidated or set to nil";
546 PathDiagnosticLocation MethodDecLocation
=
547 PathDiagnosticLocation::createEnd(MethodD
->getBody(),
548 BR
.getSourceManager(),
549 Mgr
.getAnalysisDeclContext(MethodD
));
550 BR
.EmitBasicReport(MethodD
, Filter
.checkName_InstanceVariableInvalidation
,
551 "Incomplete invalidation",
552 categories::CoreFoundationObjectiveC
, os
.str(),
556 IvarD
, Filter
.checkName_InstanceVariableInvalidation
,
557 "Incomplete invalidation", categories::CoreFoundationObjectiveC
,
559 PathDiagnosticLocation::createBegin(IvarD
, BR
.getSourceManager()));
563 void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
564 const ObjCIvarDecl
*Iv
) {
565 IvarSet::iterator I
= IVars
.find(Iv
);
566 if (I
!= IVars
.end()) {
567 // If InvalidationMethod is present, we are processing the message send and
568 // should ensure we are invalidating with the appropriate method,
569 // otherwise, we are processing setting to 'nil'.
570 if (!InvalidationMethod
|| I
->second
.hasMethod(InvalidationMethod
))
575 const Expr
*IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr
*E
) const {
576 E
= E
->IgnoreParenCasts();
577 if (const PseudoObjectExpr
*POE
= dyn_cast
<PseudoObjectExpr
>(E
))
578 E
= POE
->getSyntacticForm()->IgnoreParenCasts();
579 if (const OpaqueValueExpr
*OVE
= dyn_cast
<OpaqueValueExpr
>(E
))
580 E
= OVE
->getSourceExpr()->IgnoreParenCasts();
584 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
585 const ObjCIvarRefExpr
*IvarRef
) {
586 if (const Decl
*D
= IvarRef
->getDecl())
587 markInvalidated(cast
<ObjCIvarDecl
>(D
->getCanonicalDecl()));
590 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
591 const ObjCMessageExpr
*ME
) {
592 const ObjCMethodDecl
*MD
= ME
->getMethodDecl();
594 MD
= MD
->getCanonicalDecl();
595 MethToIvarMapTy::const_iterator IvI
= PropertyGetterToIvarMap
.find(MD
);
596 if (IvI
!= PropertyGetterToIvarMap
.end())
597 markInvalidated(IvI
->second
);
601 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
602 const ObjCPropertyRefExpr
*PA
) {
604 if (PA
->isExplicitProperty()) {
605 const ObjCPropertyDecl
*PD
= PA
->getExplicitProperty();
607 PD
= cast
<ObjCPropertyDecl
>(PD
->getCanonicalDecl());
608 PropToIvarMapTy::const_iterator IvI
= PropertyToIvarMap
.find(PD
);
609 if (IvI
!= PropertyToIvarMap
.end())
610 markInvalidated(IvI
->second
);
615 if (PA
->isImplicitProperty()) {
616 const ObjCMethodDecl
*MD
= PA
->getImplicitPropertySetter();
618 MD
= MD
->getCanonicalDecl();
619 MethToIvarMapTy::const_iterator IvI
=PropertyGetterToIvarMap
.find(MD
);
620 if (IvI
!= PropertyGetterToIvarMap
.end())
621 markInvalidated(IvI
->second
);
627 bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr
*E
) const {
630 return (E
->isNullPointerConstant(Ctx
, Expr::NPC_ValueDependentIsNotNull
)
631 != Expr::NPCK_NotNull
);
634 void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr
*E
) {
637 if (const ObjCIvarRefExpr
*IvarRef
= dyn_cast
<ObjCIvarRefExpr
>(E
)) {
638 checkObjCIvarRefExpr(IvarRef
);
642 if (const ObjCPropertyRefExpr
*PropRef
= dyn_cast
<ObjCPropertyRefExpr
>(E
)) {
643 checkObjCPropertyRefExpr(PropRef
);
647 if (const ObjCMessageExpr
*MsgExpr
= dyn_cast
<ObjCMessageExpr
>(E
)) {
648 checkObjCMessageExpr(MsgExpr
);
653 void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
654 const BinaryOperator
*BO
) {
657 // Do we assign/compare against zero? If yes, check the variable we are
659 BinaryOperatorKind Opcode
= BO
->getOpcode();
660 if (Opcode
!= BO_Assign
&&
665 if (isZero(BO
->getRHS())) {
670 if (Opcode
!= BO_Assign
&& isZero(BO
->getLHS())) {
676 void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
677 const ObjCMessageExpr
*ME
) {
678 const ObjCMethodDecl
*MD
= ME
->getMethodDecl();
679 const Expr
*Receiver
= ME
->getInstanceReceiver();
681 // Stop if we are calling '[self invalidate]'.
682 if (Receiver
&& isInvalidationMethod(MD
, /*LookForPartial*/ false))
683 if (Receiver
->isObjCSelfExpr()) {
684 CalledAnotherInvalidationMethod
= true;
688 // Check if we call a setter and set the property to 'nil'.
689 if (MD
&& (ME
->getNumArgs() == 1) && isZero(ME
->getArg(0))) {
690 MD
= MD
->getCanonicalDecl();
691 MethToIvarMapTy::const_iterator IvI
= PropertySetterToIvarMap
.find(MD
);
692 if (IvI
!= PropertySetterToIvarMap
.end()) {
693 markInvalidated(IvI
->second
);
698 // Check if we call the 'invalidation' routine on the ivar.
700 InvalidationMethod
= MD
;
701 check(Receiver
->IgnoreParenCasts());
702 InvalidationMethod
= nullptr;
707 } // end anonymous namespace
709 // Register the checkers.
711 class IvarInvalidationChecker
:
712 public Checker
<check::ASTDecl
<ObjCImplementationDecl
> > {
716 void checkASTDecl(const ObjCImplementationDecl
*D
, AnalysisManager
& Mgr
,
717 BugReporter
&BR
) const {
718 IvarInvalidationCheckerImpl
Walker(Mgr
, BR
, Filter
);
722 } // end anonymous namespace
724 void ento::registerIvarInvalidationModeling(CheckerManager
&mgr
) {
725 mgr
.registerChecker
<IvarInvalidationChecker
>();
728 bool ento::shouldRegisterIvarInvalidationModeling(const CheckerManager
&mgr
) {
732 #define REGISTER_CHECKER(name) \
733 void ento::register##name(CheckerManager &mgr) { \
734 IvarInvalidationChecker *checker = \
735 mgr.getChecker<IvarInvalidationChecker>(); \
736 checker->Filter.check_##name = true; \
737 checker->Filter.checkName_##name = mgr.getCurrentCheckerName(); \
740 bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
742 REGISTER_CHECKER(InstanceVariableInvalidation
)
743 REGISTER_CHECKER(MissingInvalidationMethod
)