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