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