xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
10b57cec5SDimitry Andric //===- IvarInvalidationChecker.cpp ------------------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric //  This checker implements annotation driven invalidation checking. If a class
100b57cec5SDimitry Andric //  contains a method annotated with 'objc_instance_variable_invalidator',
110b57cec5SDimitry Andric //  - (void) foo
120b57cec5SDimitry Andric //           __attribute__((annotate("objc_instance_variable_invalidator")));
130b57cec5SDimitry Andric //  all the "ivalidatable" instance variables of this class should be
140b57cec5SDimitry Andric //  invalidated. We call an instance variable ivalidatable if it is an object of
150b57cec5SDimitry Andric //  a class which contains an invalidation method. There could be multiple
160b57cec5SDimitry Andric //  methods annotated with such annotations per class, either one can be used
170b57cec5SDimitry Andric //  to invalidate the ivar. An ivar or property are considered to be
180b57cec5SDimitry Andric //  invalidated if they are being assigned 'nil' or an invalidation method has
190b57cec5SDimitry Andric //  been called on them. An invalidation method should either invalidate all
200b57cec5SDimitry Andric //  the ivars or call another invalidation method (on self).
210b57cec5SDimitry Andric //
220b57cec5SDimitry Andric //  Partial invalidor annotation allows to address cases when ivars are
230b57cec5SDimitry Andric //  invalidated by other methods, which might or might not be called from
240b57cec5SDimitry Andric //  the invalidation method. The checker checks that each invalidation
250b57cec5SDimitry Andric //  method and all the partial methods cumulatively invalidate all ivars.
260b57cec5SDimitry Andric //    __attribute__((annotate("objc_instance_variable_invalidator_partial")));
270b57cec5SDimitry Andric //
280b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric #include "clang/AST/Attr.h"
310b57cec5SDimitry Andric #include "clang/AST/DeclObjC.h"
320b57cec5SDimitry Andric #include "clang/AST/StmtVisitor.h"
3306c3fb27SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
340b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
350b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
360b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
370b57cec5SDimitry Andric #include "llvm/ADT/DenseMap.h"
3806c3fb27SDimitry Andric #include "llvm/ADT/STLExtras.h"
390b57cec5SDimitry Andric #include "llvm/ADT/SetVector.h"
400b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
410b57cec5SDimitry Andric 
420b57cec5SDimitry Andric using namespace clang;
430b57cec5SDimitry Andric using namespace ento;
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric namespace {
460b57cec5SDimitry Andric struct ChecksFilter {
470b57cec5SDimitry Andric   /// Check for missing invalidation method declarations.
4881ad6265SDimitry Andric   bool check_MissingInvalidationMethod = false;
490b57cec5SDimitry Andric   /// Check that all ivars are invalidated.
5081ad6265SDimitry Andric   bool check_InstanceVariableInvalidation = false;
510b57cec5SDimitry Andric 
52a7dea167SDimitry Andric   CheckerNameRef checkName_MissingInvalidationMethod;
53a7dea167SDimitry Andric   CheckerNameRef checkName_InstanceVariableInvalidation;
540b57cec5SDimitry Andric };
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric class IvarInvalidationCheckerImpl {
570b57cec5SDimitry Andric   typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
580b57cec5SDimitry Andric   typedef llvm::DenseMap<const ObjCMethodDecl*,
590b57cec5SDimitry Andric                          const ObjCIvarDecl*> MethToIvarMapTy;
600b57cec5SDimitry Andric   typedef llvm::DenseMap<const ObjCPropertyDecl*,
610b57cec5SDimitry Andric                          const ObjCIvarDecl*> PropToIvarMapTy;
620b57cec5SDimitry Andric   typedef llvm::DenseMap<const ObjCIvarDecl*,
630b57cec5SDimitry Andric                          const ObjCPropertyDecl*> IvarToPropMapTy;
640b57cec5SDimitry Andric 
650b57cec5SDimitry Andric   struct InvalidationInfo {
660b57cec5SDimitry Andric     /// Has the ivar been invalidated?
67*5f757f3fSDimitry Andric     bool IsInvalidated = false;
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric     /// The methods which can be used to invalidate the ivar.
700b57cec5SDimitry Andric     MethodSet InvalidationMethods;
710b57cec5SDimitry Andric 
72*5f757f3fSDimitry Andric     InvalidationInfo() = default;
addInvalidationMethod__anon7be946890111::IvarInvalidationCheckerImpl::InvalidationInfo730b57cec5SDimitry Andric     void addInvalidationMethod(const ObjCMethodDecl *MD) {
740b57cec5SDimitry Andric       InvalidationMethods.insert(MD);
750b57cec5SDimitry Andric     }
760b57cec5SDimitry Andric 
needsInvalidation__anon7be946890111::IvarInvalidationCheckerImpl::InvalidationInfo770b57cec5SDimitry Andric     bool needsInvalidation() const {
780b57cec5SDimitry Andric       return !InvalidationMethods.empty();
790b57cec5SDimitry Andric     }
800b57cec5SDimitry Andric 
hasMethod__anon7be946890111::IvarInvalidationCheckerImpl::InvalidationInfo810b57cec5SDimitry Andric     bool hasMethod(const ObjCMethodDecl *MD) {
820b57cec5SDimitry Andric       if (IsInvalidated)
830b57cec5SDimitry Andric         return true;
8406c3fb27SDimitry Andric       for (const ObjCMethodDecl *Curr : InvalidationMethods) {
8506c3fb27SDimitry Andric         if (Curr == MD) {
860b57cec5SDimitry Andric           IsInvalidated = true;
870b57cec5SDimitry Andric           return true;
880b57cec5SDimitry Andric         }
890b57cec5SDimitry Andric       }
900b57cec5SDimitry Andric       return false;
910b57cec5SDimitry Andric     }
920b57cec5SDimitry Andric   };
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric   typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
950b57cec5SDimitry Andric 
960b57cec5SDimitry Andric   /// Statement visitor, which walks the method body and flags the ivars
970b57cec5SDimitry Andric   /// referenced in it (either directly or via property).
980b57cec5SDimitry Andric   class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
990b57cec5SDimitry Andric     /// The set of Ivars which need to be invalidated.
1000b57cec5SDimitry Andric     IvarSet &IVars;
1010b57cec5SDimitry Andric 
1020b57cec5SDimitry Andric     /// Flag is set as the result of a message send to another
1030b57cec5SDimitry Andric     /// invalidation method.
1040b57cec5SDimitry Andric     bool &CalledAnotherInvalidationMethod;
1050b57cec5SDimitry Andric 
1060b57cec5SDimitry Andric     /// Property setter to ivar mapping.
1070b57cec5SDimitry Andric     const MethToIvarMapTy &PropertySetterToIvarMap;
1080b57cec5SDimitry Andric 
1090b57cec5SDimitry Andric     /// Property getter to ivar mapping.
1100b57cec5SDimitry Andric     const MethToIvarMapTy &PropertyGetterToIvarMap;
1110b57cec5SDimitry Andric 
1120b57cec5SDimitry Andric     /// Property to ivar mapping.
1130b57cec5SDimitry Andric     const PropToIvarMapTy &PropertyToIvarMap;
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric     /// The invalidation method being currently processed.
1160b57cec5SDimitry Andric     const ObjCMethodDecl *InvalidationMethod;
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric     ASTContext &Ctx;
1190b57cec5SDimitry Andric 
1200b57cec5SDimitry Andric     /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
1210b57cec5SDimitry Andric     const Expr *peel(const Expr *E) const;
1220b57cec5SDimitry Andric 
1230b57cec5SDimitry Andric     /// Does this expression represent zero: '0'?
1240b57cec5SDimitry Andric     bool isZero(const Expr *E) const;
1250b57cec5SDimitry Andric 
1260b57cec5SDimitry Andric     /// Mark the given ivar as invalidated.
1270b57cec5SDimitry Andric     void markInvalidated(const ObjCIvarDecl *Iv);
1280b57cec5SDimitry Andric 
1290b57cec5SDimitry Andric     /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
1300b57cec5SDimitry Andric     /// invalidated.
1310b57cec5SDimitry Andric     void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric     /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
1340b57cec5SDimitry Andric     /// it as invalidated.
1350b57cec5SDimitry Andric     void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric     /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
1380b57cec5SDimitry Andric     /// if yes, marks it as invalidated.
1390b57cec5SDimitry Andric     void checkObjCMessageExpr(const ObjCMessageExpr *ME);
1400b57cec5SDimitry Andric 
1410b57cec5SDimitry Andric     /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
1420b57cec5SDimitry Andric     void check(const Expr *E);
1430b57cec5SDimitry Andric 
1440b57cec5SDimitry Andric   public:
MethodCrawler(IvarSet & InIVars,bool & InCalledAnotherInvalidationMethod,const MethToIvarMapTy & InPropertySetterToIvarMap,const MethToIvarMapTy & InPropertyGetterToIvarMap,const PropToIvarMapTy & InPropertyToIvarMap,ASTContext & InCtx)1450b57cec5SDimitry Andric     MethodCrawler(IvarSet &InIVars,
1460b57cec5SDimitry Andric                   bool &InCalledAnotherInvalidationMethod,
1470b57cec5SDimitry Andric                   const MethToIvarMapTy &InPropertySetterToIvarMap,
1480b57cec5SDimitry Andric                   const MethToIvarMapTy &InPropertyGetterToIvarMap,
1490b57cec5SDimitry Andric                   const PropToIvarMapTy &InPropertyToIvarMap,
1500b57cec5SDimitry Andric                   ASTContext &InCtx)
1510b57cec5SDimitry Andric     : IVars(InIVars),
1520b57cec5SDimitry Andric       CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
1530b57cec5SDimitry Andric       PropertySetterToIvarMap(InPropertySetterToIvarMap),
1540b57cec5SDimitry Andric       PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
1550b57cec5SDimitry Andric       PropertyToIvarMap(InPropertyToIvarMap),
1560b57cec5SDimitry Andric       InvalidationMethod(nullptr),
1570b57cec5SDimitry Andric       Ctx(InCtx) {}
1580b57cec5SDimitry Andric 
VisitStmt(const Stmt * S)1590b57cec5SDimitry Andric     void VisitStmt(const Stmt *S) { VisitChildren(S); }
1600b57cec5SDimitry Andric 
1610b57cec5SDimitry Andric     void VisitBinaryOperator(const BinaryOperator *BO);
1620b57cec5SDimitry Andric 
1630b57cec5SDimitry Andric     void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
1640b57cec5SDimitry Andric 
VisitChildren(const Stmt * S)1650b57cec5SDimitry Andric     void VisitChildren(const Stmt *S) {
1660b57cec5SDimitry Andric       for (const auto *Child : S->children()) {
1670b57cec5SDimitry Andric         if (Child)
1680b57cec5SDimitry Andric           this->Visit(Child);
1690b57cec5SDimitry Andric         if (CalledAnotherInvalidationMethod)
1700b57cec5SDimitry Andric           return;
1710b57cec5SDimitry Andric       }
1720b57cec5SDimitry Andric     }
1730b57cec5SDimitry Andric   };
1740b57cec5SDimitry Andric 
1750b57cec5SDimitry Andric   /// Check if the any of the methods inside the interface are annotated with
1760b57cec5SDimitry Andric   /// the invalidation annotation, update the IvarInfo accordingly.
1770b57cec5SDimitry Andric   /// \param LookForPartial is set when we are searching for partial
1780b57cec5SDimitry Andric   ///        invalidators.
1790b57cec5SDimitry Andric   static void containsInvalidationMethod(const ObjCContainerDecl *D,
1800b57cec5SDimitry Andric                                          InvalidationInfo &Out,
1810b57cec5SDimitry Andric                                          bool LookForPartial);
1820b57cec5SDimitry Andric 
1830b57cec5SDimitry Andric   /// Check if ivar should be tracked and add to TrackedIvars if positive.
1840b57cec5SDimitry Andric   /// Returns true if ivar should be tracked.
1850b57cec5SDimitry Andric   static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
1860b57cec5SDimitry Andric                         const ObjCIvarDecl **FirstIvarDecl);
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric   /// Given the property declaration, and the list of tracked ivars, finds
1890b57cec5SDimitry Andric   /// the ivar backing the property when possible. Returns '0' when no such
1900b57cec5SDimitry Andric   /// ivar could be found.
1910b57cec5SDimitry Andric   static const ObjCIvarDecl *findPropertyBackingIvar(
1920b57cec5SDimitry Andric       const ObjCPropertyDecl *Prop,
1930b57cec5SDimitry Andric       const ObjCInterfaceDecl *InterfaceD,
1940b57cec5SDimitry Andric       IvarSet &TrackedIvars,
1950b57cec5SDimitry Andric       const ObjCIvarDecl **FirstIvarDecl);
1960b57cec5SDimitry Andric 
1970b57cec5SDimitry Andric   /// Print ivar name or the property if the given ivar backs a property.
1980b57cec5SDimitry Andric   static void printIvar(llvm::raw_svector_ostream &os,
1990b57cec5SDimitry Andric                         const ObjCIvarDecl *IvarDecl,
2000b57cec5SDimitry Andric                         const IvarToPropMapTy &IvarToPopertyMap);
2010b57cec5SDimitry Andric 
202a7dea167SDimitry Andric   void reportNoInvalidationMethod(CheckerNameRef CheckName,
2030b57cec5SDimitry Andric                                   const ObjCIvarDecl *FirstIvarDecl,
2040b57cec5SDimitry Andric                                   const IvarToPropMapTy &IvarToPopertyMap,
2050b57cec5SDimitry Andric                                   const ObjCInterfaceDecl *InterfaceD,
2060b57cec5SDimitry Andric                                   bool MissingDeclaration) const;
2070b57cec5SDimitry Andric 
2080b57cec5SDimitry Andric   void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
2090b57cec5SDimitry Andric                                    const IvarToPropMapTy &IvarToPopertyMap,
2100b57cec5SDimitry Andric                                    const ObjCMethodDecl *MethodD) const;
2110b57cec5SDimitry Andric 
2120b57cec5SDimitry Andric   AnalysisManager& Mgr;
2130b57cec5SDimitry Andric   BugReporter &BR;
2140b57cec5SDimitry Andric   /// Filter on the checks performed.
2150b57cec5SDimitry Andric   const ChecksFilter &Filter;
2160b57cec5SDimitry Andric 
2170b57cec5SDimitry Andric public:
IvarInvalidationCheckerImpl(AnalysisManager & InMgr,BugReporter & InBR,const ChecksFilter & InFilter)2180b57cec5SDimitry Andric   IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
2190b57cec5SDimitry Andric                               BugReporter &InBR,
2200b57cec5SDimitry Andric                               const ChecksFilter &InFilter) :
2210b57cec5SDimitry Andric     Mgr (InMgr), BR(InBR), Filter(InFilter) {}
2220b57cec5SDimitry Andric 
2230b57cec5SDimitry Andric   void visit(const ObjCImplementationDecl *D) const;
2240b57cec5SDimitry Andric };
2250b57cec5SDimitry Andric 
isInvalidationMethod(const ObjCMethodDecl * M,bool LookForPartial)2260b57cec5SDimitry Andric static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
2270b57cec5SDimitry Andric   for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) {
2280b57cec5SDimitry Andric     if (!LookForPartial &&
2290b57cec5SDimitry Andric         Ann->getAnnotation() == "objc_instance_variable_invalidator")
2300b57cec5SDimitry Andric       return true;
2310b57cec5SDimitry Andric     if (LookForPartial &&
2320b57cec5SDimitry Andric         Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
2330b57cec5SDimitry Andric       return true;
2340b57cec5SDimitry Andric   }
2350b57cec5SDimitry Andric   return false;
2360b57cec5SDimitry Andric }
2370b57cec5SDimitry Andric 
containsInvalidationMethod(const ObjCContainerDecl * D,InvalidationInfo & OutInfo,bool Partial)2380b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::containsInvalidationMethod(
2390b57cec5SDimitry Andric     const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric   if (!D)
2420b57cec5SDimitry Andric     return;
2430b57cec5SDimitry Andric 
2440b57cec5SDimitry Andric   assert(!isa<ObjCImplementationDecl>(D));
2450b57cec5SDimitry Andric   // TODO: Cache the results.
2460b57cec5SDimitry Andric 
2470b57cec5SDimitry Andric   // Check all methods.
2480b57cec5SDimitry Andric   for (const auto *MDI : D->methods())
2490b57cec5SDimitry Andric     if (isInvalidationMethod(MDI, Partial))
2500b57cec5SDimitry Andric       OutInfo.addInvalidationMethod(
2510b57cec5SDimitry Andric           cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
2520b57cec5SDimitry Andric 
2530b57cec5SDimitry Andric   // If interface, check all parent protocols and super.
2540b57cec5SDimitry Andric   if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
2550b57cec5SDimitry Andric 
2560b57cec5SDimitry Andric     // Visit all protocols.
2570b57cec5SDimitry Andric     for (const auto *I : InterfD->protocols())
2580b57cec5SDimitry Andric       containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
2590b57cec5SDimitry Andric 
2600b57cec5SDimitry Andric     // Visit all categories in case the invalidation method is declared in
2610b57cec5SDimitry Andric     // a category.
2620b57cec5SDimitry Andric     for (const auto *Ext : InterfD->visible_extensions())
2630b57cec5SDimitry Andric       containsInvalidationMethod(Ext, OutInfo, Partial);
2640b57cec5SDimitry Andric 
2650b57cec5SDimitry Andric     containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
2660b57cec5SDimitry Andric     return;
2670b57cec5SDimitry Andric   }
2680b57cec5SDimitry Andric 
2690b57cec5SDimitry Andric   // If protocol, check all parent protocols.
2700b57cec5SDimitry Andric   if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
2710b57cec5SDimitry Andric     for (const auto *I : ProtD->protocols()) {
2720b57cec5SDimitry Andric       containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
2730b57cec5SDimitry Andric     }
2740b57cec5SDimitry Andric     return;
2750b57cec5SDimitry Andric   }
2760b57cec5SDimitry Andric }
2770b57cec5SDimitry Andric 
trackIvar(const ObjCIvarDecl * Iv,IvarSet & TrackedIvars,const ObjCIvarDecl ** FirstIvarDecl)2780b57cec5SDimitry Andric bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv,
2790b57cec5SDimitry Andric                                         IvarSet &TrackedIvars,
2800b57cec5SDimitry Andric                                         const ObjCIvarDecl **FirstIvarDecl) {
2810b57cec5SDimitry Andric   QualType IvQTy = Iv->getType();
2820b57cec5SDimitry Andric   const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
2830b57cec5SDimitry Andric   if (!IvTy)
2840b57cec5SDimitry Andric     return false;
2850b57cec5SDimitry Andric   const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
2860b57cec5SDimitry Andric 
2870b57cec5SDimitry Andric   InvalidationInfo Info;
2880b57cec5SDimitry Andric   containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
2890b57cec5SDimitry Andric   if (Info.needsInvalidation()) {
2900b57cec5SDimitry Andric     const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
2910b57cec5SDimitry Andric     TrackedIvars[I] = Info;
2920b57cec5SDimitry Andric     if (!*FirstIvarDecl)
2930b57cec5SDimitry Andric       *FirstIvarDecl = I;
2940b57cec5SDimitry Andric     return true;
2950b57cec5SDimitry Andric   }
2960b57cec5SDimitry Andric   return false;
2970b57cec5SDimitry Andric }
2980b57cec5SDimitry Andric 
findPropertyBackingIvar(const ObjCPropertyDecl * Prop,const ObjCInterfaceDecl * InterfaceD,IvarSet & TrackedIvars,const ObjCIvarDecl ** FirstIvarDecl)2990b57cec5SDimitry Andric const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
3000b57cec5SDimitry Andric                         const ObjCPropertyDecl *Prop,
3010b57cec5SDimitry Andric                         const ObjCInterfaceDecl *InterfaceD,
3020b57cec5SDimitry Andric                         IvarSet &TrackedIvars,
3030b57cec5SDimitry Andric                         const ObjCIvarDecl **FirstIvarDecl) {
3040b57cec5SDimitry Andric   const ObjCIvarDecl *IvarD = nullptr;
3050b57cec5SDimitry Andric 
3060b57cec5SDimitry Andric   // Lookup for the synthesized case.
3070b57cec5SDimitry Andric   IvarD = Prop->getPropertyIvarDecl();
3080b57cec5SDimitry Andric   // We only track the ivars/properties that are defined in the current
3090b57cec5SDimitry Andric   // class (not the parent).
3100b57cec5SDimitry Andric   if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
3110b57cec5SDimitry Andric     if (TrackedIvars.count(IvarD)) {
3120b57cec5SDimitry Andric       return IvarD;
3130b57cec5SDimitry Andric     }
3140b57cec5SDimitry Andric     // If the ivar is synthesized we still want to track it.
3150b57cec5SDimitry Andric     if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
3160b57cec5SDimitry Andric       return IvarD;
3170b57cec5SDimitry Andric   }
3180b57cec5SDimitry Andric 
3190b57cec5SDimitry Andric   // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
3200b57cec5SDimitry Andric   StringRef PropName = Prop->getIdentifier()->getName();
32106c3fb27SDimitry Andric   for (const ObjCIvarDecl *Iv : llvm::make_first_range(TrackedIvars)) {
3220b57cec5SDimitry Andric     StringRef IvarName = Iv->getName();
3230b57cec5SDimitry Andric 
3240b57cec5SDimitry Andric     if (IvarName == PropName)
3250b57cec5SDimitry Andric       return Iv;
3260b57cec5SDimitry Andric 
3270b57cec5SDimitry Andric     SmallString<128> PropNameWithUnderscore;
3280b57cec5SDimitry Andric     {
3290b57cec5SDimitry Andric       llvm::raw_svector_ostream os(PropNameWithUnderscore);
3300b57cec5SDimitry Andric       os << '_' << PropName;
3310b57cec5SDimitry Andric     }
3320b57cec5SDimitry Andric     if (IvarName == PropNameWithUnderscore)
3330b57cec5SDimitry Andric       return Iv;
3340b57cec5SDimitry Andric   }
3350b57cec5SDimitry Andric 
3360b57cec5SDimitry Andric   // Note, this is a possible source of false positives. We could look at the
3370b57cec5SDimitry Andric   // getter implementation to find the ivar when its name is not derived from
3380b57cec5SDimitry Andric   // the property name.
3390b57cec5SDimitry Andric   return nullptr;
3400b57cec5SDimitry Andric }
3410b57cec5SDimitry Andric 
printIvar(llvm::raw_svector_ostream & os,const ObjCIvarDecl * IvarDecl,const IvarToPropMapTy & IvarToPopertyMap)3420b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
3430b57cec5SDimitry Andric                                       const ObjCIvarDecl *IvarDecl,
3440b57cec5SDimitry Andric                                       const IvarToPropMapTy &IvarToPopertyMap) {
3450b57cec5SDimitry Andric   if (IvarDecl->getSynthesize()) {
3460b57cec5SDimitry Andric     const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
3470b57cec5SDimitry Andric     assert(PD &&"Do we synthesize ivars for something other than properties?");
3480b57cec5SDimitry Andric     os << "Property "<< PD->getName() << " ";
3490b57cec5SDimitry Andric   } else {
3500b57cec5SDimitry Andric     os << "Instance variable "<< IvarDecl->getName() << " ";
3510b57cec5SDimitry Andric   }
3520b57cec5SDimitry Andric }
3530b57cec5SDimitry Andric 
3540b57cec5SDimitry Andric // Check that the invalidatable interfaces with ivars/properties implement the
3550b57cec5SDimitry Andric // invalidation methods.
3560b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::
visit(const ObjCImplementationDecl * ImplD) const3570b57cec5SDimitry Andric visit(const ObjCImplementationDecl *ImplD) const {
3580b57cec5SDimitry Andric   // Collect all ivars that need cleanup.
3590b57cec5SDimitry Andric   IvarSet Ivars;
3600b57cec5SDimitry Andric   // Record the first Ivar needing invalidation; used in reporting when only
3610b57cec5SDimitry Andric   // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
3620b57cec5SDimitry Andric   // deterministic output.
3630b57cec5SDimitry Andric   const ObjCIvarDecl *FirstIvarDecl = nullptr;
3640b57cec5SDimitry Andric   const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
3650b57cec5SDimitry Andric 
3660b57cec5SDimitry Andric   // Collect ivars declared in this class, its extensions and its implementation
3670b57cec5SDimitry Andric   ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
3680b57cec5SDimitry Andric   for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
3690b57cec5SDimitry Andric        Iv= Iv->getNextIvar())
3700b57cec5SDimitry Andric     trackIvar(Iv, Ivars, &FirstIvarDecl);
3710b57cec5SDimitry Andric 
3720b57cec5SDimitry Andric   // Construct Property/Property Accessor to Ivar maps to assist checking if an
3730b57cec5SDimitry Andric   // ivar which is backing a property has been reset.
3740b57cec5SDimitry Andric   MethToIvarMapTy PropSetterToIvarMap;
3750b57cec5SDimitry Andric   MethToIvarMapTy PropGetterToIvarMap;
3760b57cec5SDimitry Andric   PropToIvarMapTy PropertyToIvarMap;
3770b57cec5SDimitry Andric   IvarToPropMapTy IvarToPopertyMap;
3780b57cec5SDimitry Andric 
3790b57cec5SDimitry Andric   ObjCInterfaceDecl::PropertyMap PropMap;
380bdd1243dSDimitry Andric   InterfaceD->collectPropertiesToImplement(PropMap);
3810b57cec5SDimitry Andric 
38206c3fb27SDimitry Andric   for (const ObjCPropertyDecl *PD : llvm::make_second_range(PropMap)) {
3830b57cec5SDimitry Andric     if (PD->isClassProperty())
3840b57cec5SDimitry Andric       continue;
3850b57cec5SDimitry Andric 
3860b57cec5SDimitry Andric     const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
3870b57cec5SDimitry Andric                                                      &FirstIvarDecl);
3880b57cec5SDimitry Andric     if (!ID)
3890b57cec5SDimitry Andric       continue;
3900b57cec5SDimitry Andric 
3910b57cec5SDimitry Andric     // Store the mappings.
3920b57cec5SDimitry Andric     PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
3930b57cec5SDimitry Andric     PropertyToIvarMap[PD] = ID;
3940b57cec5SDimitry Andric     IvarToPopertyMap[ID] = PD;
3950b57cec5SDimitry Andric 
3960b57cec5SDimitry Andric     // Find the setter and the getter.
3970b57cec5SDimitry Andric     const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
3980b57cec5SDimitry Andric     if (SetterD) {
3990b57cec5SDimitry Andric       SetterD = SetterD->getCanonicalDecl();
4000b57cec5SDimitry Andric       PropSetterToIvarMap[SetterD] = ID;
4010b57cec5SDimitry Andric     }
4020b57cec5SDimitry Andric 
4030b57cec5SDimitry Andric     const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
4040b57cec5SDimitry Andric     if (GetterD) {
4050b57cec5SDimitry Andric       GetterD = GetterD->getCanonicalDecl();
4060b57cec5SDimitry Andric       PropGetterToIvarMap[GetterD] = ID;
4070b57cec5SDimitry Andric     }
4080b57cec5SDimitry Andric   }
4090b57cec5SDimitry Andric 
4100b57cec5SDimitry Andric   // If no ivars need invalidation, there is nothing to check here.
4110b57cec5SDimitry Andric   if (Ivars.empty())
4120b57cec5SDimitry Andric     return;
4130b57cec5SDimitry Andric 
4140b57cec5SDimitry Andric   // Find all partial invalidation methods.
4150b57cec5SDimitry Andric   InvalidationInfo PartialInfo;
4160b57cec5SDimitry Andric   containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true);
4170b57cec5SDimitry Andric 
4180b57cec5SDimitry Andric   // Remove ivars invalidated by the partial invalidation methods. They do not
4190b57cec5SDimitry Andric   // need to be invalidated in the regular invalidation methods.
4200b57cec5SDimitry Andric   bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false;
42106c3fb27SDimitry Andric   for (const ObjCMethodDecl *InterfD : PartialInfo.InvalidationMethods) {
4220b57cec5SDimitry Andric     // Get the corresponding method in the @implementation.
4230b57cec5SDimitry Andric     const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
4240b57cec5SDimitry Andric                                                InterfD->isInstanceMethod());
4250b57cec5SDimitry Andric     if (D && D->hasBody()) {
4260b57cec5SDimitry Andric       AtImplementationContainsAtLeastOnePartialInvalidationMethod = true;
4270b57cec5SDimitry Andric 
4280b57cec5SDimitry Andric       bool CalledAnotherInvalidationMethod = false;
4290b57cec5SDimitry Andric       // The MethodCrowler is going to remove the invalidated ivars.
4300b57cec5SDimitry Andric       MethodCrawler(Ivars,
4310b57cec5SDimitry Andric                     CalledAnotherInvalidationMethod,
4320b57cec5SDimitry Andric                     PropSetterToIvarMap,
4330b57cec5SDimitry Andric                     PropGetterToIvarMap,
4340b57cec5SDimitry Andric                     PropertyToIvarMap,
4350b57cec5SDimitry Andric                     BR.getContext()).VisitStmt(D->getBody());
4360b57cec5SDimitry Andric       // If another invalidation method was called, trust that full invalidation
4370b57cec5SDimitry Andric       // has occurred.
4380b57cec5SDimitry Andric       if (CalledAnotherInvalidationMethod)
4390b57cec5SDimitry Andric         Ivars.clear();
4400b57cec5SDimitry Andric     }
4410b57cec5SDimitry Andric   }
4420b57cec5SDimitry Andric 
4430b57cec5SDimitry Andric   // If all ivars have been invalidated by partial invalidators, there is
4440b57cec5SDimitry Andric   // nothing to check here.
4450b57cec5SDimitry Andric   if (Ivars.empty())
4460b57cec5SDimitry Andric     return;
4470b57cec5SDimitry Andric 
4480b57cec5SDimitry Andric   // Find all invalidation methods in this @interface declaration and parents.
4490b57cec5SDimitry Andric   InvalidationInfo Info;
4500b57cec5SDimitry Andric   containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
4510b57cec5SDimitry Andric 
4520b57cec5SDimitry Andric   // Report an error in case none of the invalidation methods are declared.
4530b57cec5SDimitry Andric   if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
4540b57cec5SDimitry Andric     if (Filter.check_MissingInvalidationMethod)
4550b57cec5SDimitry Andric       reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
4560b57cec5SDimitry Andric                                  FirstIvarDecl, IvarToPopertyMap, InterfaceD,
4570b57cec5SDimitry Andric                                  /*MissingDeclaration*/ true);
4580b57cec5SDimitry Andric     // If there are no invalidation methods, there is no ivar validation work
4590b57cec5SDimitry Andric     // to be done.
4600b57cec5SDimitry Andric     return;
4610b57cec5SDimitry Andric   }
4620b57cec5SDimitry Andric 
4630b57cec5SDimitry Andric   // Only check if Ivars are invalidated when InstanceVariableInvalidation
4640b57cec5SDimitry Andric   // has been requested.
4650b57cec5SDimitry Andric   if (!Filter.check_InstanceVariableInvalidation)
4660b57cec5SDimitry Andric     return;
4670b57cec5SDimitry Andric 
4680b57cec5SDimitry Andric   // Check that all ivars are invalidated by the invalidation methods.
4690b57cec5SDimitry Andric   bool AtImplementationContainsAtLeastOneInvalidationMethod = false;
47006c3fb27SDimitry Andric   for (const ObjCMethodDecl *InterfD : Info.InvalidationMethods) {
4710b57cec5SDimitry Andric     // Get the corresponding method in the @implementation.
4720b57cec5SDimitry Andric     const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
4730b57cec5SDimitry Andric                                                InterfD->isInstanceMethod());
4740b57cec5SDimitry Andric     if (D && D->hasBody()) {
4750b57cec5SDimitry Andric       AtImplementationContainsAtLeastOneInvalidationMethod = true;
4760b57cec5SDimitry Andric 
4770b57cec5SDimitry Andric       // Get a copy of ivars needing invalidation.
4780b57cec5SDimitry Andric       IvarSet IvarsI = Ivars;
4790b57cec5SDimitry Andric 
4800b57cec5SDimitry Andric       bool CalledAnotherInvalidationMethod = false;
4810b57cec5SDimitry Andric       MethodCrawler(IvarsI,
4820b57cec5SDimitry Andric                     CalledAnotherInvalidationMethod,
4830b57cec5SDimitry Andric                     PropSetterToIvarMap,
4840b57cec5SDimitry Andric                     PropGetterToIvarMap,
4850b57cec5SDimitry Andric                     PropertyToIvarMap,
4860b57cec5SDimitry Andric                     BR.getContext()).VisitStmt(D->getBody());
4870b57cec5SDimitry Andric       // If another invalidation method was called, trust that full invalidation
4880b57cec5SDimitry Andric       // has occurred.
4890b57cec5SDimitry Andric       if (CalledAnotherInvalidationMethod)
4900b57cec5SDimitry Andric         continue;
4910b57cec5SDimitry Andric 
4920b57cec5SDimitry Andric       // Warn on the ivars that were not invalidated by the method.
49306c3fb27SDimitry Andric       for (const ObjCIvarDecl *Ivar : llvm::make_first_range(IvarsI))
49406c3fb27SDimitry Andric         reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap, D);
4950b57cec5SDimitry Andric     }
4960b57cec5SDimitry Andric   }
4970b57cec5SDimitry Andric 
4980b57cec5SDimitry Andric   // Report an error in case none of the invalidation methods are implemented.
4990b57cec5SDimitry Andric   if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
5000b57cec5SDimitry Andric     if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
5010b57cec5SDimitry Andric       // Warn on the ivars that were not invalidated by the prrtial
5020b57cec5SDimitry Andric       // invalidation methods.
50306c3fb27SDimitry Andric       for (const ObjCIvarDecl *Ivar : llvm::make_first_range(Ivars))
50406c3fb27SDimitry Andric         reportIvarNeedsInvalidation(Ivar, IvarToPopertyMap, nullptr);
5050b57cec5SDimitry Andric     } else {
5060b57cec5SDimitry Andric       // Otherwise, no invalidation methods were implemented.
5070b57cec5SDimitry Andric       reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
5080b57cec5SDimitry Andric                                  FirstIvarDecl, IvarToPopertyMap, InterfaceD,
5090b57cec5SDimitry Andric                                  /*MissingDeclaration*/ false);
5100b57cec5SDimitry Andric     }
5110b57cec5SDimitry Andric   }
5120b57cec5SDimitry Andric }
5130b57cec5SDimitry Andric 
reportNoInvalidationMethod(CheckerNameRef CheckName,const ObjCIvarDecl * FirstIvarDecl,const IvarToPropMapTy & IvarToPopertyMap,const ObjCInterfaceDecl * InterfaceD,bool MissingDeclaration) const5140b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
515a7dea167SDimitry Andric     CheckerNameRef CheckName, const ObjCIvarDecl *FirstIvarDecl,
5160b57cec5SDimitry Andric     const IvarToPropMapTy &IvarToPopertyMap,
5170b57cec5SDimitry Andric     const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const {
5180b57cec5SDimitry Andric   SmallString<128> sbuf;
5190b57cec5SDimitry Andric   llvm::raw_svector_ostream os(sbuf);
5200b57cec5SDimitry Andric   assert(FirstIvarDecl);
5210b57cec5SDimitry Andric   printIvar(os, FirstIvarDecl, IvarToPopertyMap);
5220b57cec5SDimitry Andric   os << "needs to be invalidated; ";
5230b57cec5SDimitry Andric   if (MissingDeclaration)
5240b57cec5SDimitry Andric     os << "no invalidation method is declared for ";
5250b57cec5SDimitry Andric   else
5260b57cec5SDimitry Andric     os << "no invalidation method is defined in the @implementation for ";
5270b57cec5SDimitry Andric   os << InterfaceD->getName();
5280b57cec5SDimitry Andric 
5290b57cec5SDimitry Andric   PathDiagnosticLocation IvarDecLocation =
5300b57cec5SDimitry Andric     PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
5310b57cec5SDimitry Andric 
5320b57cec5SDimitry Andric   BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation",
5330b57cec5SDimitry Andric                      categories::CoreFoundationObjectiveC, os.str(),
5340b57cec5SDimitry Andric                      IvarDecLocation);
5350b57cec5SDimitry Andric }
5360b57cec5SDimitry Andric 
5370b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::
reportIvarNeedsInvalidation(const ObjCIvarDecl * IvarD,const IvarToPropMapTy & IvarToPopertyMap,const ObjCMethodDecl * MethodD) const5380b57cec5SDimitry Andric reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
5390b57cec5SDimitry Andric                             const IvarToPropMapTy &IvarToPopertyMap,
5400b57cec5SDimitry Andric                             const ObjCMethodDecl *MethodD) const {
5410b57cec5SDimitry Andric   SmallString<128> sbuf;
5420b57cec5SDimitry Andric   llvm::raw_svector_ostream os(sbuf);
5430b57cec5SDimitry Andric   printIvar(os, IvarD, IvarToPopertyMap);
5440b57cec5SDimitry Andric   os << "needs to be invalidated or set to nil";
5450b57cec5SDimitry Andric   if (MethodD) {
5460b57cec5SDimitry Andric     PathDiagnosticLocation MethodDecLocation =
5470b57cec5SDimitry Andric                            PathDiagnosticLocation::createEnd(MethodD->getBody(),
5480b57cec5SDimitry Andric                            BR.getSourceManager(),
5490b57cec5SDimitry Andric                            Mgr.getAnalysisDeclContext(MethodD));
5500b57cec5SDimitry Andric     BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
5510b57cec5SDimitry Andric                        "Incomplete invalidation",
5520b57cec5SDimitry Andric                        categories::CoreFoundationObjectiveC, os.str(),
5530b57cec5SDimitry Andric                        MethodDecLocation);
5540b57cec5SDimitry Andric   } else {
5550b57cec5SDimitry Andric     BR.EmitBasicReport(
5560b57cec5SDimitry Andric         IvarD, Filter.checkName_InstanceVariableInvalidation,
5570b57cec5SDimitry Andric         "Incomplete invalidation", categories::CoreFoundationObjectiveC,
5580b57cec5SDimitry Andric         os.str(),
5590b57cec5SDimitry Andric         PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager()));
5600b57cec5SDimitry Andric   }
5610b57cec5SDimitry Andric }
5620b57cec5SDimitry Andric 
markInvalidated(const ObjCIvarDecl * Iv)5630b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
5640b57cec5SDimitry Andric     const ObjCIvarDecl *Iv) {
5650b57cec5SDimitry Andric   IvarSet::iterator I = IVars.find(Iv);
5660b57cec5SDimitry Andric   if (I != IVars.end()) {
5670b57cec5SDimitry Andric     // If InvalidationMethod is present, we are processing the message send and
5680b57cec5SDimitry Andric     // should ensure we are invalidating with the appropriate method,
5690b57cec5SDimitry Andric     // otherwise, we are processing setting to 'nil'.
5700b57cec5SDimitry Andric     if (!InvalidationMethod || I->second.hasMethod(InvalidationMethod))
5710b57cec5SDimitry Andric       IVars.erase(I);
5720b57cec5SDimitry Andric   }
5730b57cec5SDimitry Andric }
5740b57cec5SDimitry Andric 
peel(const Expr * E) const5750b57cec5SDimitry Andric const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
5760b57cec5SDimitry Andric   E = E->IgnoreParenCasts();
5770b57cec5SDimitry Andric   if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
5780b57cec5SDimitry Andric     E = POE->getSyntacticForm()->IgnoreParenCasts();
5790b57cec5SDimitry Andric   if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
5800b57cec5SDimitry Andric     E = OVE->getSourceExpr()->IgnoreParenCasts();
5810b57cec5SDimitry Andric   return E;
5820b57cec5SDimitry Andric }
5830b57cec5SDimitry Andric 
checkObjCIvarRefExpr(const ObjCIvarRefExpr * IvarRef)5840b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
5850b57cec5SDimitry Andric     const ObjCIvarRefExpr *IvarRef) {
5860b57cec5SDimitry Andric   if (const Decl *D = IvarRef->getDecl())
5870b57cec5SDimitry Andric     markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
5880b57cec5SDimitry Andric }
5890b57cec5SDimitry Andric 
checkObjCMessageExpr(const ObjCMessageExpr * ME)5900b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
5910b57cec5SDimitry Andric     const ObjCMessageExpr *ME) {
5920b57cec5SDimitry Andric   const ObjCMethodDecl *MD = ME->getMethodDecl();
5930b57cec5SDimitry Andric   if (MD) {
5940b57cec5SDimitry Andric     MD = MD->getCanonicalDecl();
5950b57cec5SDimitry Andric     MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
5960b57cec5SDimitry Andric     if (IvI != PropertyGetterToIvarMap.end())
5970b57cec5SDimitry Andric       markInvalidated(IvI->second);
5980b57cec5SDimitry Andric   }
5990b57cec5SDimitry Andric }
6000b57cec5SDimitry Andric 
checkObjCPropertyRefExpr(const ObjCPropertyRefExpr * PA)6010b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
6020b57cec5SDimitry Andric     const ObjCPropertyRefExpr *PA) {
6030b57cec5SDimitry Andric 
6040b57cec5SDimitry Andric   if (PA->isExplicitProperty()) {
6050b57cec5SDimitry Andric     const ObjCPropertyDecl *PD = PA->getExplicitProperty();
6060b57cec5SDimitry Andric     if (PD) {
6070b57cec5SDimitry Andric       PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
6080b57cec5SDimitry Andric       PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
6090b57cec5SDimitry Andric       if (IvI != PropertyToIvarMap.end())
6100b57cec5SDimitry Andric         markInvalidated(IvI->second);
6110b57cec5SDimitry Andric       return;
6120b57cec5SDimitry Andric     }
6130b57cec5SDimitry Andric   }
6140b57cec5SDimitry Andric 
6150b57cec5SDimitry Andric   if (PA->isImplicitProperty()) {
6160b57cec5SDimitry Andric     const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
6170b57cec5SDimitry Andric     if (MD) {
6180b57cec5SDimitry Andric       MD = MD->getCanonicalDecl();
6190b57cec5SDimitry Andric       MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
6200b57cec5SDimitry Andric       if (IvI != PropertyGetterToIvarMap.end())
6210b57cec5SDimitry Andric         markInvalidated(IvI->second);
6220b57cec5SDimitry Andric       return;
6230b57cec5SDimitry Andric     }
6240b57cec5SDimitry Andric   }
6250b57cec5SDimitry Andric }
6260b57cec5SDimitry Andric 
isZero(const Expr * E) const6270b57cec5SDimitry Andric bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
6280b57cec5SDimitry Andric   E = peel(E);
6290b57cec5SDimitry Andric 
6300b57cec5SDimitry Andric   return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
6310b57cec5SDimitry Andric            != Expr::NPCK_NotNull);
6320b57cec5SDimitry Andric }
6330b57cec5SDimitry Andric 
check(const Expr * E)6340b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
6350b57cec5SDimitry Andric   E = peel(E);
6360b57cec5SDimitry Andric 
6370b57cec5SDimitry Andric   if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
6380b57cec5SDimitry Andric     checkObjCIvarRefExpr(IvarRef);
6390b57cec5SDimitry Andric     return;
6400b57cec5SDimitry Andric   }
6410b57cec5SDimitry Andric 
6420b57cec5SDimitry Andric   if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
6430b57cec5SDimitry Andric     checkObjCPropertyRefExpr(PropRef);
6440b57cec5SDimitry Andric     return;
6450b57cec5SDimitry Andric   }
6460b57cec5SDimitry Andric 
6470b57cec5SDimitry Andric   if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
6480b57cec5SDimitry Andric     checkObjCMessageExpr(MsgExpr);
6490b57cec5SDimitry Andric     return;
6500b57cec5SDimitry Andric   }
6510b57cec5SDimitry Andric }
6520b57cec5SDimitry Andric 
VisitBinaryOperator(const BinaryOperator * BO)6530b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
6540b57cec5SDimitry Andric     const BinaryOperator *BO) {
6550b57cec5SDimitry Andric   VisitStmt(BO);
6560b57cec5SDimitry Andric 
6570b57cec5SDimitry Andric   // Do we assign/compare against zero? If yes, check the variable we are
6580b57cec5SDimitry Andric   // assigning to.
6590b57cec5SDimitry Andric   BinaryOperatorKind Opcode = BO->getOpcode();
6600b57cec5SDimitry Andric   if (Opcode != BO_Assign &&
6610b57cec5SDimitry Andric       Opcode != BO_EQ &&
6620b57cec5SDimitry Andric       Opcode != BO_NE)
6630b57cec5SDimitry Andric     return;
6640b57cec5SDimitry Andric 
6650b57cec5SDimitry Andric   if (isZero(BO->getRHS())) {
6660b57cec5SDimitry Andric       check(BO->getLHS());
6670b57cec5SDimitry Andric       return;
6680b57cec5SDimitry Andric   }
6690b57cec5SDimitry Andric 
6700b57cec5SDimitry Andric   if (Opcode != BO_Assign && isZero(BO->getLHS())) {
6710b57cec5SDimitry Andric     check(BO->getRHS());
6720b57cec5SDimitry Andric     return;
6730b57cec5SDimitry Andric   }
6740b57cec5SDimitry Andric }
6750b57cec5SDimitry Andric 
VisitObjCMessageExpr(const ObjCMessageExpr * ME)6760b57cec5SDimitry Andric void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
6770b57cec5SDimitry Andric   const ObjCMessageExpr *ME) {
6780b57cec5SDimitry Andric   const ObjCMethodDecl *MD = ME->getMethodDecl();
6790b57cec5SDimitry Andric   const Expr *Receiver = ME->getInstanceReceiver();
6800b57cec5SDimitry Andric 
6810b57cec5SDimitry Andric   // Stop if we are calling '[self invalidate]'.
6820b57cec5SDimitry Andric   if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false))
6830b57cec5SDimitry Andric     if (Receiver->isObjCSelfExpr()) {
6840b57cec5SDimitry Andric       CalledAnotherInvalidationMethod = true;
6850b57cec5SDimitry Andric       return;
6860b57cec5SDimitry Andric     }
6870b57cec5SDimitry Andric 
6880b57cec5SDimitry Andric   // Check if we call a setter and set the property to 'nil'.
6890b57cec5SDimitry Andric   if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
6900b57cec5SDimitry Andric     MD = MD->getCanonicalDecl();
6910b57cec5SDimitry Andric     MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
6920b57cec5SDimitry Andric     if (IvI != PropertySetterToIvarMap.end()) {
6930b57cec5SDimitry Andric       markInvalidated(IvI->second);
6940b57cec5SDimitry Andric       return;
6950b57cec5SDimitry Andric     }
6960b57cec5SDimitry Andric   }
6970b57cec5SDimitry Andric 
6980b57cec5SDimitry Andric   // Check if we call the 'invalidation' routine on the ivar.
6990b57cec5SDimitry Andric   if (Receiver) {
7000b57cec5SDimitry Andric     InvalidationMethod = MD;
7010b57cec5SDimitry Andric     check(Receiver->IgnoreParenCasts());
7020b57cec5SDimitry Andric     InvalidationMethod = nullptr;
7030b57cec5SDimitry Andric   }
7040b57cec5SDimitry Andric 
7050b57cec5SDimitry Andric   VisitStmt(ME);
7060b57cec5SDimitry Andric }
7070b57cec5SDimitry Andric } // end anonymous namespace
7080b57cec5SDimitry Andric 
7090b57cec5SDimitry Andric // Register the checkers.
7100b57cec5SDimitry Andric namespace {
7110b57cec5SDimitry Andric class IvarInvalidationChecker :
7120b57cec5SDimitry Andric   public Checker<check::ASTDecl<ObjCImplementationDecl> > {
7130b57cec5SDimitry Andric public:
7140b57cec5SDimitry Andric   ChecksFilter Filter;
7150b57cec5SDimitry Andric public:
checkASTDecl(const ObjCImplementationDecl * D,AnalysisManager & Mgr,BugReporter & BR) const7160b57cec5SDimitry Andric   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
7170b57cec5SDimitry Andric                     BugReporter &BR) const {
7180b57cec5SDimitry Andric     IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
7190b57cec5SDimitry Andric     Walker.visit(D);
7200b57cec5SDimitry Andric   }
7210b57cec5SDimitry Andric };
7220b57cec5SDimitry Andric } // end anonymous namespace
7230b57cec5SDimitry Andric 
registerIvarInvalidationModeling(CheckerManager & mgr)7240b57cec5SDimitry Andric void ento::registerIvarInvalidationModeling(CheckerManager &mgr) {
7250b57cec5SDimitry Andric   mgr.registerChecker<IvarInvalidationChecker>();
7260b57cec5SDimitry Andric }
7270b57cec5SDimitry Andric 
shouldRegisterIvarInvalidationModeling(const CheckerManager & mgr)7285ffd83dbSDimitry Andric bool ento::shouldRegisterIvarInvalidationModeling(const CheckerManager &mgr) {
7290b57cec5SDimitry Andric   return true;
7300b57cec5SDimitry Andric }
7310b57cec5SDimitry Andric 
7320b57cec5SDimitry Andric #define REGISTER_CHECKER(name)                                                 \
7330b57cec5SDimitry Andric   void ento::register##name(CheckerManager &mgr) {                             \
7340b57cec5SDimitry Andric     IvarInvalidationChecker *checker =                                         \
7350b57cec5SDimitry Andric         mgr.getChecker<IvarInvalidationChecker>();                             \
7360b57cec5SDimitry Andric     checker->Filter.check_##name = true;                                       \
737a7dea167SDimitry Andric     checker->Filter.checkName_##name = mgr.getCurrentCheckerName();            \
7380b57cec5SDimitry Andric   }                                                                            \
7390b57cec5SDimitry Andric                                                                                \
7405ffd83dbSDimitry Andric   bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
7410b57cec5SDimitry Andric 
7420b57cec5SDimitry Andric REGISTER_CHECKER(InstanceVariableInvalidation)
7430b57cec5SDimitry Andric REGISTER_CHECKER(MissingInvalidationMethod)
744