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