xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp (revision 470543bb2b8dcd1ae3f5486bf009e5e76b5ac670)
1 //=- IvarInvalidationChecker.cpp - -*- C++ -------------------------------*-==//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This checker implements annotation driven invalidation checking. If a class
11 //  contains a method annotated with 'objc_instance_variable_invalidator',
12 //  - (void) foo
13 //           __attribute__((annotate("objc_instance_variable_invalidator")));
14 //  all the "ivalidatable" instance variables of this class should be
15 //  invalidated. We call an instance variable ivalidatable if it is an object of
16 //  a class which contains an invalidation method. There could be multiple
17 //  methods annotated with such annotations per class, either one can be used
18 //  to invalidate the ivar. An ivar or property are considered to be
19 //  invalidated if they are being assigned 'nil' or an invalidation method has
20 //  been called on them. An invalidation method should either invalidate all
21 //  the ivars or call another invalidation method (on self).
22 //
23 //  Partial invalidor annotation allows to addess cases when ivars are
24 //  invalidated by other methods, which might or might not be called from
25 //  the invalidation method. The checker checks that each invalidation
26 //  method and all the partial methods cumulatively invalidate all ivars.
27 //    __attribute__((annotate("objc_instance_variable_invalidator_partial")));
28 //
29 //===----------------------------------------------------------------------===//
30 
31 #include "ClangSACheckers.h"
32 #include "clang/AST/Attr.h"
33 #include "clang/AST/DeclObjC.h"
34 #include "clang/AST/StmtVisitor.h"
35 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
36 #include "clang/StaticAnalyzer/Core/Checker.h"
37 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
38 #include "llvm/ADT/DenseMap.h"
39 #include "llvm/ADT/SetVector.h"
40 #include "llvm/ADT/SmallString.h"
41 
42 using namespace clang;
43 using namespace ento;
44 
45 namespace {
46 class IvarInvalidationChecker :
47   public Checker<check::ASTDecl<ObjCImplementationDecl> > {
48 
49   typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
50   typedef llvm::DenseMap<const ObjCMethodDecl*,
51                          const ObjCIvarDecl*> MethToIvarMapTy;
52   typedef llvm::DenseMap<const ObjCPropertyDecl*,
53                          const ObjCIvarDecl*> PropToIvarMapTy;
54   typedef llvm::DenseMap<const ObjCIvarDecl*,
55                          const ObjCPropertyDecl*> IvarToPropMapTy;
56 
57 
58   struct InvalidationInfo {
59     /// Has the ivar been invalidated?
60     bool IsInvalidated;
61 
62     /// The methods which can be used to invalidate the ivar.
63     MethodSet InvalidationMethods;
64 
65     InvalidationInfo() : IsInvalidated(false) {}
66     void addInvalidationMethod(const ObjCMethodDecl *MD) {
67       InvalidationMethods.insert(MD);
68     }
69 
70     bool needsInvalidation() const {
71       return !InvalidationMethods.empty();
72     }
73 
74     bool hasMethod(const ObjCMethodDecl *MD) {
75       if (IsInvalidated)
76         return true;
77       for (MethodSet::iterator I = InvalidationMethods.begin(),
78           E = InvalidationMethods.end(); I != E; ++I) {
79         if (*I == MD) {
80           IsInvalidated = true;
81           return true;
82         }
83       }
84       return false;
85     }
86   };
87 
88   typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
89 
90   /// Statement visitor, which walks the method body and flags the ivars
91   /// referenced in it (either directly or via property).
92   class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
93     /// The set of Ivars which need to be invalidated.
94     IvarSet &IVars;
95 
96     /// Flag is set as the result of a message send to another
97     /// invalidation method.
98     bool &CalledAnotherInvalidationMethod;
99 
100     /// Property setter to ivar mapping.
101     const MethToIvarMapTy &PropertySetterToIvarMap;
102 
103     /// Property getter to ivar mapping.
104     const MethToIvarMapTy &PropertyGetterToIvarMap;
105 
106     /// Property to ivar mapping.
107     const PropToIvarMapTy &PropertyToIvarMap;
108 
109     /// The invalidation method being currently processed.
110     const ObjCMethodDecl *InvalidationMethod;
111 
112     ASTContext &Ctx;
113 
114     /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
115     const Expr *peel(const Expr *E) const;
116 
117     /// Does this expression represent zero: '0'?
118     bool isZero(const Expr *E) const;
119 
120     /// Mark the given ivar as invalidated.
121     void markInvalidated(const ObjCIvarDecl *Iv);
122 
123     /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
124     /// invalidated.
125     void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
126 
127     /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
128     /// it as invalidated.
129     void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
130 
131     /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
132     /// if yes, marks it as invalidated.
133     void checkObjCMessageExpr(const ObjCMessageExpr *ME);
134 
135     /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
136     void check(const Expr *E);
137 
138   public:
139     MethodCrawler(IvarSet &InIVars,
140                   bool &InCalledAnotherInvalidationMethod,
141                   const MethToIvarMapTy &InPropertySetterToIvarMap,
142                   const MethToIvarMapTy &InPropertyGetterToIvarMap,
143                   const PropToIvarMapTy &InPropertyToIvarMap,
144                   ASTContext &InCtx)
145     : IVars(InIVars),
146       CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
147       PropertySetterToIvarMap(InPropertySetterToIvarMap),
148       PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
149       PropertyToIvarMap(InPropertyToIvarMap),
150       InvalidationMethod(0),
151       Ctx(InCtx) {}
152 
153     void VisitStmt(const Stmt *S) { VisitChildren(S); }
154 
155     void VisitBinaryOperator(const BinaryOperator *BO);
156 
157     void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
158 
159     void VisitChildren(const Stmt *S) {
160       for (Stmt::const_child_range I = S->children(); I; ++I) {
161         if (*I)
162           this->Visit(*I);
163         if (CalledAnotherInvalidationMethod)
164           return;
165       }
166     }
167   };
168 
169   /// Check if the any of the methods inside the interface are annotated with
170   /// the invalidation annotation, update the IvarInfo accordingly.
171   /// \param LookForPartial is set when we are searching for partial
172   ///        invalidators.
173   static void containsInvalidationMethod(const ObjCContainerDecl *D,
174                                          InvalidationInfo &Out,
175                                          bool LookForPartial);
176 
177   /// Check if ivar should be tracked and add to TrackedIvars if positive.
178   /// Returns true if ivar should be tracked.
179   static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
180                         const ObjCIvarDecl **FirstIvarDecl);
181 
182   /// Given the property declaration, and the list of tracked ivars, finds
183   /// the ivar backing the property when possible. Returns '0' when no such
184   /// ivar could be found.
185   static const ObjCIvarDecl *findPropertyBackingIvar(
186       const ObjCPropertyDecl *Prop,
187       const ObjCInterfaceDecl *InterfaceD,
188       IvarSet &TrackedIvars,
189       const ObjCIvarDecl **FirstIvarDecl);
190 
191   /// Print ivar name or the property if the given ivar backs a property.
192   static void printIvar(llvm::raw_svector_ostream &os,
193                         const ObjCIvarDecl *IvarDecl,
194                         const IvarToPropMapTy &IvarToPopertyMap);
195 
196   static void reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
197                                       const IvarToPropMapTy &IvarToPopertyMap,
198                                       const ObjCInterfaceDecl *InterfaceD,
199                                       BugReporter &BR,
200                                       bool MissingDeclaration);
201   static void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
202                                       const IvarToPropMapTy &IvarToPopertyMap,
203                                       const ObjCMethodDecl *MethodD,
204                                       AnalysisManager& Mgr,
205                                       BugReporter &BR);
206 
207 
208 public:
209   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
210                     BugReporter &BR) const;
211 };
212 
213 static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
214   for (specific_attr_iterator<AnnotateAttr>
215        AI = M->specific_attr_begin<AnnotateAttr>(),
216        AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
217     const AnnotateAttr *Ann = *AI;
218     if (!LookForPartial &&
219         Ann->getAnnotation() == "objc_instance_variable_invalidator")
220       return true;
221     if (LookForPartial &&
222         Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
223       return true;
224   }
225   return false;
226 }
227 
228 void IvarInvalidationChecker::containsInvalidationMethod(
229     const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
230 
231   if (!D)
232     return;
233 
234   assert(!isa<ObjCImplementationDecl>(D));
235   // TODO: Cache the results.
236 
237   // Check all methods.
238   for (ObjCContainerDecl::method_iterator
239       I = D->meth_begin(),
240       E = D->meth_end(); I != E; ++I) {
241       const ObjCMethodDecl *MDI = *I;
242       if (isInvalidationMethod(MDI, Partial))
243         OutInfo.addInvalidationMethod(
244                                cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
245   }
246 
247   // If interface, check all parent protocols and super.
248   if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
249 
250     // Visit all protocols.
251     for (ObjCInterfaceDecl::protocol_iterator
252         I = InterfD->protocol_begin(),
253         E = InterfD->protocol_end(); I != E; ++I) {
254       containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial);
255     }
256 
257     // Visit all categories in case the invalidation method is declared in
258     // a category.
259     for (ObjCInterfaceDecl::visible_extensions_iterator
260            Ext = InterfD->visible_extensions_begin(),
261            ExtEnd = InterfD->visible_extensions_end();
262          Ext != ExtEnd; ++Ext) {
263       containsInvalidationMethod(*Ext, OutInfo, Partial);
264     }
265 
266     containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
267     return;
268   }
269 
270   // If protocol, check all parent protocols.
271   if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
272     for (ObjCInterfaceDecl::protocol_iterator
273         I = ProtD->protocol_begin(),
274         E = ProtD->protocol_end(); I != E; ++I) {
275       containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial);
276     }
277     return;
278   }
279 
280   return;
281 }
282 
283 bool IvarInvalidationChecker::trackIvar(const ObjCIvarDecl *Iv,
284                                         IvarSet &TrackedIvars,
285                                         const ObjCIvarDecl **FirstIvarDecl) {
286   QualType IvQTy = Iv->getType();
287   const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
288   if (!IvTy)
289     return false;
290   const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
291 
292   InvalidationInfo Info;
293   containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
294   if (Info.needsInvalidation()) {
295     const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
296     TrackedIvars[I] = Info;
297     if (!*FirstIvarDecl)
298       *FirstIvarDecl = I;
299     return true;
300   }
301   return false;
302 }
303 
304 const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar(
305                         const ObjCPropertyDecl *Prop,
306                         const ObjCInterfaceDecl *InterfaceD,
307                         IvarSet &TrackedIvars,
308                         const ObjCIvarDecl **FirstIvarDecl) {
309   const ObjCIvarDecl *IvarD = 0;
310 
311   // Lookup for the synthesized case.
312   IvarD = Prop->getPropertyIvarDecl();
313   // We only track the ivars/properties that are defined in the current
314   // class (not the parent).
315   if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
316     if (TrackedIvars.count(IvarD)) {
317       return IvarD;
318     }
319     // If the ivar is synthesized we still want to track it.
320     if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
321       return IvarD;
322   }
323 
324   // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
325   StringRef PropName = Prop->getIdentifier()->getName();
326   for (IvarSet::const_iterator I = TrackedIvars.begin(),
327                                E = TrackedIvars.end(); I != E; ++I) {
328     const ObjCIvarDecl *Iv = I->first;
329     StringRef IvarName = Iv->getName();
330 
331     if (IvarName == PropName)
332       return Iv;
333 
334     SmallString<128> PropNameWithUnderscore;
335     {
336       llvm::raw_svector_ostream os(PropNameWithUnderscore);
337       os << '_' << PropName;
338     }
339     if (IvarName == PropNameWithUnderscore.str())
340       return Iv;
341   }
342 
343   // Note, this is a possible source of false positives. We could look at the
344   // getter implementation to find the ivar when its name is not derived from
345   // the property name.
346   return 0;
347 }
348 
349 void IvarInvalidationChecker::printIvar(llvm::raw_svector_ostream &os,
350                                       const ObjCIvarDecl *IvarDecl,
351                                       const IvarToPropMapTy &IvarToPopertyMap) {
352   if (IvarDecl->getSynthesize()) {
353     const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
354     assert(PD &&"Do we synthesize ivars for something other than properties?");
355     os << "Property "<< PD->getName() << " ";
356   } else {
357     os << "Instance variable "<< IvarDecl->getName() << " ";
358   }
359 }
360 
361 // Check that the invalidatable interfaces with ivars/properties implement the
362 // invalidation methods.
363 void IvarInvalidationChecker::checkASTDecl(const ObjCImplementationDecl *ImplD,
364                                           AnalysisManager& Mgr,
365                                           BugReporter &BR) const {
366   // Collect all ivars that need cleanup.
367   IvarSet Ivars;
368   // Record the first Ivar needing invalidation; used in reporting when only
369   // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
370   // deterministic output.
371   const ObjCIvarDecl *FirstIvarDecl = 0;
372   const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
373 
374   // Collect ivars declared in this class, its extensions and its implementation
375   ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
376   for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
377        Iv= Iv->getNextIvar())
378     trackIvar(Iv, Ivars, &FirstIvarDecl);
379 
380   // Construct Property/Property Accessor to Ivar maps to assist checking if an
381   // ivar which is backing a property has been reset.
382   MethToIvarMapTy PropSetterToIvarMap;
383   MethToIvarMapTy PropGetterToIvarMap;
384   PropToIvarMapTy PropertyToIvarMap;
385   IvarToPropMapTy IvarToPopertyMap;
386 
387   ObjCInterfaceDecl::PropertyMap PropMap;
388   InterfaceD->collectPropertiesToImplement(PropMap);
389 
390   for (ObjCInterfaceDecl::PropertyMap::iterator
391       I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
392     const ObjCPropertyDecl *PD = I->second;
393 
394     const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
395                                                      &FirstIvarDecl);
396     if (!ID)
397       continue;
398 
399     // Store the mappings.
400     PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
401     PropertyToIvarMap[PD] = ID;
402     IvarToPopertyMap[ID] = PD;
403 
404     // Find the setter and the getter.
405     const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
406     if (SetterD) {
407       SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
408       PropSetterToIvarMap[SetterD] = ID;
409     }
410 
411     const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
412     if (GetterD) {
413       GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl());
414       PropGetterToIvarMap[GetterD] = ID;
415     }
416   }
417 
418   // If no ivars need invalidation, there is nothing to check here.
419   if (Ivars.empty())
420     return;
421 
422   // Find all partial invalidation methods.
423   InvalidationInfo PartialInfo;
424   containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true);
425 
426   // Remove ivars invalidated by the partial invalidation methods. They do not
427   // need to be invalidated in the regular invalidation methods.
428   for (MethodSet::iterator
429       I = PartialInfo.InvalidationMethods.begin(),
430       E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
431     const ObjCMethodDecl *InterfD = *I;
432 
433     // Get the corresponding method in the @implementation.
434     const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
435                                                InterfD->isInstanceMethod());
436     if (D && D->hasBody()) {
437       bool CalledAnotherInvalidationMethod = false;
438       // The MethodCrowler is going to remove the invalidated ivars.
439       MethodCrawler(Ivars,
440                     CalledAnotherInvalidationMethod,
441                     PropSetterToIvarMap,
442                     PropGetterToIvarMap,
443                     PropertyToIvarMap,
444                     BR.getContext()).VisitStmt(D->getBody());
445       // If another invalidation method was called, trust that full invalidation
446       // has occurred.
447       if (CalledAnotherInvalidationMethod)
448         Ivars.clear();
449     }
450   }
451 
452   // If all ivars have been invalidated by partial invalidators, there is
453   // nothing to check here.
454   if (Ivars.empty())
455     return;
456 
457   // Find all invalidation methods in this @interface declaration and parents.
458   InvalidationInfo Info;
459   containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
460 
461   // Report an error in case none of the invalidation methods are declared.
462   if (!Info.needsInvalidation()) {
463     reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, BR,
464                                /*MissingDeclaration*/ true);
465     return;
466   }
467 
468   // Check that all ivars are invalidated by the invalidation methods.
469   bool AtImplementationContainsAtLeastOneInvalidationMethod = false;
470   for (MethodSet::iterator I = Info.InvalidationMethods.begin(),
471                            E = Info.InvalidationMethods.end(); I != E; ++I) {
472     const ObjCMethodDecl *InterfD = *I;
473 
474     // Get the corresponding method in the @implementation.
475     const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
476                                                InterfD->isInstanceMethod());
477     if (D && D->hasBody()) {
478       AtImplementationContainsAtLeastOneInvalidationMethod = true;
479 
480       // Get a copy of ivars needing invalidation.
481       IvarSet IvarsI = Ivars;
482 
483       bool CalledAnotherInvalidationMethod = false;
484       MethodCrawler(IvarsI,
485                     CalledAnotherInvalidationMethod,
486                     PropSetterToIvarMap,
487                     PropGetterToIvarMap,
488                     PropertyToIvarMap,
489                     BR.getContext()).VisitStmt(D->getBody());
490       // If another invalidation method was called, trust that full invalidation
491       // has occurred.
492       if (CalledAnotherInvalidationMethod)
493         continue;
494 
495       // Warn on the ivars that were not invalidated by the method.
496       for (IvarSet::const_iterator
497           I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I)
498         reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D, Mgr, BR);
499     }
500   }
501 
502   // Report an error in case none of the invalidation methods are implemented.
503   if (!AtImplementationContainsAtLeastOneInvalidationMethod)
504     reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, BR,
505                                /*MissingDeclaration*/ false);
506 }
507 
508 void IvarInvalidationChecker::
509 reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
510                            const IvarToPropMapTy &IvarToPopertyMap,
511                            const ObjCInterfaceDecl *InterfaceD,
512                            BugReporter &BR,
513                            bool MissingDeclaration) {
514   SmallString<128> sbuf;
515   llvm::raw_svector_ostream os(sbuf);
516   assert(FirstIvarDecl);
517   printIvar(os, FirstIvarDecl, IvarToPopertyMap);
518   os << "needs to be invalidated; ";
519   if (MissingDeclaration)
520     os << "no invalidation method is declared for ";
521   else
522     os << "no invalidation method is defined in the @implementation for ";
523   os << InterfaceD->getName();
524 
525   PathDiagnosticLocation IvarDecLocation =
526     PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
527 
528   BR.EmitBasicReport(FirstIvarDecl, "Incomplete invalidation",
529                      categories::CoreFoundationObjectiveC, os.str(),
530                      IvarDecLocation);
531 }
532 
533 void IvarInvalidationChecker::
534 reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
535                                     const IvarToPropMapTy &IvarToPopertyMap,
536                                     const ObjCMethodDecl *MethodD,
537                                     AnalysisManager& Mgr,
538                                     BugReporter &BR) {
539   SmallString<128> sbuf;
540   llvm::raw_svector_ostream os(sbuf);
541   printIvar(os, IvarD, IvarToPopertyMap);
542   os << "needs to be invalidated or set to nil";
543   PathDiagnosticLocation MethodDecLocation =
544                          PathDiagnosticLocation::createEnd(MethodD->getBody(),
545                          BR.getSourceManager(),
546                          Mgr.getAnalysisDeclContext(MethodD));
547   BR.EmitBasicReport(MethodD, "Incomplete invalidation",
548                      categories::CoreFoundationObjectiveC, os.str(),
549                      MethodDecLocation);
550 }
551 
552 void IvarInvalidationChecker::MethodCrawler::markInvalidated(
553     const ObjCIvarDecl *Iv) {
554   IvarSet::iterator I = IVars.find(Iv);
555   if (I != IVars.end()) {
556     // If InvalidationMethod is present, we are processing the message send and
557     // should ensure we are invalidating with the appropriate method,
558     // otherwise, we are processing setting to 'nil'.
559     if (!InvalidationMethod ||
560         (InvalidationMethod && I->second.hasMethod(InvalidationMethod)))
561       IVars.erase(I);
562   }
563 }
564 
565 const Expr *IvarInvalidationChecker::MethodCrawler::peel(const Expr *E) const {
566   E = E->IgnoreParenCasts();
567   if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
568     E = POE->getSyntacticForm()->IgnoreParenCasts();
569   if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
570     E = OVE->getSourceExpr()->IgnoreParenCasts();
571   return E;
572 }
573 
574 void IvarInvalidationChecker::MethodCrawler::checkObjCIvarRefExpr(
575     const ObjCIvarRefExpr *IvarRef) {
576   if (const Decl *D = IvarRef->getDecl())
577     markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
578 }
579 
580 void IvarInvalidationChecker::MethodCrawler::checkObjCMessageExpr(
581     const ObjCMessageExpr *ME) {
582   const ObjCMethodDecl *MD = ME->getMethodDecl();
583   if (MD) {
584     MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
585     MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
586     if (IvI != PropertyGetterToIvarMap.end())
587       markInvalidated(IvI->second);
588   }
589 }
590 
591 void IvarInvalidationChecker::MethodCrawler::checkObjCPropertyRefExpr(
592     const ObjCPropertyRefExpr *PA) {
593 
594   if (PA->isExplicitProperty()) {
595     const ObjCPropertyDecl *PD = PA->getExplicitProperty();
596     if (PD) {
597       PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
598       PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
599       if (IvI != PropertyToIvarMap.end())
600         markInvalidated(IvI->second);
601       return;
602     }
603   }
604 
605   if (PA->isImplicitProperty()) {
606     const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
607     if (MD) {
608       MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
609       MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
610       if (IvI != PropertyGetterToIvarMap.end())
611         markInvalidated(IvI->second);
612       return;
613     }
614   }
615 }
616 
617 bool IvarInvalidationChecker::MethodCrawler::isZero(const Expr *E) const {
618   E = peel(E);
619 
620   return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
621            != Expr::NPCK_NotNull);
622 }
623 
624 void IvarInvalidationChecker::MethodCrawler::check(const Expr *E) {
625   E = peel(E);
626 
627   if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
628     checkObjCIvarRefExpr(IvarRef);
629     return;
630   }
631 
632   if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
633     checkObjCPropertyRefExpr(PropRef);
634     return;
635   }
636 
637   if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
638     checkObjCMessageExpr(MsgExpr);
639     return;
640   }
641 }
642 
643 void IvarInvalidationChecker::MethodCrawler::VisitBinaryOperator(
644     const BinaryOperator *BO) {
645   VisitStmt(BO);
646 
647   // Do we assign/compare against zero? If yes, check the variable we are
648   // assigning to.
649   BinaryOperatorKind Opcode = BO->getOpcode();
650   if (Opcode != BO_Assign &&
651       Opcode != BO_EQ &&
652       Opcode != BO_NE)
653     return;
654 
655   if (isZero(BO->getRHS())) {
656       check(BO->getLHS());
657       return;
658   }
659 
660   if (Opcode != BO_Assign && isZero(BO->getLHS())) {
661     check(BO->getRHS());
662     return;
663   }
664 }
665 
666 void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr(
667   const ObjCMessageExpr *ME) {
668   const ObjCMethodDecl *MD = ME->getMethodDecl();
669   const Expr *Receiver = ME->getInstanceReceiver();
670 
671   // Stop if we are calling '[self invalidate]'.
672   if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false))
673     if (Receiver->isObjCSelfExpr()) {
674       CalledAnotherInvalidationMethod = true;
675       return;
676     }
677 
678   // Check if we call a setter and set the property to 'nil'.
679   if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
680     MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
681     MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
682     if (IvI != PropertySetterToIvarMap.end()) {
683       markInvalidated(IvI->second);
684       return;
685     }
686   }
687 
688   // Check if we call the 'invalidation' routine on the ivar.
689   if (Receiver) {
690     InvalidationMethod = MD;
691     check(Receiver->IgnoreParenCasts());
692     InvalidationMethod = 0;
693   }
694 
695   VisitStmt(ME);
696 }
697 }
698 
699 // Register the checker.
700 void ento::registerIvarInvalidationChecker(CheckerManager &mgr) {
701   mgr.registerChecker<IvarInvalidationChecker>();
702 }
703