clang API Documentation

IvarInvalidationChecker.cpp
Go to the documentation of this file.
00001 //=- IvarInvalidationChecker.cpp - -*- C++ -------------------------------*-==//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 //  This checker implements annotation driven invalidation checking. If a class
00011 //  contains a method annotated with 'objc_instance_variable_invalidator',
00012 //  - (void) foo
00013 //           __attribute__((annotate("objc_instance_variable_invalidator")));
00014 //  all the "ivalidatable" instance variables of this class should be
00015 //  invalidated. We call an instance variable ivalidatable if it is an object of
00016 //  a class which contains an invalidation method. There could be multiple
00017 //  methods annotated with such annotations per class, either one can be used
00018 //  to invalidate the ivar. An ivar or property are considered to be
00019 //  invalidated if they are being assigned 'nil' or an invalidation method has
00020 //  been called on them. An invalidation method should either invalidate all
00021 //  the ivars or call another invalidation method (on self).
00022 //
00023 //  Partial invalidor annotation allows to addess cases when ivars are 
00024 //  invalidated by other methods, which might or might not be called from 
00025 //  the invalidation method. The checker checks that each invalidation
00026 //  method and all the partial methods cumulatively invalidate all ivars.
00027 //    __attribute__((annotate("objc_instance_variable_invalidator_partial")));
00028 //
00029 //===----------------------------------------------------------------------===//
00030 
00031 #include "ClangSACheckers.h"
00032 #include "clang/AST/Attr.h"
00033 #include "clang/AST/DeclObjC.h"
00034 #include "clang/AST/StmtVisitor.h"
00035 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
00036 #include "clang/StaticAnalyzer/Core/Checker.h"
00037 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
00038 #include "llvm/ADT/DenseMap.h"
00039 #include "llvm/ADT/SetVector.h"
00040 #include "llvm/ADT/SmallString.h"
00041 
00042 using namespace clang;
00043 using namespace ento;
00044 
00045 namespace {
00046 
00047 struct ChecksFilter {
00048   /// Check for missing invalidation method declarations.
00049   DefaultBool check_MissingInvalidationMethod;
00050   /// Check that all ivars are invalidated.
00051   DefaultBool check_InstanceVariableInvalidation;
00052 
00053   CheckName checkName_MissingInvalidationMethod;
00054   CheckName checkName_InstanceVariableInvalidation;
00055 };
00056 
00057 class IvarInvalidationCheckerImpl {
00058 
00059   typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
00060   typedef llvm::DenseMap<const ObjCMethodDecl*,
00061                          const ObjCIvarDecl*> MethToIvarMapTy;
00062   typedef llvm::DenseMap<const ObjCPropertyDecl*,
00063                          const ObjCIvarDecl*> PropToIvarMapTy;
00064   typedef llvm::DenseMap<const ObjCIvarDecl*,
00065                          const ObjCPropertyDecl*> IvarToPropMapTy;
00066 
00067 
00068   struct InvalidationInfo {
00069     /// Has the ivar been invalidated?
00070     bool IsInvalidated;
00071 
00072     /// The methods which can be used to invalidate the ivar.
00073     MethodSet InvalidationMethods;
00074 
00075     InvalidationInfo() : IsInvalidated(false) {}
00076     void addInvalidationMethod(const ObjCMethodDecl *MD) {
00077       InvalidationMethods.insert(MD);
00078     }
00079 
00080     bool needsInvalidation() const {
00081       return !InvalidationMethods.empty();
00082     }
00083 
00084     bool hasMethod(const ObjCMethodDecl *MD) {
00085       if (IsInvalidated)
00086         return true;
00087       for (MethodSet::iterator I = InvalidationMethods.begin(),
00088           E = InvalidationMethods.end(); I != E; ++I) {
00089         if (*I == MD) {
00090           IsInvalidated = true;
00091           return true;
00092         }
00093       }
00094       return false;
00095     }
00096   };
00097 
00098   typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
00099 
00100   /// Statement visitor, which walks the method body and flags the ivars
00101   /// referenced in it (either directly or via property).
00102   class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
00103     /// The set of Ivars which need to be invalidated.
00104     IvarSet &IVars;
00105 
00106     /// Flag is set as the result of a message send to another
00107     /// invalidation method.
00108     bool &CalledAnotherInvalidationMethod;
00109 
00110     /// Property setter to ivar mapping.
00111     const MethToIvarMapTy &PropertySetterToIvarMap;
00112 
00113     /// Property getter to ivar mapping.
00114     const MethToIvarMapTy &PropertyGetterToIvarMap;
00115 
00116     /// Property to ivar mapping.
00117     const PropToIvarMapTy &PropertyToIvarMap;
00118 
00119     /// The invalidation method being currently processed.
00120     const ObjCMethodDecl *InvalidationMethod;
00121 
00122     ASTContext &Ctx;
00123 
00124     /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
00125     const Expr *peel(const Expr *E) const;
00126 
00127     /// Does this expression represent zero: '0'?
00128     bool isZero(const Expr *E) const;
00129 
00130     /// Mark the given ivar as invalidated.
00131     void markInvalidated(const ObjCIvarDecl *Iv);
00132 
00133     /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
00134     /// invalidated.
00135     void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
00136 
00137     /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
00138     /// it as invalidated.
00139     void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
00140 
00141     /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
00142     /// if yes, marks it as invalidated.
00143     void checkObjCMessageExpr(const ObjCMessageExpr *ME);
00144 
00145     /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
00146     void check(const Expr *E);
00147 
00148   public:
00149     MethodCrawler(IvarSet &InIVars,
00150                   bool &InCalledAnotherInvalidationMethod,
00151                   const MethToIvarMapTy &InPropertySetterToIvarMap,
00152                   const MethToIvarMapTy &InPropertyGetterToIvarMap,
00153                   const PropToIvarMapTy &InPropertyToIvarMap,
00154                   ASTContext &InCtx)
00155     : IVars(InIVars),
00156       CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
00157       PropertySetterToIvarMap(InPropertySetterToIvarMap),
00158       PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
00159       PropertyToIvarMap(InPropertyToIvarMap),
00160       InvalidationMethod(nullptr),
00161       Ctx(InCtx) {}
00162 
00163     void VisitStmt(const Stmt *S) { VisitChildren(S); }
00164 
00165     void VisitBinaryOperator(const BinaryOperator *BO);
00166 
00167     void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
00168 
00169     void VisitChildren(const Stmt *S) {
00170       for (Stmt::const_child_range I = S->children(); I; ++I) {
00171         if (*I)
00172           this->Visit(*I);
00173         if (CalledAnotherInvalidationMethod)
00174           return;
00175       }
00176     }
00177   };
00178 
00179   /// Check if the any of the methods inside the interface are annotated with
00180   /// the invalidation annotation, update the IvarInfo accordingly.
00181   /// \param LookForPartial is set when we are searching for partial
00182   ///        invalidators.
00183   static void containsInvalidationMethod(const ObjCContainerDecl *D,
00184                                          InvalidationInfo &Out,
00185                                          bool LookForPartial);
00186 
00187   /// Check if ivar should be tracked and add to TrackedIvars if positive.
00188   /// Returns true if ivar should be tracked.
00189   static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
00190                         const ObjCIvarDecl **FirstIvarDecl);
00191 
00192   /// Given the property declaration, and the list of tracked ivars, finds
00193   /// the ivar backing the property when possible. Returns '0' when no such
00194   /// ivar could be found.
00195   static const ObjCIvarDecl *findPropertyBackingIvar(
00196       const ObjCPropertyDecl *Prop,
00197       const ObjCInterfaceDecl *InterfaceD,
00198       IvarSet &TrackedIvars,
00199       const ObjCIvarDecl **FirstIvarDecl);
00200 
00201   /// Print ivar name or the property if the given ivar backs a property.
00202   static void printIvar(llvm::raw_svector_ostream &os,
00203                         const ObjCIvarDecl *IvarDecl,
00204                         const IvarToPropMapTy &IvarToPopertyMap);
00205 
00206   void reportNoInvalidationMethod(CheckName CheckName,
00207                                   const ObjCIvarDecl *FirstIvarDecl,
00208                                   const IvarToPropMapTy &IvarToPopertyMap,
00209                                   const ObjCInterfaceDecl *InterfaceD,
00210                                   bool MissingDeclaration) const;
00211   void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
00212                                    const IvarToPropMapTy &IvarToPopertyMap,
00213                                    const ObjCMethodDecl *MethodD) const;
00214 
00215   AnalysisManager& Mgr;
00216   BugReporter &BR;
00217   /// Filter on the checks performed.
00218   const ChecksFilter &Filter;
00219 
00220 public:
00221   IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
00222                               BugReporter &InBR,
00223                               const ChecksFilter &InFilter) :
00224     Mgr (InMgr), BR(InBR), Filter(InFilter) {}
00225 
00226   void visit(const ObjCImplementationDecl *D) const;
00227 };
00228 
00229 static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
00230   for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) {
00231     if (!LookForPartial &&
00232         Ann->getAnnotation() == "objc_instance_variable_invalidator")
00233       return true;
00234     if (LookForPartial &&
00235         Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
00236       return true;
00237   }
00238   return false;
00239 }
00240 
00241 void IvarInvalidationCheckerImpl::containsInvalidationMethod(
00242     const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
00243 
00244   if (!D)
00245     return;
00246 
00247   assert(!isa<ObjCImplementationDecl>(D));
00248   // TODO: Cache the results.
00249 
00250   // Check all methods.
00251   for (const auto *MDI : D->methods())
00252     if (isInvalidationMethod(MDI, Partial))
00253       OutInfo.addInvalidationMethod(
00254           cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
00255 
00256   // If interface, check all parent protocols and super.
00257   if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
00258 
00259     // Visit all protocols.
00260     for (const auto *I : InterfD->protocols())
00261       containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
00262 
00263     // Visit all categories in case the invalidation method is declared in
00264     // a category.
00265     for (const auto *Ext : InterfD->visible_extensions())
00266       containsInvalidationMethod(Ext, OutInfo, Partial);
00267 
00268     containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
00269     return;
00270   }
00271 
00272   // If protocol, check all parent protocols.
00273   if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
00274     for (const auto *I : ProtD->protocols()) {
00275       containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
00276     }
00277     return;
00278   }
00279 
00280   return;
00281 }
00282 
00283 bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv,
00284                                         IvarSet &TrackedIvars,
00285                                         const ObjCIvarDecl **FirstIvarDecl) {
00286   QualType IvQTy = Iv->getType();
00287   const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
00288   if (!IvTy)
00289     return false;
00290   const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
00291 
00292   InvalidationInfo Info;
00293   containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
00294   if (Info.needsInvalidation()) {
00295     const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
00296     TrackedIvars[I] = Info;
00297     if (!*FirstIvarDecl)
00298       *FirstIvarDecl = I;
00299     return true;
00300   }
00301   return false;
00302 }
00303 
00304 const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
00305                         const ObjCPropertyDecl *Prop,
00306                         const ObjCInterfaceDecl *InterfaceD,
00307                         IvarSet &TrackedIvars,
00308                         const ObjCIvarDecl **FirstIvarDecl) {
00309   const ObjCIvarDecl *IvarD = nullptr;
00310 
00311   // Lookup for the synthesized case.
00312   IvarD = Prop->getPropertyIvarDecl();
00313   // We only track the ivars/properties that are defined in the current 
00314   // class (not the parent).
00315   if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
00316     if (TrackedIvars.count(IvarD)) {
00317       return IvarD;
00318     }
00319     // If the ivar is synthesized we still want to track it.
00320     if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
00321       return IvarD;
00322   }
00323 
00324   // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
00325   StringRef PropName = Prop->getIdentifier()->getName();
00326   for (IvarSet::const_iterator I = TrackedIvars.begin(),
00327                                E = TrackedIvars.end(); I != E; ++I) {
00328     const ObjCIvarDecl *Iv = I->first;
00329     StringRef IvarName = Iv->getName();
00330 
00331     if (IvarName == PropName)
00332       return Iv;
00333 
00334     SmallString<128> PropNameWithUnderscore;
00335     {
00336       llvm::raw_svector_ostream os(PropNameWithUnderscore);
00337       os << '_' << PropName;
00338     }
00339     if (IvarName == PropNameWithUnderscore.str())
00340       return Iv;
00341   }
00342 
00343   // Note, this is a possible source of false positives. We could look at the
00344   // getter implementation to find the ivar when its name is not derived from
00345   // the property name.
00346   return nullptr;
00347 }
00348 
00349 void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
00350                                       const ObjCIvarDecl *IvarDecl,
00351                                       const IvarToPropMapTy &IvarToPopertyMap) {
00352   if (IvarDecl->getSynthesize()) {
00353     const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
00354     assert(PD &&"Do we synthesize ivars for something other than properties?");
00355     os << "Property "<< PD->getName() << " ";
00356   } else {
00357     os << "Instance variable "<< IvarDecl->getName() << " ";
00358   }
00359 }
00360 
00361 // Check that the invalidatable interfaces with ivars/properties implement the
00362 // invalidation methods.
00363 void IvarInvalidationCheckerImpl::
00364 visit(const ObjCImplementationDecl *ImplD) const {
00365   // Collect all ivars that need cleanup.
00366   IvarSet Ivars;
00367   // Record the first Ivar needing invalidation; used in reporting when only
00368   // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
00369   // deterministic output.
00370   const ObjCIvarDecl *FirstIvarDecl = nullptr;
00371   const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
00372 
00373   // Collect ivars declared in this class, its extensions and its implementation
00374   ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
00375   for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
00376        Iv= Iv->getNextIvar())
00377     trackIvar(Iv, Ivars, &FirstIvarDecl);
00378 
00379   // Construct Property/Property Accessor to Ivar maps to assist checking if an
00380   // ivar which is backing a property has been reset.
00381   MethToIvarMapTy PropSetterToIvarMap;
00382   MethToIvarMapTy PropGetterToIvarMap;
00383   PropToIvarMapTy PropertyToIvarMap;
00384   IvarToPropMapTy IvarToPopertyMap;
00385 
00386   ObjCInterfaceDecl::PropertyMap PropMap;
00387   ObjCInterfaceDecl::PropertyDeclOrder PropOrder;
00388   InterfaceD->collectPropertiesToImplement(PropMap, PropOrder);
00389 
00390   for (ObjCInterfaceDecl::PropertyMap::iterator
00391       I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
00392     const ObjCPropertyDecl *PD = I->second;
00393 
00394     const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
00395                                                      &FirstIvarDecl);
00396     if (!ID)
00397       continue;
00398 
00399     // Store the mappings.
00400     PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
00401     PropertyToIvarMap[PD] = ID;
00402     IvarToPopertyMap[ID] = PD;
00403 
00404     // Find the setter and the getter.
00405     const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
00406     if (SetterD) {
00407       SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
00408       PropSetterToIvarMap[SetterD] = ID;
00409     }
00410 
00411     const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
00412     if (GetterD) {
00413       GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl());
00414       PropGetterToIvarMap[GetterD] = ID;
00415     }
00416   }
00417 
00418   // If no ivars need invalidation, there is nothing to check here.
00419   if (Ivars.empty())
00420     return;
00421 
00422   // Find all partial invalidation methods.
00423   InvalidationInfo PartialInfo;
00424   containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true);
00425 
00426   // Remove ivars invalidated by the partial invalidation methods. They do not
00427   // need to be invalidated in the regular invalidation methods.
00428   bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false;
00429   for (MethodSet::iterator
00430       I = PartialInfo.InvalidationMethods.begin(),
00431       E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
00432     const ObjCMethodDecl *InterfD = *I;
00433 
00434     // Get the corresponding method in the @implementation.
00435     const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
00436                                                InterfD->isInstanceMethod());
00437     if (D && D->hasBody()) {
00438       AtImplementationContainsAtLeastOnePartialInvalidationMethod = true;
00439 
00440       bool CalledAnotherInvalidationMethod = false;
00441       // The MethodCrowler is going to remove the invalidated ivars.
00442       MethodCrawler(Ivars,
00443                     CalledAnotherInvalidationMethod,
00444                     PropSetterToIvarMap,
00445                     PropGetterToIvarMap,
00446                     PropertyToIvarMap,
00447                     BR.getContext()).VisitStmt(D->getBody());
00448       // If another invalidation method was called, trust that full invalidation
00449       // has occurred.
00450       if (CalledAnotherInvalidationMethod)
00451         Ivars.clear();
00452     }
00453   }
00454 
00455   // If all ivars have been invalidated by partial invalidators, there is
00456   // nothing to check here.
00457   if (Ivars.empty())
00458     return;
00459 
00460   // Find all invalidation methods in this @interface declaration and parents.
00461   InvalidationInfo Info;
00462   containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
00463 
00464   // Report an error in case none of the invalidation methods are declared.
00465   if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
00466     if (Filter.check_MissingInvalidationMethod)
00467       reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
00468                                  FirstIvarDecl, IvarToPopertyMap, InterfaceD,
00469                                  /*MissingDeclaration*/ true);
00470     // If there are no invalidation methods, there is no ivar validation work
00471     // to be done.
00472     return;
00473   }
00474 
00475   // Only check if Ivars are invalidated when InstanceVariableInvalidation
00476   // has been requested.
00477   if (!Filter.check_InstanceVariableInvalidation)
00478     return;
00479 
00480   // Check that all ivars are invalidated by the invalidation methods.
00481   bool AtImplementationContainsAtLeastOneInvalidationMethod = false;
00482   for (MethodSet::iterator I = Info.InvalidationMethods.begin(),
00483                            E = Info.InvalidationMethods.end(); I != E; ++I) {
00484     const ObjCMethodDecl *InterfD = *I;
00485 
00486     // Get the corresponding method in the @implementation.
00487     const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
00488                                                InterfD->isInstanceMethod());
00489     if (D && D->hasBody()) {
00490       AtImplementationContainsAtLeastOneInvalidationMethod = true;
00491 
00492       // Get a copy of ivars needing invalidation.
00493       IvarSet IvarsI = Ivars;
00494 
00495       bool CalledAnotherInvalidationMethod = false;
00496       MethodCrawler(IvarsI,
00497                     CalledAnotherInvalidationMethod,
00498                     PropSetterToIvarMap,
00499                     PropGetterToIvarMap,
00500                     PropertyToIvarMap,
00501                     BR.getContext()).VisitStmt(D->getBody());
00502       // If another invalidation method was called, trust that full invalidation
00503       // has occurred.
00504       if (CalledAnotherInvalidationMethod)
00505         continue;
00506 
00507       // Warn on the ivars that were not invalidated by the method.
00508       for (IvarSet::const_iterator
00509           I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I)
00510         reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D);
00511     }
00512   }
00513 
00514   // Report an error in case none of the invalidation methods are implemented.
00515   if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
00516     if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
00517       // Warn on the ivars that were not invalidated by the prrtial
00518       // invalidation methods.
00519       for (IvarSet::const_iterator
00520            I = Ivars.begin(), E = Ivars.end(); I != E; ++I)
00521         reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, nullptr);
00522     } else {
00523       // Otherwise, no invalidation methods were implemented.
00524       reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
00525                                  FirstIvarDecl, IvarToPopertyMap, InterfaceD,
00526                                  /*MissingDeclaration*/ false);
00527     }
00528   }
00529 }
00530 
00531 void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
00532     CheckName CheckName, const ObjCIvarDecl *FirstIvarDecl,
00533     const IvarToPropMapTy &IvarToPopertyMap,
00534     const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const {
00535   SmallString<128> sbuf;
00536   llvm::raw_svector_ostream os(sbuf);
00537   assert(FirstIvarDecl);
00538   printIvar(os, FirstIvarDecl, IvarToPopertyMap);
00539   os << "needs to be invalidated; ";
00540   if (MissingDeclaration)
00541     os << "no invalidation method is declared for ";
00542   else
00543     os << "no invalidation method is defined in the @implementation for ";
00544   os << InterfaceD->getName();
00545 
00546   PathDiagnosticLocation IvarDecLocation =
00547     PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
00548 
00549   BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation",
00550                      categories::CoreFoundationObjectiveC, os.str(),
00551                      IvarDecLocation);
00552 }
00553 
00554 void IvarInvalidationCheckerImpl::
00555 reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
00556                             const IvarToPropMapTy &IvarToPopertyMap,
00557                             const ObjCMethodDecl *MethodD) const {
00558   SmallString<128> sbuf;
00559   llvm::raw_svector_ostream os(sbuf);
00560   printIvar(os, IvarD, IvarToPopertyMap);
00561   os << "needs to be invalidated or set to nil";
00562   if (MethodD) {
00563     PathDiagnosticLocation MethodDecLocation =
00564                            PathDiagnosticLocation::createEnd(MethodD->getBody(),
00565                            BR.getSourceManager(),
00566                            Mgr.getAnalysisDeclContext(MethodD));
00567     BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
00568                        "Incomplete invalidation",
00569                        categories::CoreFoundationObjectiveC, os.str(),
00570                        MethodDecLocation);
00571   } else {
00572     BR.EmitBasicReport(
00573         IvarD, Filter.checkName_InstanceVariableInvalidation,
00574         "Incomplete invalidation", categories::CoreFoundationObjectiveC,
00575         os.str(),
00576         PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager()));
00577   }
00578 }
00579 
00580 void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
00581     const ObjCIvarDecl *Iv) {
00582   IvarSet::iterator I = IVars.find(Iv);
00583   if (I != IVars.end()) {
00584     // If InvalidationMethod is present, we are processing the message send and
00585     // should ensure we are invalidating with the appropriate method,
00586     // otherwise, we are processing setting to 'nil'.
00587     if (!InvalidationMethod ||
00588         (InvalidationMethod && I->second.hasMethod(InvalidationMethod)))
00589       IVars.erase(I);
00590   }
00591 }
00592 
00593 const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
00594   E = E->IgnoreParenCasts();
00595   if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
00596     E = POE->getSyntacticForm()->IgnoreParenCasts();
00597   if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
00598     E = OVE->getSourceExpr()->IgnoreParenCasts();
00599   return E;
00600 }
00601 
00602 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
00603     const ObjCIvarRefExpr *IvarRef) {
00604   if (const Decl *D = IvarRef->getDecl())
00605     markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
00606 }
00607 
00608 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
00609     const ObjCMessageExpr *ME) {
00610   const ObjCMethodDecl *MD = ME->getMethodDecl();
00611   if (MD) {
00612     MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
00613     MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
00614     if (IvI != PropertyGetterToIvarMap.end())
00615       markInvalidated(IvI->second);
00616   }
00617 }
00618 
00619 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
00620     const ObjCPropertyRefExpr *PA) {
00621 
00622   if (PA->isExplicitProperty()) {
00623     const ObjCPropertyDecl *PD = PA->getExplicitProperty();
00624     if (PD) {
00625       PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
00626       PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
00627       if (IvI != PropertyToIvarMap.end())
00628         markInvalidated(IvI->second);
00629       return;
00630     }
00631   }
00632 
00633   if (PA->isImplicitProperty()) {
00634     const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
00635     if (MD) {
00636       MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
00637       MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
00638       if (IvI != PropertyGetterToIvarMap.end())
00639         markInvalidated(IvI->second);
00640       return;
00641     }
00642   }
00643 }
00644 
00645 bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
00646   E = peel(E);
00647 
00648   return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
00649            != Expr::NPCK_NotNull);
00650 }
00651 
00652 void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
00653   E = peel(E);
00654 
00655   if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
00656     checkObjCIvarRefExpr(IvarRef);
00657     return;
00658   }
00659 
00660   if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
00661     checkObjCPropertyRefExpr(PropRef);
00662     return;
00663   }
00664 
00665   if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
00666     checkObjCMessageExpr(MsgExpr);
00667     return;
00668   }
00669 }
00670 
00671 void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
00672     const BinaryOperator *BO) {
00673   VisitStmt(BO);
00674 
00675   // Do we assign/compare against zero? If yes, check the variable we are
00676   // assigning to.
00677   BinaryOperatorKind Opcode = BO->getOpcode();
00678   if (Opcode != BO_Assign &&
00679       Opcode != BO_EQ &&
00680       Opcode != BO_NE)
00681     return;
00682 
00683   if (isZero(BO->getRHS())) {
00684       check(BO->getLHS());
00685       return;
00686   }
00687 
00688   if (Opcode != BO_Assign && isZero(BO->getLHS())) {
00689     check(BO->getRHS());
00690     return;
00691   }
00692 }
00693 
00694 void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
00695   const ObjCMessageExpr *ME) {
00696   const ObjCMethodDecl *MD = ME->getMethodDecl();
00697   const Expr *Receiver = ME->getInstanceReceiver();
00698 
00699   // Stop if we are calling '[self invalidate]'.
00700   if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false))
00701     if (Receiver->isObjCSelfExpr()) {
00702       CalledAnotherInvalidationMethod = true;
00703       return;
00704     }
00705 
00706   // Check if we call a setter and set the property to 'nil'.
00707   if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
00708     MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
00709     MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
00710     if (IvI != PropertySetterToIvarMap.end()) {
00711       markInvalidated(IvI->second);
00712       return;
00713     }
00714   }
00715 
00716   // Check if we call the 'invalidation' routine on the ivar.
00717   if (Receiver) {
00718     InvalidationMethod = MD;
00719     check(Receiver->IgnoreParenCasts());
00720     InvalidationMethod = nullptr;
00721   }
00722 
00723   VisitStmt(ME);
00724 }
00725 }
00726 
00727 // Register the checkers.
00728 namespace {
00729 
00730 class IvarInvalidationChecker :
00731   public Checker<check::ASTDecl<ObjCImplementationDecl> > {
00732 public:
00733   ChecksFilter Filter;
00734 public:
00735   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
00736                     BugReporter &BR) const {
00737     IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
00738     Walker.visit(D);
00739   }
00740 };
00741 }
00742 
00743 #define REGISTER_CHECKER(name)                                                 \
00744   void ento::register##name(CheckerManager &mgr) {                             \
00745     IvarInvalidationChecker *checker =                                         \
00746         mgr.registerChecker<IvarInvalidationChecker>();                        \
00747     checker->Filter.check_##name = true;                                       \
00748     checker->Filter.checkName_##name = mgr.getCurrentCheckName();              \
00749   }
00750 
00751 REGISTER_CHECKER(InstanceVariableInvalidation)
00752 REGISTER_CHECKER(MissingInvalidationMethod)
00753