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