1243791Sdim//=- IvarInvalidationChecker.cpp - -*- C++ -------------------------------*-==// 2243791Sdim// 3243791Sdim// The LLVM Compiler Infrastructure 4243791Sdim// 5243791Sdim// This file is distributed under the University of Illinois Open Source 6243791Sdim// License. See LICENSE.TXT for details. 7243791Sdim// 8243791Sdim//===----------------------------------------------------------------------===// 9243791Sdim// 10243791Sdim// This checker implements annotation driven invalidation checking. If a class 11243791Sdim// contains a method annotated with 'objc_instance_variable_invalidator', 12243791Sdim// - (void) foo 13243791Sdim// __attribute__((annotate("objc_instance_variable_invalidator"))); 14243791Sdim// all the "ivalidatable" instance variables of this class should be 15243791Sdim// invalidated. We call an instance variable ivalidatable if it is an object of 16243791Sdim// a class which contains an invalidation method. There could be multiple 17243791Sdim// methods annotated with such annotations per class, either one can be used 18243791Sdim// to invalidate the ivar. An ivar or property are considered to be 19243791Sdim// invalidated if they are being assigned 'nil' or an invalidation method has 20243791Sdim// been called on them. An invalidation method should either invalidate all 21243791Sdim// the ivars or call another invalidation method (on self). 22243791Sdim// 23252723Sdim// Partial invalidor annotation allows to addess cases when ivars are 24252723Sdim// invalidated by other methods, which might or might not be called from 25252723Sdim// the invalidation method. The checker checks that each invalidation 26252723Sdim// method and all the partial methods cumulatively invalidate all ivars. 27252723Sdim// __attribute__((annotate("objc_instance_variable_invalidator_partial"))); 28252723Sdim// 29243791Sdim//===----------------------------------------------------------------------===// 30243791Sdim 31243791Sdim#include "ClangSACheckers.h" 32252723Sdim#include "clang/AST/Attr.h" 33252723Sdim#include "clang/AST/DeclObjC.h" 34252723Sdim#include "clang/AST/StmtVisitor.h" 35252723Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 36243791Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 37243791Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 38243791Sdim#include "llvm/ADT/DenseMap.h" 39252723Sdim#include "llvm/ADT/SetVector.h" 40243791Sdim#include "llvm/ADT/SmallString.h" 41243791Sdim 42243791Sdimusing namespace clang; 43243791Sdimusing namespace ento; 44243791Sdim 45243791Sdimnamespace { 46243791Sdim 47252723Sdimstruct ChecksFilter { 48252723Sdim /// Check for missing invalidation method declarations. 49252723Sdim DefaultBool check_MissingInvalidationMethod; 50252723Sdim /// Check that all ivars are invalidated. 51252723Sdim DefaultBool check_InstanceVariableInvalidation; 52252723Sdim}; 53252723Sdim 54252723Sdimclass IvarInvalidationCheckerImpl { 55252723Sdim 56252723Sdim typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet; 57243791Sdim typedef llvm::DenseMap<const ObjCMethodDecl*, 58243791Sdim const ObjCIvarDecl*> MethToIvarMapTy; 59243791Sdim typedef llvm::DenseMap<const ObjCPropertyDecl*, 60243791Sdim const ObjCIvarDecl*> PropToIvarMapTy; 61243791Sdim typedef llvm::DenseMap<const ObjCIvarDecl*, 62243791Sdim const ObjCPropertyDecl*> IvarToPropMapTy; 63243791Sdim 64243791Sdim 65252723Sdim struct InvalidationInfo { 66243791Sdim /// Has the ivar been invalidated? 67243791Sdim bool IsInvalidated; 68243791Sdim 69243791Sdim /// The methods which can be used to invalidate the ivar. 70243791Sdim MethodSet InvalidationMethods; 71243791Sdim 72252723Sdim InvalidationInfo() : IsInvalidated(false) {} 73243791Sdim void addInvalidationMethod(const ObjCMethodDecl *MD) { 74243791Sdim InvalidationMethods.insert(MD); 75243791Sdim } 76243791Sdim 77243791Sdim bool needsInvalidation() const { 78243791Sdim return !InvalidationMethods.empty(); 79243791Sdim } 80243791Sdim 81252723Sdim bool hasMethod(const ObjCMethodDecl *MD) { 82243791Sdim if (IsInvalidated) 83243791Sdim return true; 84243791Sdim for (MethodSet::iterator I = InvalidationMethods.begin(), 85243791Sdim E = InvalidationMethods.end(); I != E; ++I) { 86243791Sdim if (*I == MD) { 87243791Sdim IsInvalidated = true; 88243791Sdim return true; 89243791Sdim } 90243791Sdim } 91243791Sdim return false; 92243791Sdim } 93243791Sdim }; 94243791Sdim 95252723Sdim typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet; 96243791Sdim 97243791Sdim /// Statement visitor, which walks the method body and flags the ivars 98243791Sdim /// referenced in it (either directly or via property). 99243791Sdim class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { 100243791Sdim /// The set of Ivars which need to be invalidated. 101243791Sdim IvarSet &IVars; 102243791Sdim 103243791Sdim /// Flag is set as the result of a message send to another 104243791Sdim /// invalidation method. 105243791Sdim bool &CalledAnotherInvalidationMethod; 106243791Sdim 107243791Sdim /// Property setter to ivar mapping. 108243791Sdim const MethToIvarMapTy &PropertySetterToIvarMap; 109243791Sdim 110243791Sdim /// Property getter to ivar mapping. 111243791Sdim const MethToIvarMapTy &PropertyGetterToIvarMap; 112243791Sdim 113243791Sdim /// Property to ivar mapping. 114243791Sdim const PropToIvarMapTy &PropertyToIvarMap; 115243791Sdim 116243791Sdim /// The invalidation method being currently processed. 117243791Sdim const ObjCMethodDecl *InvalidationMethod; 118243791Sdim 119243791Sdim ASTContext &Ctx; 120243791Sdim 121243791Sdim /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr. 122243791Sdim const Expr *peel(const Expr *E) const; 123243791Sdim 124243791Sdim /// Does this expression represent zero: '0'? 125243791Sdim bool isZero(const Expr *E) const; 126243791Sdim 127243791Sdim /// Mark the given ivar as invalidated. 128243791Sdim void markInvalidated(const ObjCIvarDecl *Iv); 129243791Sdim 130243791Sdim /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as 131243791Sdim /// invalidated. 132243791Sdim void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef); 133243791Sdim 134243791Sdim /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks 135243791Sdim /// it as invalidated. 136243791Sdim void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA); 137243791Sdim 138243791Sdim /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar, 139243791Sdim /// if yes, marks it as invalidated. 140243791Sdim void checkObjCMessageExpr(const ObjCMessageExpr *ME); 141243791Sdim 142243791Sdim /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated. 143243791Sdim void check(const Expr *E); 144243791Sdim 145243791Sdim public: 146243791Sdim MethodCrawler(IvarSet &InIVars, 147243791Sdim bool &InCalledAnotherInvalidationMethod, 148243791Sdim const MethToIvarMapTy &InPropertySetterToIvarMap, 149243791Sdim const MethToIvarMapTy &InPropertyGetterToIvarMap, 150243791Sdim const PropToIvarMapTy &InPropertyToIvarMap, 151243791Sdim ASTContext &InCtx) 152243791Sdim : IVars(InIVars), 153243791Sdim CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod), 154243791Sdim PropertySetterToIvarMap(InPropertySetterToIvarMap), 155243791Sdim PropertyGetterToIvarMap(InPropertyGetterToIvarMap), 156243791Sdim PropertyToIvarMap(InPropertyToIvarMap), 157243791Sdim InvalidationMethod(0), 158243791Sdim Ctx(InCtx) {} 159243791Sdim 160243791Sdim void VisitStmt(const Stmt *S) { VisitChildren(S); } 161243791Sdim 162243791Sdim void VisitBinaryOperator(const BinaryOperator *BO); 163243791Sdim 164243791Sdim void VisitObjCMessageExpr(const ObjCMessageExpr *ME); 165243791Sdim 166243791Sdim void VisitChildren(const Stmt *S) { 167243791Sdim for (Stmt::const_child_range I = S->children(); I; ++I) { 168243791Sdim if (*I) 169243791Sdim this->Visit(*I); 170243791Sdim if (CalledAnotherInvalidationMethod) 171243791Sdim return; 172243791Sdim } 173243791Sdim } 174243791Sdim }; 175243791Sdim 176243791Sdim /// Check if the any of the methods inside the interface are annotated with 177243791Sdim /// the invalidation annotation, update the IvarInfo accordingly. 178252723Sdim /// \param LookForPartial is set when we are searching for partial 179252723Sdim /// invalidators. 180243791Sdim static void containsInvalidationMethod(const ObjCContainerDecl *D, 181252723Sdim InvalidationInfo &Out, 182252723Sdim bool LookForPartial); 183243791Sdim 184243791Sdim /// Check if ivar should be tracked and add to TrackedIvars if positive. 185243791Sdim /// Returns true if ivar should be tracked. 186252723Sdim static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars, 187252723Sdim const ObjCIvarDecl **FirstIvarDecl); 188243791Sdim 189243791Sdim /// Given the property declaration, and the list of tracked ivars, finds 190243791Sdim /// the ivar backing the property when possible. Returns '0' when no such 191243791Sdim /// ivar could be found. 192243791Sdim static const ObjCIvarDecl *findPropertyBackingIvar( 193243791Sdim const ObjCPropertyDecl *Prop, 194243791Sdim const ObjCInterfaceDecl *InterfaceD, 195252723Sdim IvarSet &TrackedIvars, 196252723Sdim const ObjCIvarDecl **FirstIvarDecl); 197243791Sdim 198252723Sdim /// Print ivar name or the property if the given ivar backs a property. 199252723Sdim static void printIvar(llvm::raw_svector_ostream &os, 200252723Sdim const ObjCIvarDecl *IvarDecl, 201252723Sdim const IvarToPropMapTy &IvarToPopertyMap); 202252723Sdim 203252723Sdim void reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl, 204252723Sdim const IvarToPropMapTy &IvarToPopertyMap, 205252723Sdim const ObjCInterfaceDecl *InterfaceD, 206252723Sdim bool MissingDeclaration) const; 207252723Sdim void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, 208252723Sdim const IvarToPropMapTy &IvarToPopertyMap, 209252723Sdim const ObjCMethodDecl *MethodD) const; 210252723Sdim 211252723Sdim AnalysisManager& Mgr; 212252723Sdim BugReporter &BR; 213252723Sdim /// Filter on the checks performed. 214252723Sdim const ChecksFilter &Filter; 215252723Sdim 216243791Sdimpublic: 217252723Sdim IvarInvalidationCheckerImpl(AnalysisManager& InMgr, 218252723Sdim BugReporter &InBR, 219252723Sdim const ChecksFilter &InFilter) : 220252723Sdim Mgr (InMgr), BR(InBR), Filter(InFilter) {} 221243791Sdim 222252723Sdim void visit(const ObjCImplementationDecl *D) const; 223243791Sdim}; 224243791Sdim 225252723Sdimstatic bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) { 226243791Sdim for (specific_attr_iterator<AnnotateAttr> 227243791Sdim AI = M->specific_attr_begin<AnnotateAttr>(), 228243791Sdim AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) { 229243791Sdim const AnnotateAttr *Ann = *AI; 230252723Sdim if (!LookForPartial && 231252723Sdim Ann->getAnnotation() == "objc_instance_variable_invalidator") 232243791Sdim return true; 233252723Sdim if (LookForPartial && 234252723Sdim Ann->getAnnotation() == "objc_instance_variable_invalidator_partial") 235252723Sdim return true; 236243791Sdim } 237243791Sdim return false; 238243791Sdim} 239243791Sdim 240252723Sdimvoid IvarInvalidationCheckerImpl::containsInvalidationMethod( 241252723Sdim const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) { 242243791Sdim 243243791Sdim if (!D) 244243791Sdim return; 245243791Sdim 246252723Sdim assert(!isa<ObjCImplementationDecl>(D)); 247252723Sdim // TODO: Cache the results. 248252723Sdim 249243791Sdim // Check all methods. 250243791Sdim for (ObjCContainerDecl::method_iterator 251243791Sdim I = D->meth_begin(), 252243791Sdim E = D->meth_end(); I != E; ++I) { 253243791Sdim const ObjCMethodDecl *MDI = *I; 254252723Sdim if (isInvalidationMethod(MDI, Partial)) 255243791Sdim OutInfo.addInvalidationMethod( 256243791Sdim cast<ObjCMethodDecl>(MDI->getCanonicalDecl())); 257243791Sdim } 258243791Sdim 259243791Sdim // If interface, check all parent protocols and super. 260252723Sdim if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) { 261252723Sdim 262252723Sdim // Visit all protocols. 263243791Sdim for (ObjCInterfaceDecl::protocol_iterator 264252723Sdim I = InterfD->protocol_begin(), 265252723Sdim E = InterfD->protocol_end(); I != E; ++I) { 266252723Sdim containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial); 267243791Sdim } 268252723Sdim 269252723Sdim // Visit all categories in case the invalidation method is declared in 270252723Sdim // a category. 271252723Sdim for (ObjCInterfaceDecl::visible_extensions_iterator 272252723Sdim Ext = InterfD->visible_extensions_begin(), 273252723Sdim ExtEnd = InterfD->visible_extensions_end(); 274252723Sdim Ext != ExtEnd; ++Ext) { 275252723Sdim containsInvalidationMethod(*Ext, OutInfo, Partial); 276252723Sdim } 277252723Sdim 278252723Sdim containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial); 279243791Sdim return; 280243791Sdim } 281243791Sdim 282243791Sdim // If protocol, check all parent protocols. 283243791Sdim if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) { 284243791Sdim for (ObjCInterfaceDecl::protocol_iterator 285243791Sdim I = ProtD->protocol_begin(), 286243791Sdim E = ProtD->protocol_end(); I != E; ++I) { 287252723Sdim containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial); 288243791Sdim } 289243791Sdim return; 290243791Sdim } 291243791Sdim 292252723Sdim return; 293243791Sdim} 294243791Sdim 295252723Sdimbool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv, 296252723Sdim IvarSet &TrackedIvars, 297252723Sdim const ObjCIvarDecl **FirstIvarDecl) { 298243791Sdim QualType IvQTy = Iv->getType(); 299243791Sdim const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>(); 300243791Sdim if (!IvTy) 301243791Sdim return false; 302243791Sdim const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl(); 303243791Sdim 304252723Sdim InvalidationInfo Info; 305252723Sdim containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false); 306243791Sdim if (Info.needsInvalidation()) { 307252723Sdim const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl()); 308252723Sdim TrackedIvars[I] = Info; 309252723Sdim if (!*FirstIvarDecl) 310252723Sdim *FirstIvarDecl = I; 311243791Sdim return true; 312243791Sdim } 313243791Sdim return false; 314243791Sdim} 315243791Sdim 316252723Sdimconst ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar( 317243791Sdim const ObjCPropertyDecl *Prop, 318243791Sdim const ObjCInterfaceDecl *InterfaceD, 319252723Sdim IvarSet &TrackedIvars, 320252723Sdim const ObjCIvarDecl **FirstIvarDecl) { 321243791Sdim const ObjCIvarDecl *IvarD = 0; 322243791Sdim 323243791Sdim // Lookup for the synthesized case. 324243791Sdim IvarD = Prop->getPropertyIvarDecl(); 325252723Sdim // We only track the ivars/properties that are defined in the current 326252723Sdim // class (not the parent). 327252723Sdim if (IvarD && IvarD->getContainingInterface() == InterfaceD) { 328243791Sdim if (TrackedIvars.count(IvarD)) { 329243791Sdim return IvarD; 330243791Sdim } 331243791Sdim // If the ivar is synthesized we still want to track it. 332252723Sdim if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl)) 333243791Sdim return IvarD; 334243791Sdim } 335243791Sdim 336243791Sdim // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars. 337243791Sdim StringRef PropName = Prop->getIdentifier()->getName(); 338243791Sdim for (IvarSet::const_iterator I = TrackedIvars.begin(), 339243791Sdim E = TrackedIvars.end(); I != E; ++I) { 340243791Sdim const ObjCIvarDecl *Iv = I->first; 341243791Sdim StringRef IvarName = Iv->getName(); 342243791Sdim 343243791Sdim if (IvarName == PropName) 344243791Sdim return Iv; 345243791Sdim 346243791Sdim SmallString<128> PropNameWithUnderscore; 347243791Sdim { 348243791Sdim llvm::raw_svector_ostream os(PropNameWithUnderscore); 349243791Sdim os << '_' << PropName; 350243791Sdim } 351243791Sdim if (IvarName == PropNameWithUnderscore.str()) 352243791Sdim return Iv; 353243791Sdim } 354243791Sdim 355243791Sdim // Note, this is a possible source of false positives. We could look at the 356243791Sdim // getter implementation to find the ivar when its name is not derived from 357243791Sdim // the property name. 358243791Sdim return 0; 359243791Sdim} 360243791Sdim 361252723Sdimvoid IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os, 362252723Sdim const ObjCIvarDecl *IvarDecl, 363252723Sdim const IvarToPropMapTy &IvarToPopertyMap) { 364252723Sdim if (IvarDecl->getSynthesize()) { 365252723Sdim const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl); 366252723Sdim assert(PD &&"Do we synthesize ivars for something other than properties?"); 367252723Sdim os << "Property "<< PD->getName() << " "; 368252723Sdim } else { 369252723Sdim os << "Instance variable "<< IvarDecl->getName() << " "; 370252723Sdim } 371252723Sdim} 372243791Sdim 373252723Sdim// Check that the invalidatable interfaces with ivars/properties implement the 374252723Sdim// invalidation methods. 375252723Sdimvoid IvarInvalidationCheckerImpl:: 376252723Sdimvisit(const ObjCImplementationDecl *ImplD) const { 377243791Sdim // Collect all ivars that need cleanup. 378243791Sdim IvarSet Ivars; 379252723Sdim // Record the first Ivar needing invalidation; used in reporting when only 380252723Sdim // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure 381252723Sdim // deterministic output. 382252723Sdim const ObjCIvarDecl *FirstIvarDecl = 0; 383252723Sdim const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface(); 384243791Sdim 385243791Sdim // Collect ivars declared in this class, its extensions and its implementation 386243791Sdim ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD); 387243791Sdim for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv; 388243791Sdim Iv= Iv->getNextIvar()) 389252723Sdim trackIvar(Iv, Ivars, &FirstIvarDecl); 390243791Sdim 391243791Sdim // Construct Property/Property Accessor to Ivar maps to assist checking if an 392243791Sdim // ivar which is backing a property has been reset. 393243791Sdim MethToIvarMapTy PropSetterToIvarMap; 394243791Sdim MethToIvarMapTy PropGetterToIvarMap; 395243791Sdim PropToIvarMapTy PropertyToIvarMap; 396243791Sdim IvarToPropMapTy IvarToPopertyMap; 397243791Sdim 398243791Sdim ObjCInterfaceDecl::PropertyMap PropMap; 399252723Sdim ObjCInterfaceDecl::PropertyDeclOrder PropOrder; 400252723Sdim InterfaceD->collectPropertiesToImplement(PropMap, PropOrder); 401243791Sdim 402243791Sdim for (ObjCInterfaceDecl::PropertyMap::iterator 403243791Sdim I = PropMap.begin(), E = PropMap.end(); I != E; ++I) { 404243791Sdim const ObjCPropertyDecl *PD = I->second; 405243791Sdim 406252723Sdim const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars, 407252723Sdim &FirstIvarDecl); 408252723Sdim if (!ID) 409243791Sdim continue; 410243791Sdim 411243791Sdim // Store the mappings. 412243791Sdim PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); 413243791Sdim PropertyToIvarMap[PD] = ID; 414243791Sdim IvarToPopertyMap[ID] = PD; 415243791Sdim 416243791Sdim // Find the setter and the getter. 417243791Sdim const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl(); 418243791Sdim if (SetterD) { 419243791Sdim SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl()); 420243791Sdim PropSetterToIvarMap[SetterD] = ID; 421243791Sdim } 422243791Sdim 423243791Sdim const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl(); 424243791Sdim if (GetterD) { 425243791Sdim GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl()); 426243791Sdim PropGetterToIvarMap[GetterD] = ID; 427243791Sdim } 428243791Sdim } 429243791Sdim 430252723Sdim // If no ivars need invalidation, there is nothing to check here. 431252723Sdim if (Ivars.empty()) 432252723Sdim return; 433243791Sdim 434252723Sdim // Find all partial invalidation methods. 435252723Sdim InvalidationInfo PartialInfo; 436252723Sdim containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true); 437243791Sdim 438252723Sdim // Remove ivars invalidated by the partial invalidation methods. They do not 439252723Sdim // need to be invalidated in the regular invalidation methods. 440252723Sdim bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false; 441252723Sdim for (MethodSet::iterator 442252723Sdim I = PartialInfo.InvalidationMethods.begin(), 443252723Sdim E = PartialInfo.InvalidationMethods.end(); I != E; ++I) { 444252723Sdim const ObjCMethodDecl *InterfD = *I; 445252723Sdim 446252723Sdim // Get the corresponding method in the @implementation. 447252723Sdim const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), 448252723Sdim InterfD->isInstanceMethod()); 449252723Sdim if (D && D->hasBody()) { 450252723Sdim AtImplementationContainsAtLeastOnePartialInvalidationMethod = true; 451252723Sdim 452252723Sdim bool CalledAnotherInvalidationMethod = false; 453252723Sdim // The MethodCrowler is going to remove the invalidated ivars. 454252723Sdim MethodCrawler(Ivars, 455252723Sdim CalledAnotherInvalidationMethod, 456252723Sdim PropSetterToIvarMap, 457252723Sdim PropGetterToIvarMap, 458252723Sdim PropertyToIvarMap, 459252723Sdim BR.getContext()).VisitStmt(D->getBody()); 460252723Sdim // If another invalidation method was called, trust that full invalidation 461252723Sdim // has occurred. 462252723Sdim if (CalledAnotherInvalidationMethod) 463252723Sdim Ivars.clear(); 464252723Sdim } 465252723Sdim } 466252723Sdim 467252723Sdim // If all ivars have been invalidated by partial invalidators, there is 468252723Sdim // nothing to check here. 469252723Sdim if (Ivars.empty()) 470243791Sdim return; 471243791Sdim 472252723Sdim // Find all invalidation methods in this @interface declaration and parents. 473252723Sdim InvalidationInfo Info; 474252723Sdim containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false); 475243791Sdim 476252723Sdim // Report an error in case none of the invalidation methods are declared. 477252723Sdim if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) { 478252723Sdim if (Filter.check_MissingInvalidationMethod) 479252723Sdim reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, 480252723Sdim /*MissingDeclaration*/ true); 481252723Sdim // If there are no invalidation methods, there is no ivar validation work 482252723Sdim // to be done. 483252723Sdim return; 484252723Sdim } 485243791Sdim 486252723Sdim // Only check if Ivars are invalidated when InstanceVariableInvalidation 487252723Sdim // has been requested. 488252723Sdim if (!Filter.check_InstanceVariableInvalidation) 489252723Sdim return; 490243791Sdim 491252723Sdim // Check that all ivars are invalidated by the invalidation methods. 492252723Sdim bool AtImplementationContainsAtLeastOneInvalidationMethod = false; 493252723Sdim for (MethodSet::iterator I = Info.InvalidationMethods.begin(), 494252723Sdim E = Info.InvalidationMethods.end(); I != E; ++I) { 495252723Sdim const ObjCMethodDecl *InterfD = *I; 496243791Sdim 497252723Sdim // Get the corresponding method in the @implementation. 498252723Sdim const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), 499252723Sdim InterfD->isInstanceMethod()); 500252723Sdim if (D && D->hasBody()) { 501252723Sdim AtImplementationContainsAtLeastOneInvalidationMethod = true; 502252723Sdim 503252723Sdim // Get a copy of ivars needing invalidation. 504252723Sdim IvarSet IvarsI = Ivars; 505252723Sdim 506252723Sdim bool CalledAnotherInvalidationMethod = false; 507252723Sdim MethodCrawler(IvarsI, 508252723Sdim CalledAnotherInvalidationMethod, 509252723Sdim PropSetterToIvarMap, 510252723Sdim PropGetterToIvarMap, 511252723Sdim PropertyToIvarMap, 512252723Sdim BR.getContext()).VisitStmt(D->getBody()); 513252723Sdim // If another invalidation method was called, trust that full invalidation 514252723Sdim // has occurred. 515252723Sdim if (CalledAnotherInvalidationMethod) 516252723Sdim continue; 517252723Sdim 518252723Sdim // Warn on the ivars that were not invalidated by the method. 519252723Sdim for (IvarSet::const_iterator 520252723Sdim I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I) 521252723Sdim reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D); 522243791Sdim } 523243791Sdim } 524252723Sdim 525252723Sdim // Report an error in case none of the invalidation methods are implemented. 526252723Sdim if (!AtImplementationContainsAtLeastOneInvalidationMethod) { 527252723Sdim if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) { 528252723Sdim // Warn on the ivars that were not invalidated by the prrtial 529252723Sdim // invalidation methods. 530252723Sdim for (IvarSet::const_iterator 531252723Sdim I = Ivars.begin(), E = Ivars.end(); I != E; ++I) 532252723Sdim reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, 0); 533252723Sdim } else { 534252723Sdim // Otherwise, no invalidation methods were implemented. 535252723Sdim reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD, 536252723Sdim /*MissingDeclaration*/ false); 537252723Sdim } 538252723Sdim } 539243791Sdim} 540243791Sdim 541252723Sdimvoid IvarInvalidationCheckerImpl:: 542252723SdimreportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl, 543252723Sdim const IvarToPropMapTy &IvarToPopertyMap, 544252723Sdim const ObjCInterfaceDecl *InterfaceD, 545252723Sdim bool MissingDeclaration) const { 546252723Sdim SmallString<128> sbuf; 547252723Sdim llvm::raw_svector_ostream os(sbuf); 548252723Sdim assert(FirstIvarDecl); 549252723Sdim printIvar(os, FirstIvarDecl, IvarToPopertyMap); 550252723Sdim os << "needs to be invalidated; "; 551252723Sdim if (MissingDeclaration) 552252723Sdim os << "no invalidation method is declared for "; 553252723Sdim else 554252723Sdim os << "no invalidation method is defined in the @implementation for "; 555252723Sdim os << InterfaceD->getName(); 556252723Sdim 557252723Sdim PathDiagnosticLocation IvarDecLocation = 558252723Sdim PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager()); 559252723Sdim 560252723Sdim BR.EmitBasicReport(FirstIvarDecl, "Incomplete invalidation", 561252723Sdim categories::CoreFoundationObjectiveC, os.str(), 562252723Sdim IvarDecLocation); 563252723Sdim} 564252723Sdim 565252723Sdimvoid IvarInvalidationCheckerImpl:: 566252723SdimreportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, 567252723Sdim const IvarToPropMapTy &IvarToPopertyMap, 568252723Sdim const ObjCMethodDecl *MethodD) const { 569252723Sdim SmallString<128> sbuf; 570252723Sdim llvm::raw_svector_ostream os(sbuf); 571252723Sdim printIvar(os, IvarD, IvarToPopertyMap); 572252723Sdim os << "needs to be invalidated or set to nil"; 573252723Sdim if (MethodD) { 574252723Sdim PathDiagnosticLocation MethodDecLocation = 575252723Sdim PathDiagnosticLocation::createEnd(MethodD->getBody(), 576252723Sdim BR.getSourceManager(), 577252723Sdim Mgr.getAnalysisDeclContext(MethodD)); 578252723Sdim BR.EmitBasicReport(MethodD, "Incomplete invalidation", 579252723Sdim categories::CoreFoundationObjectiveC, os.str(), 580252723Sdim MethodDecLocation); 581252723Sdim } else { 582252723Sdim BR.EmitBasicReport(IvarD, "Incomplete invalidation", 583252723Sdim categories::CoreFoundationObjectiveC, os.str(), 584252723Sdim PathDiagnosticLocation::createBegin(IvarD, 585252723Sdim BR.getSourceManager())); 586252723Sdim 587252723Sdim } 588252723Sdim} 589252723Sdim 590252723Sdimvoid IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated( 591243791Sdim const ObjCIvarDecl *Iv) { 592243791Sdim IvarSet::iterator I = IVars.find(Iv); 593243791Sdim if (I != IVars.end()) { 594243791Sdim // If InvalidationMethod is present, we are processing the message send and 595243791Sdim // should ensure we are invalidating with the appropriate method, 596243791Sdim // otherwise, we are processing setting to 'nil'. 597252723Sdim if (!InvalidationMethod || 598252723Sdim (InvalidationMethod && I->second.hasMethod(InvalidationMethod))) 599252723Sdim IVars.erase(I); 600243791Sdim } 601243791Sdim} 602243791Sdim 603252723Sdimconst Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const { 604243791Sdim E = E->IgnoreParenCasts(); 605243791Sdim if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) 606243791Sdim E = POE->getSyntacticForm()->IgnoreParenCasts(); 607243791Sdim if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) 608243791Sdim E = OVE->getSourceExpr()->IgnoreParenCasts(); 609243791Sdim return E; 610243791Sdim} 611243791Sdim 612252723Sdimvoid IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr( 613243791Sdim const ObjCIvarRefExpr *IvarRef) { 614243791Sdim if (const Decl *D = IvarRef->getDecl()) 615243791Sdim markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl())); 616243791Sdim} 617243791Sdim 618252723Sdimvoid IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr( 619243791Sdim const ObjCMessageExpr *ME) { 620243791Sdim const ObjCMethodDecl *MD = ME->getMethodDecl(); 621243791Sdim if (MD) { 622243791Sdim MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 623243791Sdim MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD); 624243791Sdim if (IvI != PropertyGetterToIvarMap.end()) 625243791Sdim markInvalidated(IvI->second); 626243791Sdim } 627243791Sdim} 628243791Sdim 629252723Sdimvoid IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr( 630243791Sdim const ObjCPropertyRefExpr *PA) { 631243791Sdim 632243791Sdim if (PA->isExplicitProperty()) { 633243791Sdim const ObjCPropertyDecl *PD = PA->getExplicitProperty(); 634243791Sdim if (PD) { 635243791Sdim PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); 636243791Sdim PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD); 637243791Sdim if (IvI != PropertyToIvarMap.end()) 638243791Sdim markInvalidated(IvI->second); 639243791Sdim return; 640243791Sdim } 641243791Sdim } 642243791Sdim 643243791Sdim if (PA->isImplicitProperty()) { 644243791Sdim const ObjCMethodDecl *MD = PA->getImplicitPropertySetter(); 645243791Sdim if (MD) { 646243791Sdim MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 647243791Sdim MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD); 648243791Sdim if (IvI != PropertyGetterToIvarMap.end()) 649243791Sdim markInvalidated(IvI->second); 650243791Sdim return; 651243791Sdim } 652243791Sdim } 653243791Sdim} 654243791Sdim 655252723Sdimbool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const { 656243791Sdim E = peel(E); 657243791Sdim 658243791Sdim return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull) 659243791Sdim != Expr::NPCK_NotNull); 660243791Sdim} 661243791Sdim 662252723Sdimvoid IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) { 663243791Sdim E = peel(E); 664243791Sdim 665243791Sdim if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { 666243791Sdim checkObjCIvarRefExpr(IvarRef); 667243791Sdim return; 668243791Sdim } 669243791Sdim 670243791Sdim if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) { 671243791Sdim checkObjCPropertyRefExpr(PropRef); 672243791Sdim return; 673243791Sdim } 674243791Sdim 675243791Sdim if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) { 676243791Sdim checkObjCMessageExpr(MsgExpr); 677243791Sdim return; 678243791Sdim } 679243791Sdim} 680243791Sdim 681252723Sdimvoid IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator( 682243791Sdim const BinaryOperator *BO) { 683243791Sdim VisitStmt(BO); 684243791Sdim 685252723Sdim // Do we assign/compare against zero? If yes, check the variable we are 686252723Sdim // assigning to. 687252723Sdim BinaryOperatorKind Opcode = BO->getOpcode(); 688252723Sdim if (Opcode != BO_Assign && 689252723Sdim Opcode != BO_EQ && 690252723Sdim Opcode != BO_NE) 691243791Sdim return; 692243791Sdim 693252723Sdim if (isZero(BO->getRHS())) { 694252723Sdim check(BO->getLHS()); 695252723Sdim return; 696252723Sdim } 697252723Sdim 698252723Sdim if (Opcode != BO_Assign && isZero(BO->getLHS())) { 699252723Sdim check(BO->getRHS()); 700243791Sdim return; 701252723Sdim } 702243791Sdim} 703243791Sdim 704252723Sdimvoid IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr( 705252723Sdim const ObjCMessageExpr *ME) { 706243791Sdim const ObjCMethodDecl *MD = ME->getMethodDecl(); 707243791Sdim const Expr *Receiver = ME->getInstanceReceiver(); 708243791Sdim 709243791Sdim // Stop if we are calling '[self invalidate]'. 710252723Sdim if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false)) 711243791Sdim if (Receiver->isObjCSelfExpr()) { 712243791Sdim CalledAnotherInvalidationMethod = true; 713243791Sdim return; 714243791Sdim } 715243791Sdim 716243791Sdim // Check if we call a setter and set the property to 'nil'. 717243791Sdim if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) { 718243791Sdim MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 719243791Sdim MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD); 720243791Sdim if (IvI != PropertySetterToIvarMap.end()) { 721243791Sdim markInvalidated(IvI->second); 722243791Sdim return; 723243791Sdim } 724243791Sdim } 725243791Sdim 726243791Sdim // Check if we call the 'invalidation' routine on the ivar. 727243791Sdim if (Receiver) { 728243791Sdim InvalidationMethod = MD; 729243791Sdim check(Receiver->IgnoreParenCasts()); 730243791Sdim InvalidationMethod = 0; 731243791Sdim } 732243791Sdim 733243791Sdim VisitStmt(ME); 734243791Sdim} 735243791Sdim} 736243791Sdim 737252723Sdim// Register the checkers. 738252723Sdimnamespace { 739252723Sdim 740252723Sdimclass IvarInvalidationChecker : 741252723Sdim public Checker<check::ASTDecl<ObjCImplementationDecl> > { 742252723Sdimpublic: 743252723Sdim ChecksFilter Filter; 744252723Sdimpublic: 745252723Sdim void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, 746252723Sdim BugReporter &BR) const { 747252723Sdim IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter); 748252723Sdim Walker.visit(D); 749252723Sdim } 750252723Sdim}; 751243791Sdim} 752252723Sdim 753252723Sdim#define REGISTER_CHECKER(name) \ 754252723Sdimvoid ento::register##name(CheckerManager &mgr) {\ 755252723Sdim mgr.registerChecker<IvarInvalidationChecker>()->Filter.check_##name = true;\ 756252723Sdim} 757252723Sdim 758252723SdimREGISTER_CHECKER(InstanceVariableInvalidation) 759252723SdimREGISTER_CHECKER(MissingInvalidationMethod) 760252723Sdim 761