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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This checker implements annotation driven invalidation checking. If a class 10 // contains a method annotated with 'objc_instance_variable_invalidator', 11 // - (void) foo 12 // __attribute__((annotate("objc_instance_variable_invalidator"))); 13 // all the "ivalidatable" instance variables of this class should be 14 // invalidated. We call an instance variable ivalidatable if it is an object of 15 // a class which contains an invalidation method. There could be multiple 16 // methods annotated with such annotations per class, either one can be used 17 // to invalidate the ivar. An ivar or property are considered to be 18 // invalidated if they are being assigned 'nil' or an invalidation method has 19 // been called on them. An invalidation method should either invalidate all 20 // the ivars or call another invalidation method (on self). 21 // 22 // Partial invalidor annotation allows to address cases when ivars are 23 // invalidated by other methods, which might or might not be called from 24 // the invalidation method. The checker checks that each invalidation 25 // 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 41 using namespace clang; 42 using namespace ento; 43 44 namespace { 45 struct 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 55 class 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 ivars 96 /// 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 another 102 /// 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 as 129 /// invalidated. 130 void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef); 131 132 /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks 133 /// 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 with 175 /// the invalidation annotation, update the IvarInfo accordingly. 176 /// \param LookForPartial is set when we are searching for partial 177 /// 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, finds 188 /// the ivar backing the property when possible. Returns '0' when no such 189 /// 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 216 public: 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 225 static 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 237 void 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 in 260 // 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 277 bool 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 298 const 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 current 308 // 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 the 336 // getter implementation to find the ivar when its name is not derived from 337 // the property name. 338 return nullptr; 339 } 340 341 void 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 the 354 // invalidation methods. 355 void IvarInvalidationCheckerImpl:: 356 visit(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 only 360 // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure 361 // 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 implementation 366 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 an 372 // 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 not 418 // 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 invalidation 436 // has occurred. 437 if (CalledAnotherInvalidationMethod) 438 Ivars.clear(); 439 } 440 } 441 442 // If all ivars have been invalidated by partial invalidators, there is 443 // 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 work 458 // to be done. 459 return; 460 } 461 462 // Only check if Ivars are invalidated when InstanceVariableInvalidation 463 // 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 invalidation 487 // 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 prrtial 501 // 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 513 void 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 else 525 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 536 void IvarInvalidationCheckerImpl:: 537 reportIvarNeedsInvalidation(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 562 void 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 and 567 // 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 574 const 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 583 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr( 584 const ObjCIvarRefExpr *IvarRef) { 585 if (const Decl *D = IvarRef->getDecl()) 586 markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl())); 587 } 588 589 void 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 600 void 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 626 bool 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 633 void 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 652 void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator( 653 const BinaryOperator *BO) { 654 VisitStmt(BO); 655 656 // Do we assign/compare against zero? If yes, check the variable we are 657 // 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 675 void 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 namespace 707 708 // Register the checkers. 709 namespace { 710 class IvarInvalidationChecker : 711 public Checker<check::ASTDecl<ObjCImplementationDecl> > { 712 public: 713 ChecksFilter Filter; 714 public: 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 namespace 722 723 void ento::registerIvarInvalidationModeling(CheckerManager &mgr) { 724 mgr.registerChecker<IvarInvalidationChecker>(); 725 } 726 727 bool 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 741 REGISTER_CHECKER(InstanceVariableInvalidation) 742 REGISTER_CHECKER(MissingInvalidationMethod) 743