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