brintos

brintos / llvm-project-archived public Read only

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