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