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