IvarInvalidationChecker.cpp revision 5bf5c2ec54ede5352293e5739e9b44bea2f6b01b
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 45 /// Statement visitor, which walks the method body and flags the ivars 46 /// referenced in it (either directly or via property). 47 class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { 48 const ObjCInterfaceDecl *InterfD; 49 50 /// The set of Ivars which need to be invalidated. 51 IvarSet &IVars; 52 53 /// Property setter to ivar mapping. 54 MethToIvarMapTy &PropertySetterToIvarMap; 55 56 // Property to ivar mapping. 57 PropToIvarMapTy &PropertyToIvarMap; 58 59 public: 60 MethodCrawler(const ObjCInterfaceDecl *InID, 61 IvarSet &InIVars, MethToIvarMapTy &InPropertySetterToIvarMap, 62 PropToIvarMapTy &InPropertyToIvarMap) 63 : InterfD(InID), IVars(InIVars), 64 PropertySetterToIvarMap(InPropertySetterToIvarMap), 65 PropertyToIvarMap(InPropertyToIvarMap) {} 66 67 void VisitStmt(const Stmt *S) { VisitChildren(S); } 68 69 void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef); 70 71 void VisitObjCMessageExpr(const ObjCMessageExpr *ME); 72 73 void VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA); 74 75 void VisitChildren(const Stmt *S) { 76 for (Stmt::const_child_range I = S->children(); I; ++I) 77 if (*I) 78 static_cast<MethodCrawler*>(this)->Visit(*I); 79 } 80 }; 81 82 /// Check if the any of the methods inside the interface are annotated with 83 /// the invalidation annotation. 84 bool containsInvalidationMethod(const ObjCContainerDecl *D) const; 85 86 /// Given the property declaration, and the list of tracked ivars, finds 87 /// the ivar backing the property when possible. Returns '0' when no such 88 /// ivar could be found. 89 static const ObjCIvarDecl *findPropertyBackingIvar( 90 const ObjCPropertyDecl *Prop, 91 const ObjCInterfaceDecl *InterfaceD, 92 IvarSet TrackedIvars); 93 94public: 95 void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager& Mgr, 96 BugReporter &BR) const; 97 98}; 99 100bool isInvalidationMethod(const ObjCMethodDecl *M) { 101 const AnnotateAttr *Ann = M->getAttr<AnnotateAttr>(); 102 if (!Ann) 103 return false; 104 if (Ann->getAnnotation() == "objc_instance_variable_invalidator") 105 return true; 106 return false; 107} 108 109bool IvarInvalidationChecker::containsInvalidationMethod ( 110 const ObjCContainerDecl *D) const { 111 112 // TODO: Cache the results. 113 114 if (!D) 115 return false; 116 117 // Check all methods. 118 for (ObjCContainerDecl::method_iterator 119 I = D->meth_begin(), 120 E = D->meth_end(); I != E; ++I) { 121 const ObjCMethodDecl *MDI = *I; 122 if (isInvalidationMethod(MDI)) 123 return true; 124 } 125 126 // If interface, check all parent protocols and super. 127 // TODO: Visit all categories in case the invalidation method is declared in 128 // a category. 129 if (const ObjCInterfaceDecl *InterfaceD = dyn_cast<ObjCInterfaceDecl>(D)) { 130 for (ObjCInterfaceDecl::protocol_iterator 131 I = InterfaceD->protocol_begin(), 132 E = InterfaceD->protocol_end(); I != E; ++I) { 133 if (containsInvalidationMethod(*I)) 134 return true; 135 } 136 return containsInvalidationMethod(InterfaceD->getSuperClass()); 137 } 138 139 // If protocol, check all parent protocols. 140 if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) { 141 for (ObjCInterfaceDecl::protocol_iterator 142 I = ProtD->protocol_begin(), 143 E = ProtD->protocol_end(); I != E; ++I) { 144 if (containsInvalidationMethod(*I)) 145 return true; 146 } 147 return false; 148 } 149 150 llvm_unreachable("One of the casts above should have succeeded."); 151} 152 153const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar( 154 const ObjCPropertyDecl *Prop, 155 const ObjCInterfaceDecl *InterfaceD, 156 IvarSet TrackedIvars) { 157 const ObjCIvarDecl *IvarD = 0; 158 159 // Lookup for the synthesized case. 160 IvarD = Prop->getPropertyIvarDecl(); 161 if (IvarD) 162 return IvarD; 163 164 // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars. 165 StringRef PropName = Prop->getIdentifier()->getName(); 166 for (IvarSet::const_iterator I = TrackedIvars.begin(), 167 E = TrackedIvars.end(); I != E; ++I) { 168 const ObjCIvarDecl *Iv = I->first; 169 StringRef IvarName = Iv->getName(); 170 171 if (IvarName == PropName) 172 return Iv; 173 174 SmallString<128> PropNameWithUnderscore; 175 { 176 llvm::raw_svector_ostream os(PropNameWithUnderscore); 177 os << '_' << PropName; 178 } 179 if (IvarName == PropNameWithUnderscore.str()) 180 return Iv; 181 } 182 183 // Note, this is a possible source of false positives. We could look at the 184 // getter implementation to find the ivar when its name is not derived from 185 // the property name. 186 return 0; 187} 188 189void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D, 190 AnalysisManager& Mgr, 191 BugReporter &BR) const { 192 // We are only interested in checking the cleanup methods. 193 if (!D->hasBody() || !isInvalidationMethod(D)) 194 return; 195 196 // Collect all ivars that need cleanup. 197 IvarSet Ivars; 198 const ObjCInterfaceDecl *InterfaceD = D->getClassInterface(); 199 for (ObjCInterfaceDecl::ivar_iterator 200 II = InterfaceD->ivar_begin(), 201 IE = InterfaceD->ivar_end(); II != IE; ++II) { 202 const ObjCIvarDecl *Iv = *II; 203 QualType IvQTy = Iv->getType(); 204 const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>(); 205 if (!IvTy) 206 continue; 207 const ObjCInterfaceDecl *IvInterf = IvTy->getObjectType()->getInterface(); 208 if (containsInvalidationMethod(IvInterf)) 209 Ivars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = false; 210 } 211 212 // Construct Property/Property Setter to Ivar maps to assist checking if an 213 // ivar which is backing a property has been reset. 214 MethToIvarMapTy PropSetterToIvarMap; 215 PropToIvarMapTy PropertyToIvarMap; 216 for (ObjCInterfaceDecl::prop_iterator 217 I = InterfaceD->prop_begin(), 218 E = InterfaceD->prop_end(); I != E; ++I) { 219 const ObjCPropertyDecl *PD = *I; 220 221 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars); 222 if (!ID) { 223 continue; 224 } 225 // Find the setter. 226 const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl(); 227 // If we don't know the setter, do not track this ivar. 228 if (!SetterD) { 229 Ivars[cast<ObjCIvarDecl>(ID->getCanonicalDecl())] = true; 230 continue; 231 } 232 233 // Store the mappings. 234 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); 235 SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl()); 236 PropertyToIvarMap[PD] = ID; 237 PropSetterToIvarMap[SetterD] = ID; 238 } 239 240 241 // Check which ivars have been accessed by the method. 242 // We assume that if ivar was at least accessed, it was not forgotten. 243 MethodCrawler(InterfaceD, Ivars, 244 PropSetterToIvarMap, PropertyToIvarMap).VisitStmt(D->getBody()); 245 246 // Warn on the ivars that were not accessed by the method. 247 for (IvarSet::const_iterator I = Ivars.begin(), E = Ivars.end(); I != E; ++I){ 248 if (I->second == false) { 249 const ObjCIvarDecl *IvarDecl = I->first; 250 251 PathDiagnosticLocation IvarDecLocation = 252 PathDiagnosticLocation::createBegin(IvarDecl, BR.getSourceManager()); 253 254 SmallString<128> sbuf; 255 llvm::raw_svector_ostream os(sbuf); 256 os << "Ivar needs to be invalidated in the '" << 257 D->getSelector().getAsString()<< "' method"; 258 259 BR.EmitBasicReport(IvarDecl, 260 "Incomplete invalidation", 261 categories::CoreFoundationObjectiveC, os.str(), 262 IvarDecLocation); 263 } 264 } 265} 266 267/// Handle the case when an ivar is directly accessed. 268void IvarInvalidationChecker::MethodCrawler::VisitObjCIvarRefExpr( 269 const ObjCIvarRefExpr *IvarRef) { 270 const Decl *D = IvarRef->getDecl(); 271 if (D) 272 IVars[cast<ObjCIvarDecl>(D->getCanonicalDecl())] = true; 273 VisitStmt(IvarRef); 274} 275 276 277/// Handle the case when the property backing ivar is set via a direct call 278/// to the setter. 279void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr( 280 const ObjCMessageExpr *ME) { 281 const ObjCMethodDecl *MD = ME->getMethodDecl(); 282 if (MD) { 283 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 284 IVars[PropertySetterToIvarMap[MD]] = true; 285 } 286 VisitStmt(ME); 287} 288 289/// Handle the case when the property backing ivar is set via the dot syntax. 290void IvarInvalidationChecker::MethodCrawler::VisitObjCPropertyRefExpr( 291 const ObjCPropertyRefExpr *PA) { 292 293 if (PA->isExplicitProperty()) { 294 const ObjCPropertyDecl *PD = PA->getExplicitProperty(); 295 if (PD) { 296 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); 297 IVars[PropertyToIvarMap[PD]] = true; 298 VisitStmt(PA); 299 return; 300 } 301 } 302 303 if (PA->isImplicitProperty()) { 304 const ObjCMethodDecl *MD = PA->getImplicitPropertySetter(); 305 if (MD) { 306 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl()); 307 IVars[PropertySetterToIvarMap[MD]] = true; 308 VisitStmt(PA); 309 return; 310 } 311 } 312 VisitStmt(PA); 313} 314} 315 316// Register the checker. 317void ento::registerIvarInvalidationChecker(CheckerManager &mgr) { 318 mgr.registerChecker<IvarInvalidationChecker>(); 319} 320