IvarInvalidationChecker.cpp revision b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3
15bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//=- IvarInvalidationChecker.cpp - -*- C++ ----*-==//
25bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//
35bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//                     The LLVM Compiler Infrastructure
45bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//
55bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks// This file is distributed under the University of Illinois Open Source
65bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks// License. See LICENSE.TXT for details.
75bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//
85bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//===----------------------------------------------------------------------===//
95bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//
105bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//  This checker implements annotation driven invalidation checking. If a class
115bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//  contains a method annotated with 'objc_instance_variable_invalidator',
125bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//  - (void) foo
135bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//           __attribute__((annotate("objc_instance_variable_invalidator")));
145bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//  all the "ivalidatable" instance variables of this class should be
155bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//  invalidated. We call an instance variable ivalidatable if it is an object of
165bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//  a class which contains an invalidation method.
175bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//
185bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//  Note, this checker currently only checks if an ivar was accessed by the
195bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//  method, we do not currently support any deeper invalidation checking.
205bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//
215bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks//===----------------------------------------------------------------------===//
225bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
235bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks#include "ClangSACheckers.h"
245bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks#include "clang/StaticAnalyzer/Core/Checker.h"
255bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
265bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
275bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks#include "clang/AST/DeclObjC.h"
285bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks#include "clang/AST/StmtVisitor.h"
295bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks#include "llvm/ADT/DenseMap.h"
305bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks#include "llvm/ADT/SmallString.h"
315bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
325bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaksusing namespace clang;
335bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaksusing namespace ento;
345bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
355bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaksnamespace {
365bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaksclass IvarInvalidationChecker :
375bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  public Checker<check::ASTDecl<ObjCMethodDecl> > {
385bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
395bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  typedef llvm::DenseMap<const ObjCIvarDecl*, bool> IvarSet;
405bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  typedef llvm::DenseMap<const ObjCMethodDecl*,
415bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks                         const ObjCIvarDecl*> MethToIvarMapTy;
425bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  typedef llvm::DenseMap<const ObjCPropertyDecl*,
435bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks                         const ObjCIvarDecl*> PropToIvarMapTy;
445bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
455bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  /// Statement visitor, which walks the method body and flags the ivars
465bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  /// referenced in it (either directly or via property).
475bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
485bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
495bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    /// The set of Ivars which need to be invalidated.
505bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    IvarSet &IVars;
515bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
525bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    /// Property setter to ivar mapping.
535bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    MethToIvarMapTy &PropertySetterToIvarMap;
545bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
555bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    // Property to ivar mapping.
565bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    PropToIvarMapTy &PropertyToIvarMap;
575bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
585bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  public:
595bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    MethodCrawler(const ObjCInterfaceDecl *InID,
605bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks                  IvarSet &InIVars, MethToIvarMapTy &InPropertySetterToIvarMap,
615bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks                  PropToIvarMapTy &InPropertyToIvarMap)
627836011482bc26dfebf15df4fd993d07b607fbcfNAKAMURA Takumi    : IVars(InIVars),
635bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      PropertySetterToIvarMap(InPropertySetterToIvarMap),
645bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      PropertyToIvarMap(InPropertyToIvarMap) {}
655bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
665bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    void VisitStmt(const Stmt *S) { VisitChildren(S); }
675bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
685bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
695bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
705bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
715bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
725bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    void VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
735bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
745bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    void VisitChildren(const Stmt *S) {
755bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      for (Stmt::const_child_range I = S->children(); I; ++I)
765bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks        if (*I)
77b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks          this->Visit(*I);
785bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    }
795bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  };
805bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
815bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  /// Check if the any of the methods inside the interface are annotated with
825bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  /// the invalidation annotation.
835bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  bool containsInvalidationMethod(const ObjCContainerDecl *D) const;
845bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
855bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  /// Given the property declaration, and the list of tracked ivars, finds
865bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  /// the ivar backing the property when possible. Returns '0' when no such
875bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  /// ivar could be found.
885bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  static const ObjCIvarDecl *findPropertyBackingIvar(
895bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      const ObjCPropertyDecl *Prop,
905bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      const ObjCInterfaceDecl *InterfaceD,
915bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      IvarSet TrackedIvars);
925bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
935bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zakspublic:
945bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager& Mgr,
955bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks                    BugReporter &BR) const;
965bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
97b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks  // TODO: We are currently ignoring the ivars coming from class extensions.
985bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks};
995bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1005bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaksbool isInvalidationMethod(const ObjCMethodDecl *M) {
101b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks  for (specific_attr_iterator<AnnotateAttr>
102b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks       AI = M->specific_attr_begin<AnnotateAttr>(),
103b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks       AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
104b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks    const AnnotateAttr *Ann = *AI;
105b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks    if (Ann->getAnnotation() == "objc_instance_variable_invalidator")
106b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks      return true;
107b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks  }
1085bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  return false;
1095bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks}
1105bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1115bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaksbool IvarInvalidationChecker::containsInvalidationMethod (
1125bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    const ObjCContainerDecl *D) const {
1135bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1145bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // TODO: Cache the results.
1155bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1165bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  if (!D)
1175bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    return false;
1185bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1195bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // Check all methods.
1205bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  for (ObjCContainerDecl::method_iterator
1215bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      I = D->meth_begin(),
1225bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      E = D->meth_end(); I != E; ++I) {
1235bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      const ObjCMethodDecl *MDI = *I;
1245bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      if (isInvalidationMethod(MDI))
1255bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks        return true;
1265bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  }
1275bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1285bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // If interface, check all parent protocols and super.
1295bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // TODO: Visit all categories in case the invalidation method is declared in
1305bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // a category.
1315bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  if (const ObjCInterfaceDecl *InterfaceD = dyn_cast<ObjCInterfaceDecl>(D)) {
1325bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    for (ObjCInterfaceDecl::protocol_iterator
1335bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks        I = InterfaceD->protocol_begin(),
1345bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks        E = InterfaceD->protocol_end(); I != E; ++I) {
1355bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      if (containsInvalidationMethod(*I))
1365bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks        return true;
1375bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    }
1385bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    return containsInvalidationMethod(InterfaceD->getSuperClass());
1395bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  }
1405bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1415bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // If protocol, check all parent protocols.
1425bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
1435bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    for (ObjCInterfaceDecl::protocol_iterator
1445bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks        I = ProtD->protocol_begin(),
1455bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks        E = ProtD->protocol_end(); I != E; ++I) {
1465bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      if (containsInvalidationMethod(*I))
1475bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks        return true;
1485bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    }
1495bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    return false;
1505bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  }
1515bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1525bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  llvm_unreachable("One of the casts above should have succeeded.");
1535bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks}
1545bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1555bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaksconst ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar(
1565bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks                        const ObjCPropertyDecl *Prop,
1575bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks                        const ObjCInterfaceDecl *InterfaceD,
1585bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks                        IvarSet TrackedIvars) {
1595bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  const ObjCIvarDecl *IvarD = 0;
1605bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1615bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // Lookup for the synthesized case.
1625bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  IvarD = Prop->getPropertyIvarDecl();
1635bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  if (IvarD)
1645bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    return IvarD;
1655bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1665bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
1675bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  StringRef PropName = Prop->getIdentifier()->getName();
1685bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  for (IvarSet::const_iterator I = TrackedIvars.begin(),
1695bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks                               E = TrackedIvars.end(); I != E; ++I) {
1705bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    const ObjCIvarDecl *Iv = I->first;
1715bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    StringRef IvarName = Iv->getName();
1725bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1735bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    if (IvarName == PropName)
1745bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      return Iv;
1755bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1765bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    SmallString<128> PropNameWithUnderscore;
1775bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    {
1785bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      llvm::raw_svector_ostream os(PropNameWithUnderscore);
1795bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      os << '_' << PropName;
1805bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    }
1815bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    if (IvarName == PropNameWithUnderscore.str())
1825bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      return Iv;
1835bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  }
1845bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1855bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // Note, this is a possible source of false positives. We could look at the
1865bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // getter implementation to find the ivar when its name is not derived from
1875bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // the property name.
1885bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  return 0;
1895bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks}
1905bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1915bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaksvoid IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D,
1925bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks                                          AnalysisManager& Mgr,
1935bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks                                          BugReporter &BR) const {
1945bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // We are only interested in checking the cleanup methods.
1955bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  if (!D->hasBody() || !isInvalidationMethod(D))
1965bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    return;
1975bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
1985bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // Collect all ivars that need cleanup.
1995bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  IvarSet Ivars;
2005bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
2015bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  for (ObjCInterfaceDecl::ivar_iterator
2025bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      II = InterfaceD->ivar_begin(),
2035bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      IE = InterfaceD->ivar_end(); II != IE; ++II) {
2045bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    const ObjCIvarDecl *Iv = *II;
2055bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    QualType IvQTy = Iv->getType();
2065bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
2075bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    if (!IvTy)
2085bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      continue;
209b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks    const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
2105bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    if (containsInvalidationMethod(IvInterf))
2115bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      Ivars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = false;
2125bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  }
2135bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2145bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // Construct Property/Property Setter to Ivar maps to assist checking if an
2155bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // ivar which is backing a property has been reset.
2165bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  MethToIvarMapTy PropSetterToIvarMap;
2175bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  PropToIvarMapTy PropertyToIvarMap;
2185bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  for (ObjCInterfaceDecl::prop_iterator
2195bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      I = InterfaceD->prop_begin(),
2205bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      E = InterfaceD->prop_end(); I != E; ++I) {
2215bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    const ObjCPropertyDecl *PD = *I;
2225bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2235bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars);
2245bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    if (!ID) {
2255bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      continue;
2265bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    }
2275bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    // Find the setter.
2285bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
2295bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    // If we don't know the setter, do not track this ivar.
230b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks    if (!SetterD)
2315bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      continue;
2325bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2335bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    // Store the mappings.
2345bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
2355bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
2365bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    PropertyToIvarMap[PD] = ID;
2375bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    PropSetterToIvarMap[SetterD] = ID;
2385bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  }
2395bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2405bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2415bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // Check which ivars have been accessed by the method.
2425bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // We assume that if ivar was at least accessed, it was not forgotten.
2435bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  MethodCrawler(InterfaceD, Ivars,
2445bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks                PropSetterToIvarMap, PropertyToIvarMap).VisitStmt(D->getBody());
2455bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2465bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  // Warn on the ivars that were not accessed by the method.
2475bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  for (IvarSet::const_iterator I = Ivars.begin(), E = Ivars.end(); I != E; ++I){
2485bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    if (I->second == false) {
2495bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      const ObjCIvarDecl *IvarDecl = I->first;
2505bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2515bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      PathDiagnosticLocation IvarDecLocation =
252b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks          PathDiagnosticLocation::createEnd(D->getBody(), BR.getSourceManager(),
253b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks                                            Mgr.getAnalysisDeclContext(D));
2545bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2555bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      SmallString<128> sbuf;
2565bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      llvm::raw_svector_ostream os(sbuf);
257b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks      os << "Instance variable "<< IvarDecl->getName()
258b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks         << " needs to be invalidated";
2595bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
260b087bbf3cf44a56d60ad1ed6fd5abb48dab0e0b3Anna Zaks      BR.EmitBasicReport(D,
2615bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks          "Incomplete invalidation",
2625bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks          categories::CoreFoundationObjectiveC, os.str(),
2635bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks          IvarDecLocation);
2645bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    }
2655bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  }
2665bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks}
2675bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2685bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks/// Handle the case when an ivar is directly accessed.
2695bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaksvoid IvarInvalidationChecker::MethodCrawler::VisitObjCIvarRefExpr(
2705bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    const ObjCIvarRefExpr *IvarRef) {
2715bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  const Decl *D = IvarRef->getDecl();
2725bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  if (D)
2735bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    IVars[cast<ObjCIvarDecl>(D->getCanonicalDecl())] = true;
2745bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  VisitStmt(IvarRef);
2755bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks}
2765bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2775bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2785bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks/// Handle the case when the property backing ivar is set via a direct call
2795bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks/// to the setter.
2805bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaksvoid IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr(
2815bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    const ObjCMessageExpr *ME) {
2825bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  const ObjCMethodDecl *MD = ME->getMethodDecl();
2835bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  if (MD) {
2845bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
2855bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    IVars[PropertySetterToIvarMap[MD]] = true;
2865bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  }
2875bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  VisitStmt(ME);
2885bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks}
2895bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2905bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks/// Handle the case when the property backing ivar is set via the dot syntax.
2915bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaksvoid IvarInvalidationChecker::MethodCrawler::VisitObjCPropertyRefExpr(
2925bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    const ObjCPropertyRefExpr *PA) {
2935bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
2945bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  if (PA->isExplicitProperty()) {
2955bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    const ObjCPropertyDecl *PD = PA->getExplicitProperty();
2965bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    if (PD) {
2975bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
2985bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      IVars[PropertyToIvarMap[PD]] = true;
2995bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      VisitStmt(PA);
3005bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      return;
3015bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    }
3025bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  }
3035bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
3045bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  if (PA->isImplicitProperty()) {
3055bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
3065bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    if (MD) {
3075bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
3085bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      IVars[PropertySetterToIvarMap[MD]] = true;
3095bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      VisitStmt(PA);
3105bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks      return;
3115bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks    }
3125bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  }
3135bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  VisitStmt(PA);
3145bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks}
3155bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks}
3165bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks
3175bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks// Register the checker.
3185bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaksvoid ento::registerIvarInvalidationChecker(CheckerManager &mgr) {
3195bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks  mgr.registerChecker<IvarInvalidationChecker>();
3205bf5c2ec54ede5352293e5739e9b44bea2f6b01bAnna Zaks}
321