IvarInvalidationChecker.cpp revision 377945cc9e4f23cdbb01ade2a664acd5ff95a888
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. 17// 18// Note, this checker currently only checks if an ivar was accessed by the 19// method, we do not currently support any deeper invalidation checking. 20// 21//===----------------------------------------------------------------------===// 22 23#include "ClangSACheckers.h" 24#include "clang/StaticAnalyzer/Core/Checker.h" 25#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 26#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 27#include "clang/AST/DeclObjC.h" 28#include "clang/AST/StmtVisitor.h" 29#include "llvm/ADT/DenseMap.h" 30#include "llvm/ADT/SmallString.h" 31 32using namespace clang; 33using namespace ento; 34 35namespace { 36class IvarInvalidationChecker : 37 public Checker<check::ASTDecl<ObjCMethodDecl> > { 38 39 typedef llvm::DenseMap<const ObjCIvarDecl*, bool> IvarSet; 40 typedef llvm::DenseMap<const ObjCMethodDecl*, 41 const ObjCIvarDecl*> MethToIvarMapTy; 42 typedef llvm::DenseMap<const ObjCPropertyDecl*, 43 const ObjCIvarDecl*> PropToIvarMapTy; 44 typedef llvm::DenseMap<const ObjCIvarDecl*, 45 const ObjCPropertyDecl*> IvarToPropMapTy; 46 47 /// Statement visitor, which walks the method body and flags the ivars 48 /// referenced in it (either directly or via property). 49 class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { 50 51 /// The set of Ivars which need to be invalidated. 52 IvarSet &IVars; 53 54 /// Property setter/getter to ivar mapping. 55 MethToIvarMapTy &PropertyAccessorToIvarMap; 56 57 // Property to ivar mapping. 58 PropToIvarMapTy &PropertyToIvarMap; 59 60 public: 61 MethodCrawler(const ObjCInterfaceDecl *InID, 62 IvarSet &InIVars, 63 MethToIvarMapTy &InPropertyAccessorToIvarMap, 64 PropToIvarMapTy &InPropertyToIvarMap) 65 : IVars(InIVars), 66 PropertyAccessorToIvarMap(InPropertyAccessorToIvarMap), 67 PropertyToIvarMap(InPropertyToIvarMap) {} 68 69 void VisitStmt(const Stmt *S) { VisitChildren(S); } 70 71 void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef); 72 73 void VisitObjCMessageExpr(const ObjCMessageExpr *ME); 74 75 void VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA); 76 77 void VisitChildren(const Stmt *S) { 78 for (Stmt::const_child_range I = S->children(); I; ++I) 79 if (*I) 80 this->Visit(*I); 81 } 82 }; 83 84 /// Check if the any of the methods inside the interface are annotated with 85 /// the invalidation annotation. 86 static bool containsInvalidationMethod(const ObjCContainerDecl *D); 87 88 /// Check if ivar should be tracked and add to TrackedIvars if positive. 89 /// Returns true if ivar should be tracked. 90 static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars); 91 92 /// Given the property declaration, and the list of tracked ivars, finds 93 /// the ivar backing the property when possible. Returns '0' when no such 94 /// ivar could be found. 95 static const ObjCIvarDecl *findPropertyBackingIvar( 96 const ObjCPropertyDecl *Prop, 97 const ObjCInterfaceDecl *InterfaceD, 98 IvarSet &TrackedIvars); 99 100public: 101 void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager& Mgr, 102 BugReporter &BR) const; 103 104 // TODO: We are currently ignoring the ivars coming from class extensions. 105}; 106 107bool isInvalidationMethod(const ObjCMethodDecl *M) { 108 for (specific_attr_iterator<AnnotateAttr> 109 AI = M->specific_attr_begin<AnnotateAttr>(), 110 AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) { 111 const AnnotateAttr *Ann = *AI; 112 if (Ann->getAnnotation() == "objc_instance_variable_invalidator") 113 return true; 114 } 115 return false; 116} 117 118bool IvarInvalidationChecker::containsInvalidationMethod ( 119 const ObjCContainerDecl *D) { 120 121 // TODO: Cache the results. 122 123 if (!D) 124 return false; 125 126 // Check all methods. 127 for (ObjCContainerDecl::method_iterator 128 I = D->meth_begin(), 129 E = D->meth_end(); I != E; ++I) { 130 const ObjCMethodDecl *MDI = *I; 131 if (isInvalidationMethod(MDI)) 132 return true; 133 } 134 135 // If interface, check all parent protocols and super. 136 // TODO: Visit all categories in case the invalidation method is declared in 137 // a category. 138 if (const ObjCInterfaceDecl *InterfaceD = dyn_cast<ObjCInterfaceDecl>(D)) { 139 for (ObjCInterfaceDecl::protocol_iterator 140 I = InterfaceD->protocol_begin(), 141 E = InterfaceD->protocol_end(); I != E; ++I) { 142 if (containsInvalidationMethod(*I)) 143 return true; 144 } 145 return containsInvalidationMethod(InterfaceD->getSuperClass()); 146 } 147 148 // If protocol, check all parent protocols. 149 if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) { 150 for (ObjCInterfaceDecl::protocol_iterator 151 I = ProtD->protocol_begin(), 152 E = ProtD->protocol_end(); I != E; ++I) { 153 if (containsInvalidationMethod(*I)) 154 return true; 155 } 156 return false; 157 } 158 159 llvm_unreachable("One of the casts above should have succeeded."); 160} 161 162bool IvarInvalidationChecker::trackIvar(const ObjCIvarDecl *Iv, 163 IvarSet &TrackedIvars) { 164 QualType IvQTy = Iv->getType(); 165 const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>(); 166 if (!IvTy) 167 return false; 168 const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl(); 169 if (containsInvalidationMethod(IvInterf)) { 170 TrackedIvars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = false; 171 return true; 172 } 173 return false; 174} 175 176const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar( 177 const ObjCPropertyDecl *Prop, 178 const ObjCInterfaceDecl *InterfaceD, 179 IvarSet &TrackedIvars) { 180 const ObjCIvarDecl *IvarD = 0; 181 182 // Lookup for the synthesized case. 183 IvarD = Prop->getPropertyIvarDecl(); 184 if (IvarD) { 185 if (TrackedIvars.count(IvarD)) { 186 return IvarD; 187 } 188 // If the ivar is synthesized we still want to track it. 189 if (trackIvar(IvarD, TrackedIvars)) 190 return IvarD; 191 } 192 193 // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars. 194 StringRef PropName = Prop->getIdentifier()->getName(); 195 for (IvarSet::const_iterator I = TrackedIvars.begin(), 196 E = TrackedIvars.end(); I != E; ++I) { 197 const ObjCIvarDecl *Iv = I->first; 198 StringRef IvarName = Iv->getName(); 199 200 if (IvarName == PropName) 201 return Iv; 202 203 SmallString<128> PropNameWithUnderscore; 204 { 205 llvm::raw_svector_ostream os(PropNameWithUnderscore); 206 os << '_' << PropName; 207 } 208 if (IvarName == PropNameWithUnderscore.str()) 209 return Iv; 210 } 211 212 // Note, this is a possible source of false positives. We could look at the 213 // getter implementation to find the ivar when its name is not derived from 214 // the property name. 215 return 0; 216} 217 218void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D, 219 AnalysisManager& Mgr, 220 BugReporter &BR) const { 221 // We are only interested in checking the cleanup methods. 222 if (!D->hasBody() || !isInvalidationMethod(D)) 223 return; 224 225 // Collect all ivars that need cleanup. 226 IvarSet Ivars; 227 const ObjCInterfaceDecl *InterfaceD = D->getClassInterface(); 228 for (ObjCInterfaceDecl::ivar_iterator 229 II = InterfaceD->ivar_begin(), 230 IE = InterfaceD->ivar_end(); II != IE; ++II) { 231 const ObjCIvarDecl *Iv = *II; 232 trackIvar(Iv, Ivars); 233 } 234 235 // Construct Property/Property Accessor to Ivar maps to assist checking if an 236 // ivar which is backing a property has been reset. 237 MethToIvarMapTy PropAccessorToIvarMap; 238 PropToIvarMapTy PropertyToIvarMap; 239 IvarToPropMapTy IvarToPopertyMap; 240 for (ObjCInterfaceDecl::prop_iterator 241 I = InterfaceD->prop_begin(), 242 E = InterfaceD->prop_end(); I != E; ++I) { 243 const ObjCPropertyDecl *PD = *I; 244 245 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars); 246 if (!ID) { 247 continue; 248 } 249 250 // Store the mappings. 251 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); 252 PropertyToIvarMap[PD] = ID; 253 IvarToPopertyMap[ID] = PD; 254 255 // Find the setter and the getter. 256 const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl(); 257 if (SetterD) { 258 SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl()); 259 PropAccessorToIvarMap[SetterD] = ID; 260 } 261 262 const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl(); 263 if (GetterD) { 264 GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl()); 265 PropAccessorToIvarMap[GetterD] = ID; 266 } 267 } 268 269 270 // Check which ivars have been accessed by the method. 271 // We assume that if ivar was at least accessed, it was not forgotten. 272 MethodCrawler(InterfaceD, Ivars, 273 PropAccessorToIvarMap, PropertyToIvarMap).VisitStmt(D->getBody()); 274 275 // Warn on the ivars that were not accessed by the method. 276 for (IvarSet::const_iterator I = Ivars.begin(), E = Ivars.end(); I != E; ++I){ 277 if (I->second == false) { 278 const ObjCIvarDecl *IvarDecl = I->first; 279 280 PathDiagnosticLocation IvarDecLocation = 281 PathDiagnosticLocation::createEnd(D->getBody(), BR.getSourceManager(), 282 Mgr.getAnalysisDeclContext(D)); 283 284 SmallString<128> sbuf; 285 llvm::raw_svector_ostream os(sbuf); 286 287 // Construct the warning message. 288 if (IvarDecl->getSynthesize()) { 289 const ObjCPropertyDecl *PD = IvarToPopertyMap[IvarDecl]; 290 assert(PD && 291 "Do we synthesize ivars for something other than properties?"); 292 os << "Property "<< PD->getName() << " needs to be invalidated"; 293 } else { 294 os << "Instance variable "<< IvarDecl->getName() 295 << " needs to be invalidated"; 296 } 297 298 BR.EmitBasicReport(D, 299 "Incomplete invalidation", 300 categories::CoreFoundationObjectiveC, os.str(), 301 IvarDecLocation); 302 } 303 } 304} 305 306/// Handle the case when an ivar is directly accessed. 307void IvarInvalidationChecker::MethodCrawler::VisitObjCIvarRefExpr( 308 const ObjCIvarRefExpr *IvarRef) { 309 const Decl *D = IvarRef->getDecl(); 310 if (D) 311 IVars[cast<ObjCIvarDecl>(D->getCanonicalDecl())] = true; 312 VisitStmt(IvarRef); 313} 314 315 316/// Handle the case when the property backing ivar is set via a direct call 317/// to the setter. 318void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr( 319 const ObjCMessageExpr *ME) { 320 const ObjCMethodDecl *MD = ME->getMethodDecl(); 321 if (MD) { 322 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 323 IVars[PropertyAccessorToIvarMap[MD]] = true; 324 } 325 VisitStmt(ME); 326} 327 328/// Handle the case when the property backing ivar is set via the dot syntax. 329void IvarInvalidationChecker::MethodCrawler::VisitObjCPropertyRefExpr( 330 const ObjCPropertyRefExpr *PA) { 331 332 if (PA->isExplicitProperty()) { 333 const ObjCPropertyDecl *PD = PA->getExplicitProperty(); 334 if (PD) { 335 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); 336 IVars[PropertyToIvarMap[PD]] = true; 337 VisitStmt(PA); 338 return; 339 } 340 } 341 342 if (PA->isImplicitProperty()) { 343 const ObjCMethodDecl *MD = PA->getImplicitPropertySetter(); 344 if (MD) { 345 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 346 IVars[PropertyAccessorToIvarMap[MD]] = true; 347 VisitStmt(PA); 348 return; 349 } 350 } 351 VisitStmt(PA); 352} 353} 354 355// Register the checker. 356void ento::registerIvarInvalidationChecker(CheckerManager &mgr) { 357 mgr.registerChecker<IvarInvalidationChecker>(); 358} 359