1 //==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- 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 file defines a CheckObjCDealloc, a checker that 11 // analyzes an Objective-C class's implementation to determine if it 12 // correctly implements -dealloc. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "ClangSACheckers.h" 17 #include "clang/AST/Attr.h" 18 #include "clang/AST/DeclObjC.h" 19 #include "clang/AST/Expr.h" 20 #include "clang/AST/ExprObjC.h" 21 #include "clang/Basic/LangOptions.h" 22 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 23 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 24 #include "clang/StaticAnalyzer/Core/Checker.h" 25 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 26 #include "llvm/Support/raw_ostream.h" 27 28 using namespace clang; 29 using namespace ento; 30 31 static bool scan_ivar_release(Stmt *S, const ObjCIvarDecl *ID, 32 const ObjCPropertyDecl *PD, 33 Selector Release, 34 IdentifierInfo* SelfII, 35 ASTContext &Ctx) { 36 37 // [mMyIvar release] 38 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) 39 if (ME->getSelector() == Release) 40 if (ME->getInstanceReceiver()) 41 if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) 42 if (ObjCIvarRefExpr *E = dyn_cast<ObjCIvarRefExpr>(Receiver)) 43 if (E->getDecl() == ID) 44 return true; 45 46 // [self setMyIvar:nil]; 47 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) 48 if (ME->getInstanceReceiver()) 49 if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) 50 if (DeclRefExpr *E = dyn_cast<DeclRefExpr>(Receiver)) 51 if (E->getDecl()->getIdentifier() == SelfII) 52 if (ME->getMethodDecl() == PD->getSetterMethodDecl() && 53 ME->getNumArgs() == 1 && 54 ME->getArg(0)->isNullPointerConstant(Ctx, 55 Expr::NPC_ValueDependentIsNull)) 56 return true; 57 58 // self.myIvar = nil; 59 if (BinaryOperator* BO = dyn_cast<BinaryOperator>(S)) 60 if (BO->isAssignmentOp()) 61 if (ObjCPropertyRefExpr *PRE = 62 dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts())) 63 if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD) 64 if (BO->getRHS()->isNullPointerConstant(Ctx, 65 Expr::NPC_ValueDependentIsNull)) { 66 // This is only a 'release' if the property kind is not 67 // 'assign'. 68 return PD->getSetterKind() != ObjCPropertyDecl::Assign; 69 } 70 71 // Recurse to children. 72 for (Stmt *SubStmt : S->children()) 73 if (SubStmt && scan_ivar_release(SubStmt, ID, PD, Release, SelfII, Ctx)) 74 return true; 75 76 return false; 77 } 78 79 static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, 80 const ObjCIvarDecl **ID, 81 const ObjCPropertyDecl **PD) { 82 83 if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) 84 return false; 85 86 (*ID) = I->getPropertyIvarDecl(); 87 if (!(*ID)) 88 return false; 89 90 QualType T = (*ID)->getType(); 91 if (!T->isObjCRetainableType()) 92 return false; 93 94 (*PD) = I->getPropertyDecl(); 95 // Shouldn't be able to synthesize a property that doesn't exist. 96 assert(*PD); 97 98 return true; 99 } 100 101 static bool synthesizedPropertyRequiresRelease(const ObjCPropertyDecl *PD) { 102 // A synthesized property must be released if and only if the kind of setter 103 // was neither 'assign' or 'weak'. 104 ObjCPropertyDecl::SetterKind SK = PD->getSetterKind(); 105 return (SK != ObjCPropertyDecl::Assign && SK != ObjCPropertyDecl::Weak); 106 } 107 108 static void checkObjCDealloc(const CheckerBase *Checker, 109 const ObjCImplementationDecl *D, 110 const LangOptions &LOpts, BugReporter &BR) { 111 112 assert(LOpts.getGC() != LangOptions::GCOnly); 113 assert(!LOpts.ObjCAutoRefCount); 114 115 ASTContext &Ctx = BR.getContext(); 116 const ObjCInterfaceDecl *ID = D->getClassInterface(); 117 118 // Does the class contain any synthesized properties that are retainable? 119 // If not, skip the check entirely. 120 bool containsRetainedSynthesizedProperty = false; 121 for (const auto *I : D->property_impls()) { 122 const ObjCIvarDecl *ID = nullptr; 123 const ObjCPropertyDecl *PD = nullptr; 124 if (!isSynthesizedRetainableProperty(I, &ID, &PD)) 125 continue; 126 127 if (synthesizedPropertyRequiresRelease(PD)) { 128 containsRetainedSynthesizedProperty = true; 129 break; 130 } 131 } 132 133 if (!containsRetainedSynthesizedProperty) 134 return; 135 136 // Determine if the class subclasses NSObject. 137 IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); 138 IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); 139 140 for ( ; ID ; ID = ID->getSuperClass()) { 141 IdentifierInfo *II = ID->getIdentifier(); 142 143 if (II == NSObjectII) 144 break; 145 146 // FIXME: For now, ignore classes that subclass SenTestCase, as these don't 147 // need to implement -dealloc. They implement tear down in another way, 148 // which we should try and catch later. 149 // http://llvm.org/bugs/show_bug.cgi?id=3187 150 if (II == SenTestCaseII) 151 return; 152 } 153 154 if (!ID) 155 return; 156 157 // Get the "dealloc" selector. 158 IdentifierInfo* II = &Ctx.Idents.get("dealloc"); 159 Selector S = Ctx.Selectors.getSelector(0, &II); 160 const ObjCMethodDecl *MD = nullptr; 161 162 // Scan the instance methods for "dealloc". 163 for (const auto *I : D->instance_methods()) { 164 if (I->getSelector() == S) { 165 MD = I; 166 break; 167 } 168 } 169 170 if (!MD) { // No dealloc found. 171 172 const char* name = LOpts.getGC() == LangOptions::NonGC 173 ? "missing -dealloc" 174 : "missing -dealloc (Hybrid MM, non-GC)"; 175 176 std::string buf; 177 llvm::raw_string_ostream os(buf); 178 os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; 179 180 PathDiagnosticLocation DLoc = 181 PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); 182 183 BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC, 184 os.str(), DLoc); 185 return; 186 } 187 188 // Get the "release" selector. 189 IdentifierInfo* RII = &Ctx.Idents.get("release"); 190 Selector RS = Ctx.Selectors.getSelector(0, &RII); 191 192 // Get the "self" identifier 193 IdentifierInfo* SelfII = &Ctx.Idents.get("self"); 194 195 // Scan for missing and extra releases of ivars used by implementations 196 // of synthesized properties 197 for (const auto *I : D->property_impls()) { 198 const ObjCIvarDecl *ID = nullptr; 199 const ObjCPropertyDecl *PD = nullptr; 200 if (!isSynthesizedRetainableProperty(I, &ID, &PD)) 201 continue; 202 203 bool requiresRelease = synthesizedPropertyRequiresRelease(PD); 204 if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) 205 != requiresRelease) { 206 const char *name = nullptr; 207 std::string buf; 208 llvm::raw_string_ostream os(buf); 209 210 if (requiresRelease) { 211 name = LOpts.getGC() == LangOptions::NonGC 212 ? "missing ivar release (leak)" 213 : "missing ivar release (Hybrid MM, non-GC)"; 214 215 os << "The '" << *ID << "' instance variable in '" << *D 216 << "' was retained by a synthesized property " 217 "but was not released in 'dealloc'"; 218 } else { 219 name = LOpts.getGC() == LangOptions::NonGC 220 ? "extra ivar release (use-after-release)" 221 : "extra ivar release (Hybrid MM, non-GC)"; 222 223 os << "The '" << *ID << "' instance variable in '" << *D 224 << "' was not retained by a synthesized property " 225 "but was released in 'dealloc'"; 226 } 227 228 // If @synthesize statement is missing, fall back to @property statement. 229 const Decl *SPDecl = I->getLocation().isValid() 230 ? static_cast<const Decl *>(I) 231 : static_cast<const Decl *>(PD); 232 PathDiagnosticLocation SPLoc = 233 PathDiagnosticLocation::createBegin(SPDecl, BR.getSourceManager()); 234 235 BR.EmitBasicReport(MD, Checker, name, 236 categories::CoreFoundationObjectiveC, os.str(), SPLoc); 237 } 238 } 239 } 240 241 //===----------------------------------------------------------------------===// 242 // ObjCDeallocChecker 243 //===----------------------------------------------------------------------===// 244 245 namespace { 246 class ObjCDeallocChecker : public Checker< 247 check::ASTDecl<ObjCImplementationDecl> > { 248 public: 249 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, 250 BugReporter &BR) const { 251 if (mgr.getLangOpts().getGC() == LangOptions::GCOnly || 252 mgr.getLangOpts().ObjCAutoRefCount) 253 return; 254 checkObjCDealloc(this, cast<ObjCImplementationDecl>(D), mgr.getLangOpts(), 255 BR); 256 } 257 }; 258 } 259 260 void ento::registerObjCDeallocChecker(CheckerManager &mgr) { 261 mgr.registerChecker<ObjCDeallocChecker>(); 262 } 263