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