clang API Documentation
00001 //=- IvarInvalidationChecker.cpp - -*- C++ -------------------------------*-==// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This checker implements annotation driven invalidation checking. If a class 00011 // contains a method annotated with 'objc_instance_variable_invalidator', 00012 // - (void) foo 00013 // __attribute__((annotate("objc_instance_variable_invalidator"))); 00014 // all the "ivalidatable" instance variables of this class should be 00015 // invalidated. We call an instance variable ivalidatable if it is an object of 00016 // a class which contains an invalidation method. There could be multiple 00017 // methods annotated with such annotations per class, either one can be used 00018 // to invalidate the ivar. An ivar or property are considered to be 00019 // invalidated if they are being assigned 'nil' or an invalidation method has 00020 // been called on them. An invalidation method should either invalidate all 00021 // the ivars or call another invalidation method (on self). 00022 // 00023 // Partial invalidor annotation allows to addess cases when ivars are 00024 // invalidated by other methods, which might or might not be called from 00025 // the invalidation method. The checker checks that each invalidation 00026 // method and all the partial methods cumulatively invalidate all ivars. 00027 // __attribute__((annotate("objc_instance_variable_invalidator_partial"))); 00028 // 00029 //===----------------------------------------------------------------------===// 00030 00031 #include "ClangSACheckers.h" 00032 #include "clang/AST/Attr.h" 00033 #include "clang/AST/DeclObjC.h" 00034 #include "clang/AST/StmtVisitor.h" 00035 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 00036 #include "clang/StaticAnalyzer/Core/Checker.h" 00037 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 00038 #include "llvm/ADT/DenseMap.h" 00039 #include "llvm/ADT/SetVector.h" 00040 #include "llvm/ADT/SmallString.h" 00041 00042 using namespace clang; 00043 using namespace ento; 00044 00045 namespace { 00046 00047 struct ChecksFilter { 00048 /// Check for missing invalidation method declarations. 00049 DefaultBool check_MissingInvalidationMethod; 00050 /// Check that all ivars are invalidated. 00051 DefaultBool check_InstanceVariableInvalidation; 00052 00053 CheckName checkName_MissingInvalidationMethod; 00054 CheckName checkName_InstanceVariableInvalidation; 00055 }; 00056 00057 class IvarInvalidationCheckerImpl { 00058 00059 typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet; 00060 typedef llvm::DenseMap<const ObjCMethodDecl*, 00061 const ObjCIvarDecl*> MethToIvarMapTy; 00062 typedef llvm::DenseMap<const ObjCPropertyDecl*, 00063 const ObjCIvarDecl*> PropToIvarMapTy; 00064 typedef llvm::DenseMap<const ObjCIvarDecl*, 00065 const ObjCPropertyDecl*> IvarToPropMapTy; 00066 00067 00068 struct InvalidationInfo { 00069 /// Has the ivar been invalidated? 00070 bool IsInvalidated; 00071 00072 /// The methods which can be used to invalidate the ivar. 00073 MethodSet InvalidationMethods; 00074 00075 InvalidationInfo() : IsInvalidated(false) {} 00076 void addInvalidationMethod(const ObjCMethodDecl *MD) { 00077 InvalidationMethods.insert(MD); 00078 } 00079 00080 bool needsInvalidation() const { 00081 return !InvalidationMethods.empty(); 00082 } 00083 00084 bool hasMethod(const ObjCMethodDecl *MD) { 00085 if (IsInvalidated) 00086 return true; 00087 for (MethodSet::iterator I = InvalidationMethods.begin(), 00088 E = InvalidationMethods.end(); I != E; ++I) { 00089 if (*I == MD) { 00090 IsInvalidated = true; 00091 return true; 00092 } 00093 } 00094 return false; 00095 } 00096 }; 00097 00098 typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet; 00099 00100 /// Statement visitor, which walks the method body and flags the ivars 00101 /// referenced in it (either directly or via property). 00102 class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { 00103 /// The set of Ivars which need to be invalidated. 00104 IvarSet &IVars; 00105 00106 /// Flag is set as the result of a message send to another 00107 /// invalidation method. 00108 bool &CalledAnotherInvalidationMethod; 00109 00110 /// Property setter to ivar mapping. 00111 const MethToIvarMapTy &PropertySetterToIvarMap; 00112 00113 /// Property getter to ivar mapping. 00114 const MethToIvarMapTy &PropertyGetterToIvarMap; 00115 00116 /// Property to ivar mapping. 00117 const PropToIvarMapTy &PropertyToIvarMap; 00118 00119 /// The invalidation method being currently processed. 00120 const ObjCMethodDecl *InvalidationMethod; 00121 00122 ASTContext &Ctx; 00123 00124 /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr. 00125 const Expr *peel(const Expr *E) const; 00126 00127 /// Does this expression represent zero: '0'? 00128 bool isZero(const Expr *E) const; 00129 00130 /// Mark the given ivar as invalidated. 00131 void markInvalidated(const ObjCIvarDecl *Iv); 00132 00133 /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as 00134 /// invalidated. 00135 void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef); 00136 00137 /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks 00138 /// it as invalidated. 00139 void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA); 00140 00141 /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar, 00142 /// if yes, marks it as invalidated. 00143 void checkObjCMessageExpr(const ObjCMessageExpr *ME); 00144 00145 /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated. 00146 void check(const Expr *E); 00147 00148 public: 00149 MethodCrawler(IvarSet &InIVars, 00150 bool &InCalledAnotherInvalidationMethod, 00151 const MethToIvarMapTy &InPropertySetterToIvarMap, 00152 const MethToIvarMapTy &InPropertyGetterToIvarMap, 00153 const PropToIvarMapTy &InPropertyToIvarMap, 00154 ASTContext &InCtx) 00155 : IVars(InIVars), 00156 CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod), 00157 PropertySetterToIvarMap(InPropertySetterToIvarMap), 00158 PropertyGetterToIvarMap(InPropertyGetterToIvarMap), 00159 PropertyToIvarMap(InPropertyToIvarMap), 00160 InvalidationMethod(nullptr), 00161 Ctx(InCtx) {} 00162 00163 void VisitStmt(const Stmt *S) { VisitChildren(S); } 00164 00165 void VisitBinaryOperator(const BinaryOperator *BO); 00166 00167 void VisitObjCMessageExpr(const ObjCMessageExpr *ME); 00168 00169 void VisitChildren(const Stmt *S) { 00170 for (Stmt::const_child_range I = S->children(); I; ++I) { 00171 if (*I) 00172 this->Visit(*I); 00173 if (CalledAnotherInvalidationMethod) 00174 return; 00175 } 00176 } 00177 }; 00178 00179 /// Check if the any of the methods inside the interface are annotated with 00180 /// the invalidation annotation, update the IvarInfo accordingly. 00181 /// \param LookForPartial is set when we are searching for partial 00182 /// invalidators. 00183 static void containsInvalidationMethod(const ObjCContainerDecl *D, 00184 InvalidationInfo &Out, 00185 bool LookForPartial); 00186 00187 /// Check if ivar should be tracked and add to TrackedIvars if positive. 00188 /// Returns true if ivar should be tracked. 00189 static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars, 00190 const ObjCIvarDecl **FirstIvarDecl); 00191 00192 /// Given the property declaration, and the list of tracked ivars, finds 00193 /// the ivar backing the property when possible. Returns '0' when no such 00194 /// ivar could be found. 00195 static const ObjCIvarDecl *findPropertyBackingIvar( 00196 const ObjCPropertyDecl *Prop, 00197 const ObjCInterfaceDecl *InterfaceD, 00198 IvarSet &TrackedIvars, 00199 const ObjCIvarDecl **FirstIvarDecl); 00200 00201 /// Print ivar name or the property if the given ivar backs a property. 00202 static void printIvar(llvm::raw_svector_ostream &os, 00203 const ObjCIvarDecl *IvarDecl, 00204 const IvarToPropMapTy &IvarToPopertyMap); 00205 00206 void reportNoInvalidationMethod(CheckName CheckName, 00207 const ObjCIvarDecl *FirstIvarDecl, 00208 const IvarToPropMapTy &IvarToPopertyMap, 00209 const ObjCInterfaceDecl *InterfaceD, 00210 bool MissingDeclaration) const; 00211 void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, 00212 const IvarToPropMapTy &IvarToPopertyMap, 00213 const ObjCMethodDecl *MethodD) const; 00214 00215 AnalysisManager& Mgr; 00216 BugReporter &BR; 00217 /// Filter on the checks performed. 00218 const ChecksFilter &Filter; 00219 00220 public: 00221 IvarInvalidationCheckerImpl(AnalysisManager& InMgr, 00222 BugReporter &InBR, 00223 const ChecksFilter &InFilter) : 00224 Mgr (InMgr), BR(InBR), Filter(InFilter) {} 00225 00226 void visit(const ObjCImplementationDecl *D) const; 00227 }; 00228 00229 static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) { 00230 for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) { 00231 if (!LookForPartial && 00232 Ann->getAnnotation() == "objc_instance_variable_invalidator") 00233 return true; 00234 if (LookForPartial && 00235 Ann->getAnnotation() == "objc_instance_variable_invalidator_partial") 00236 return true; 00237 } 00238 return false; 00239 } 00240 00241 void IvarInvalidationCheckerImpl::containsInvalidationMethod( 00242 const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) { 00243 00244 if (!D) 00245 return; 00246 00247 assert(!isa<ObjCImplementationDecl>(D)); 00248 // TODO: Cache the results. 00249 00250 // Check all methods. 00251 for (const auto *MDI : D->methods()) 00252 if (isInvalidationMethod(MDI, Partial)) 00253 OutInfo.addInvalidationMethod( 00254 cast<ObjCMethodDecl>(MDI->getCanonicalDecl())); 00255 00256 // If interface, check all parent protocols and super. 00257 if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) { 00258 00259 // Visit all protocols. 00260 for (const auto *I : InterfD->protocols()) 00261 containsInvalidationMethod(I->getDefinition(), OutInfo, Partial); 00262 00263 // Visit all categories in case the invalidation method is declared in 00264 // a category. 00265 for (const auto *Ext : InterfD->visible_extensions()) 00266 containsInvalidationMethod(Ext, OutInfo, Partial); 00267 00268 containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial); 00269 return; 00270 } 00271 00272 // If protocol, check all parent protocols. 00273 if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) { 00274 for (const auto *I : ProtD->protocols()) { 00275 containsInvalidationMethod(I->getDefinition(), OutInfo, Partial); 00276 } 00277 return; 00278 } 00279 00280 return; 00281 } 00282 00283 bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv, 00284 IvarSet &TrackedIvars, 00285 const ObjCIvarDecl **FirstIvarDecl) { 00286 QualType IvQTy = Iv->getType(); 00287 const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>(); 00288 if (!IvTy) 00289 return false; 00290 const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl(); 00291 00292 InvalidationInfo Info; 00293 containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false); 00294 if (Info.needsInvalidation()) { 00295 const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl()); 00296 TrackedIvars[I] = Info; 00297 if (!*FirstIvarDecl) 00298 *FirstIvarDecl = I; 00299 return true; 00300 } 00301 return false; 00302 } 00303 00304 const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar( 00305 const ObjCPropertyDecl *Prop, 00306 const ObjCInterfaceDecl *InterfaceD, 00307 IvarSet &TrackedIvars, 00308 const ObjCIvarDecl **FirstIvarDecl) { 00309 const ObjCIvarDecl *IvarD = nullptr; 00310 00311 // Lookup for the synthesized case. 00312 IvarD = Prop->getPropertyIvarDecl(); 00313 // We only track the ivars/properties that are defined in the current 00314 // class (not the parent). 00315 if (IvarD && IvarD->getContainingInterface() == InterfaceD) { 00316 if (TrackedIvars.count(IvarD)) { 00317 return IvarD; 00318 } 00319 // If the ivar is synthesized we still want to track it. 00320 if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl)) 00321 return IvarD; 00322 } 00323 00324 // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars. 00325 StringRef PropName = Prop->getIdentifier()->getName(); 00326 for (IvarSet::const_iterator I = TrackedIvars.begin(), 00327 E = TrackedIvars.end(); I != E; ++I) { 00328 const ObjCIvarDecl *Iv = I->first; 00329 StringRef IvarName = Iv->getName(); 00330 00331 if (IvarName == PropName) 00332 return Iv; 00333 00334 SmallString<128> PropNameWithUnderscore; 00335 { 00336 llvm::raw_svector_ostream os(PropNameWithUnderscore); 00337 os << '_' << PropName; 00338 } 00339 if (IvarName == PropNameWithUnderscore.str()) 00340 return Iv; 00341 } 00342 00343 // Note, this is a possible source of false positives. We could look at the 00344 // getter implementation to find the ivar when its name is not derived from 00345 // the property name. 00346 return nullptr; 00347 } 00348 00349 void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os, 00350 const ObjCIvarDecl *IvarDecl, 00351 const IvarToPropMapTy &IvarToPopertyMap) { 00352 if (IvarDecl->getSynthesize()) { 00353 const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl); 00354 assert(PD &&"Do we synthesize ivars for something other than properties?"); 00355 os << "Property "<< PD->getName() << " "; 00356 } else { 00357 os << "Instance variable "<< IvarDecl->getName() << " "; 00358 } 00359 } 00360 00361 // Check that the invalidatable interfaces with ivars/properties implement the 00362 // invalidation methods. 00363 void IvarInvalidationCheckerImpl:: 00364 visit(const ObjCImplementationDecl *ImplD) const { 00365 // Collect all ivars that need cleanup. 00366 IvarSet Ivars; 00367 // Record the first Ivar needing invalidation; used in reporting when only 00368 // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure 00369 // deterministic output. 00370 const ObjCIvarDecl *FirstIvarDecl = nullptr; 00371 const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface(); 00372 00373 // Collect ivars declared in this class, its extensions and its implementation 00374 ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD); 00375 for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv; 00376 Iv= Iv->getNextIvar()) 00377 trackIvar(Iv, Ivars, &FirstIvarDecl); 00378 00379 // Construct Property/Property Accessor to Ivar maps to assist checking if an 00380 // ivar which is backing a property has been reset. 00381 MethToIvarMapTy PropSetterToIvarMap; 00382 MethToIvarMapTy PropGetterToIvarMap; 00383 PropToIvarMapTy PropertyToIvarMap; 00384 IvarToPropMapTy IvarToPopertyMap; 00385 00386 ObjCInterfaceDecl::PropertyMap PropMap; 00387 ObjCInterfaceDecl::PropertyDeclOrder PropOrder; 00388 InterfaceD->collectPropertiesToImplement(PropMap, PropOrder); 00389 00390 for (ObjCInterfaceDecl::PropertyMap::iterator 00391 I = PropMap.begin(), E = PropMap.end(); I != E; ++I) { 00392 const ObjCPropertyDecl *PD = I->second; 00393 00394 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars, 00395 &FirstIvarDecl); 00396 if (!ID) 00397 continue; 00398 00399 // Store the mappings. 00400 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); 00401 PropertyToIvarMap[PD] = ID; 00402 IvarToPopertyMap[ID] = PD; 00403 00404 // Find the setter and the getter. 00405 const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl(); 00406 if (SetterD) { 00407 SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl()); 00408 PropSetterToIvarMap[SetterD] = ID; 00409 } 00410 00411 const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl(); 00412 if (GetterD) { 00413 GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl()); 00414 PropGetterToIvarMap[GetterD] = ID; 00415 } 00416 } 00417 00418 // If no ivars need invalidation, there is nothing to check here. 00419 if (Ivars.empty()) 00420 return; 00421 00422 // Find all partial invalidation methods. 00423 InvalidationInfo PartialInfo; 00424 containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true); 00425 00426 // Remove ivars invalidated by the partial invalidation methods. They do not 00427 // need to be invalidated in the regular invalidation methods. 00428 bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false; 00429 for (MethodSet::iterator 00430 I = PartialInfo.InvalidationMethods.begin(), 00431 E = PartialInfo.InvalidationMethods.end(); I != E; ++I) { 00432 const ObjCMethodDecl *InterfD = *I; 00433 00434 // Get the corresponding method in the @implementation. 00435 const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), 00436 InterfD->isInstanceMethod()); 00437 if (D && D->hasBody()) { 00438 AtImplementationContainsAtLeastOnePartialInvalidationMethod = true; 00439 00440 bool CalledAnotherInvalidationMethod = false; 00441 // The MethodCrowler is going to remove the invalidated ivars. 00442 MethodCrawler(Ivars, 00443 CalledAnotherInvalidationMethod, 00444 PropSetterToIvarMap, 00445 PropGetterToIvarMap, 00446 PropertyToIvarMap, 00447 BR.getContext()).VisitStmt(D->getBody()); 00448 // If another invalidation method was called, trust that full invalidation 00449 // has occurred. 00450 if (CalledAnotherInvalidationMethod) 00451 Ivars.clear(); 00452 } 00453 } 00454 00455 // If all ivars have been invalidated by partial invalidators, there is 00456 // nothing to check here. 00457 if (Ivars.empty()) 00458 return; 00459 00460 // Find all invalidation methods in this @interface declaration and parents. 00461 InvalidationInfo Info; 00462 containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false); 00463 00464 // Report an error in case none of the invalidation methods are declared. 00465 if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) { 00466 if (Filter.check_MissingInvalidationMethod) 00467 reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod, 00468 FirstIvarDecl, IvarToPopertyMap, InterfaceD, 00469 /*MissingDeclaration*/ true); 00470 // If there are no invalidation methods, there is no ivar validation work 00471 // to be done. 00472 return; 00473 } 00474 00475 // Only check if Ivars are invalidated when InstanceVariableInvalidation 00476 // has been requested. 00477 if (!Filter.check_InstanceVariableInvalidation) 00478 return; 00479 00480 // Check that all ivars are invalidated by the invalidation methods. 00481 bool AtImplementationContainsAtLeastOneInvalidationMethod = false; 00482 for (MethodSet::iterator I = Info.InvalidationMethods.begin(), 00483 E = Info.InvalidationMethods.end(); I != E; ++I) { 00484 const ObjCMethodDecl *InterfD = *I; 00485 00486 // Get the corresponding method in the @implementation. 00487 const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), 00488 InterfD->isInstanceMethod()); 00489 if (D && D->hasBody()) { 00490 AtImplementationContainsAtLeastOneInvalidationMethod = true; 00491 00492 // Get a copy of ivars needing invalidation. 00493 IvarSet IvarsI = Ivars; 00494 00495 bool CalledAnotherInvalidationMethod = false; 00496 MethodCrawler(IvarsI, 00497 CalledAnotherInvalidationMethod, 00498 PropSetterToIvarMap, 00499 PropGetterToIvarMap, 00500 PropertyToIvarMap, 00501 BR.getContext()).VisitStmt(D->getBody()); 00502 // If another invalidation method was called, trust that full invalidation 00503 // has occurred. 00504 if (CalledAnotherInvalidationMethod) 00505 continue; 00506 00507 // Warn on the ivars that were not invalidated by the method. 00508 for (IvarSet::const_iterator 00509 I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I) 00510 reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D); 00511 } 00512 } 00513 00514 // Report an error in case none of the invalidation methods are implemented. 00515 if (!AtImplementationContainsAtLeastOneInvalidationMethod) { 00516 if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) { 00517 // Warn on the ivars that were not invalidated by the prrtial 00518 // invalidation methods. 00519 for (IvarSet::const_iterator 00520 I = Ivars.begin(), E = Ivars.end(); I != E; ++I) 00521 reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, nullptr); 00522 } else { 00523 // Otherwise, no invalidation methods were implemented. 00524 reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation, 00525 FirstIvarDecl, IvarToPopertyMap, InterfaceD, 00526 /*MissingDeclaration*/ false); 00527 } 00528 } 00529 } 00530 00531 void IvarInvalidationCheckerImpl::reportNoInvalidationMethod( 00532 CheckName CheckName, const ObjCIvarDecl *FirstIvarDecl, 00533 const IvarToPropMapTy &IvarToPopertyMap, 00534 const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const { 00535 SmallString<128> sbuf; 00536 llvm::raw_svector_ostream os(sbuf); 00537 assert(FirstIvarDecl); 00538 printIvar(os, FirstIvarDecl, IvarToPopertyMap); 00539 os << "needs to be invalidated; "; 00540 if (MissingDeclaration) 00541 os << "no invalidation method is declared for "; 00542 else 00543 os << "no invalidation method is defined in the @implementation for "; 00544 os << InterfaceD->getName(); 00545 00546 PathDiagnosticLocation IvarDecLocation = 00547 PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager()); 00548 00549 BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation", 00550 categories::CoreFoundationObjectiveC, os.str(), 00551 IvarDecLocation); 00552 } 00553 00554 void IvarInvalidationCheckerImpl:: 00555 reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, 00556 const IvarToPropMapTy &IvarToPopertyMap, 00557 const ObjCMethodDecl *MethodD) const { 00558 SmallString<128> sbuf; 00559 llvm::raw_svector_ostream os(sbuf); 00560 printIvar(os, IvarD, IvarToPopertyMap); 00561 os << "needs to be invalidated or set to nil"; 00562 if (MethodD) { 00563 PathDiagnosticLocation MethodDecLocation = 00564 PathDiagnosticLocation::createEnd(MethodD->getBody(), 00565 BR.getSourceManager(), 00566 Mgr.getAnalysisDeclContext(MethodD)); 00567 BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation, 00568 "Incomplete invalidation", 00569 categories::CoreFoundationObjectiveC, os.str(), 00570 MethodDecLocation); 00571 } else { 00572 BR.EmitBasicReport( 00573 IvarD, Filter.checkName_InstanceVariableInvalidation, 00574 "Incomplete invalidation", categories::CoreFoundationObjectiveC, 00575 os.str(), 00576 PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager())); 00577 } 00578 } 00579 00580 void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated( 00581 const ObjCIvarDecl *Iv) { 00582 IvarSet::iterator I = IVars.find(Iv); 00583 if (I != IVars.end()) { 00584 // If InvalidationMethod is present, we are processing the message send and 00585 // should ensure we are invalidating with the appropriate method, 00586 // otherwise, we are processing setting to 'nil'. 00587 if (!InvalidationMethod || 00588 (InvalidationMethod && I->second.hasMethod(InvalidationMethod))) 00589 IVars.erase(I); 00590 } 00591 } 00592 00593 const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const { 00594 E = E->IgnoreParenCasts(); 00595 if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) 00596 E = POE->getSyntacticForm()->IgnoreParenCasts(); 00597 if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) 00598 E = OVE->getSourceExpr()->IgnoreParenCasts(); 00599 return E; 00600 } 00601 00602 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr( 00603 const ObjCIvarRefExpr *IvarRef) { 00604 if (const Decl *D = IvarRef->getDecl()) 00605 markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl())); 00606 } 00607 00608 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr( 00609 const ObjCMessageExpr *ME) { 00610 const ObjCMethodDecl *MD = ME->getMethodDecl(); 00611 if (MD) { 00612 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 00613 MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD); 00614 if (IvI != PropertyGetterToIvarMap.end()) 00615 markInvalidated(IvI->second); 00616 } 00617 } 00618 00619 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr( 00620 const ObjCPropertyRefExpr *PA) { 00621 00622 if (PA->isExplicitProperty()) { 00623 const ObjCPropertyDecl *PD = PA->getExplicitProperty(); 00624 if (PD) { 00625 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); 00626 PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD); 00627 if (IvI != PropertyToIvarMap.end()) 00628 markInvalidated(IvI->second); 00629 return; 00630 } 00631 } 00632 00633 if (PA->isImplicitProperty()) { 00634 const ObjCMethodDecl *MD = PA->getImplicitPropertySetter(); 00635 if (MD) { 00636 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 00637 MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD); 00638 if (IvI != PropertyGetterToIvarMap.end()) 00639 markInvalidated(IvI->second); 00640 return; 00641 } 00642 } 00643 } 00644 00645 bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const { 00646 E = peel(E); 00647 00648 return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull) 00649 != Expr::NPCK_NotNull); 00650 } 00651 00652 void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) { 00653 E = peel(E); 00654 00655 if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { 00656 checkObjCIvarRefExpr(IvarRef); 00657 return; 00658 } 00659 00660 if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) { 00661 checkObjCPropertyRefExpr(PropRef); 00662 return; 00663 } 00664 00665 if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) { 00666 checkObjCMessageExpr(MsgExpr); 00667 return; 00668 } 00669 } 00670 00671 void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator( 00672 const BinaryOperator *BO) { 00673 VisitStmt(BO); 00674 00675 // Do we assign/compare against zero? If yes, check the variable we are 00676 // assigning to. 00677 BinaryOperatorKind Opcode = BO->getOpcode(); 00678 if (Opcode != BO_Assign && 00679 Opcode != BO_EQ && 00680 Opcode != BO_NE) 00681 return; 00682 00683 if (isZero(BO->getRHS())) { 00684 check(BO->getLHS()); 00685 return; 00686 } 00687 00688 if (Opcode != BO_Assign && isZero(BO->getLHS())) { 00689 check(BO->getRHS()); 00690 return; 00691 } 00692 } 00693 00694 void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr( 00695 const ObjCMessageExpr *ME) { 00696 const ObjCMethodDecl *MD = ME->getMethodDecl(); 00697 const Expr *Receiver = ME->getInstanceReceiver(); 00698 00699 // Stop if we are calling '[self invalidate]'. 00700 if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false)) 00701 if (Receiver->isObjCSelfExpr()) { 00702 CalledAnotherInvalidationMethod = true; 00703 return; 00704 } 00705 00706 // Check if we call a setter and set the property to 'nil'. 00707 if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) { 00708 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 00709 MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD); 00710 if (IvI != PropertySetterToIvarMap.end()) { 00711 markInvalidated(IvI->second); 00712 return; 00713 } 00714 } 00715 00716 // Check if we call the 'invalidation' routine on the ivar. 00717 if (Receiver) { 00718 InvalidationMethod = MD; 00719 check(Receiver->IgnoreParenCasts()); 00720 InvalidationMethod = nullptr; 00721 } 00722 00723 VisitStmt(ME); 00724 } 00725 } 00726 00727 // Register the checkers. 00728 namespace { 00729 00730 class IvarInvalidationChecker : 00731 public Checker<check::ASTDecl<ObjCImplementationDecl> > { 00732 public: 00733 ChecksFilter Filter; 00734 public: 00735 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, 00736 BugReporter &BR) const { 00737 IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter); 00738 Walker.visit(D); 00739 } 00740 }; 00741 } 00742 00743 #define REGISTER_CHECKER(name) \ 00744 void ento::register##name(CheckerManager &mgr) { \ 00745 IvarInvalidationChecker *checker = \ 00746 mgr.registerChecker<IvarInvalidationChecker>(); \ 00747 checker->Filter.check_##name = true; \ 00748 checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \ 00749 } 00750 00751 REGISTER_CHECKER(InstanceVariableInvalidation) 00752 REGISTER_CHECKER(MissingInvalidationMethod) 00753