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