clang API Documentation
00001 //==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file defines a CheckObjCDealloc, a checker that 00011 // analyzes an Objective-C class's implementation to determine if it 00012 // correctly implements -dealloc. 00013 // 00014 //===----------------------------------------------------------------------===// 00015 00016 #include "ClangSACheckers.h" 00017 #include "clang/AST/Attr.h" 00018 #include "clang/AST/DeclObjC.h" 00019 #include "clang/AST/Expr.h" 00020 #include "clang/AST/ExprObjC.h" 00021 #include "clang/Basic/LangOptions.h" 00022 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 00023 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 00024 #include "clang/StaticAnalyzer/Core/Checker.h" 00025 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 00026 #include "llvm/Support/raw_ostream.h" 00027 00028 using namespace clang; 00029 using namespace ento; 00030 00031 static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID, 00032 const ObjCPropertyDecl *PD, 00033 Selector Release, 00034 IdentifierInfo* SelfII, 00035 ASTContext &Ctx) { 00036 00037 // [mMyIvar release] 00038 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) 00039 if (ME->getSelector() == Release) 00040 if (ME->getInstanceReceiver()) 00041 if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) 00042 if (ObjCIvarRefExpr *E = dyn_cast<ObjCIvarRefExpr>(Receiver)) 00043 if (E->getDecl() == ID) 00044 return true; 00045 00046 // [self setMyIvar:nil]; 00047 if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) 00048 if (ME->getInstanceReceiver()) 00049 if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) 00050 if (DeclRefExpr *E = dyn_cast<DeclRefExpr>(Receiver)) 00051 if (E->getDecl()->getIdentifier() == SelfII) 00052 if (ME->getMethodDecl() == PD->getSetterMethodDecl() && 00053 ME->getNumArgs() == 1 && 00054 ME->getArg(0)->isNullPointerConstant(Ctx, 00055 Expr::NPC_ValueDependentIsNull)) 00056 return true; 00057 00058 // self.myIvar = nil; 00059 if (BinaryOperator* BO = dyn_cast<BinaryOperator>(S)) 00060 if (BO->isAssignmentOp()) 00061 if (ObjCPropertyRefExpr *PRE = 00062 dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts())) 00063 if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD) 00064 if (BO->getRHS()->isNullPointerConstant(Ctx, 00065 Expr::NPC_ValueDependentIsNull)) { 00066 // This is only a 'release' if the property kind is not 00067 // 'assign'. 00068 return PD->getSetterKind() != ObjCPropertyDecl::Assign; 00069 } 00070 00071 // Recurse to children. 00072 for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I) 00073 if (*I && scan_ivar_release(*I, ID, PD, Release, SelfII, Ctx)) 00074 return true; 00075 00076 return false; 00077 } 00078 00079 static void checkObjCDealloc(const CheckerBase *Checker, 00080 const ObjCImplementationDecl *D, 00081 const LangOptions &LOpts, BugReporter &BR) { 00082 00083 assert (LOpts.getGC() != LangOptions::GCOnly); 00084 00085 ASTContext &Ctx = BR.getContext(); 00086 const ObjCInterfaceDecl *ID = D->getClassInterface(); 00087 00088 // Does the class contain any ivars that are pointers (or id<...>)? 00089 // If not, skip the check entirely. 00090 // NOTE: This is motivated by PR 2517: 00091 // http://llvm.org/bugs/show_bug.cgi?id=2517 00092 00093 bool containsPointerIvar = false; 00094 00095 for (const auto *Ivar : ID->ivars()) { 00096 QualType T = Ivar->getType(); 00097 00098 if (!T->isObjCObjectPointerType() || 00099 Ivar->hasAttr<IBOutletAttr>() || // Skip IBOutlets. 00100 Ivar->hasAttr<IBOutletCollectionAttr>()) // Skip IBOutletCollections. 00101 continue; 00102 00103 containsPointerIvar = true; 00104 break; 00105 } 00106 00107 if (!containsPointerIvar) 00108 return; 00109 00110 // Determine if the class subclasses NSObject. 00111 IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); 00112 IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); 00113 00114 00115 for ( ; ID ; ID = ID->getSuperClass()) { 00116 IdentifierInfo *II = ID->getIdentifier(); 00117 00118 if (II == NSObjectII) 00119 break; 00120 00121 // FIXME: For now, ignore classes that subclass SenTestCase, as these don't 00122 // need to implement -dealloc. They implement tear down in another way, 00123 // which we should try and catch later. 00124 // http://llvm.org/bugs/show_bug.cgi?id=3187 00125 if (II == SenTestCaseII) 00126 return; 00127 } 00128 00129 if (!ID) 00130 return; 00131 00132 // Get the "dealloc" selector. 00133 IdentifierInfo* II = &Ctx.Idents.get("dealloc"); 00134 Selector S = Ctx.Selectors.getSelector(0, &II); 00135 const ObjCMethodDecl *MD = nullptr; 00136 00137 // Scan the instance methods for "dealloc". 00138 for (const auto *I : D->instance_methods()) { 00139 if (I->getSelector() == S) { 00140 MD = I; 00141 break; 00142 } 00143 } 00144 00145 PathDiagnosticLocation DLoc = 00146 PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); 00147 00148 if (!MD) { // No dealloc found. 00149 00150 const char* name = LOpts.getGC() == LangOptions::NonGC 00151 ? "missing -dealloc" 00152 : "missing -dealloc (Hybrid MM, non-GC)"; 00153 00154 std::string buf; 00155 llvm::raw_string_ostream os(buf); 00156 os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method"; 00157 00158 BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC, 00159 os.str(), DLoc); 00160 return; 00161 } 00162 00163 // Get the "release" selector. 00164 IdentifierInfo* RII = &Ctx.Idents.get("release"); 00165 Selector RS = Ctx.Selectors.getSelector(0, &RII); 00166 00167 // Get the "self" identifier 00168 IdentifierInfo* SelfII = &Ctx.Idents.get("self"); 00169 00170 // Scan for missing and extra releases of ivars used by implementations 00171 // of synthesized properties 00172 for (const auto *I : D->property_impls()) { 00173 // We can only check the synthesized properties 00174 if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) 00175 continue; 00176 00177 ObjCIvarDecl *ID = I->getPropertyIvarDecl(); 00178 if (!ID) 00179 continue; 00180 00181 QualType T = ID->getType(); 00182 if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars 00183 continue; 00184 00185 const ObjCPropertyDecl *PD = I->getPropertyDecl(); 00186 if (!PD) 00187 continue; 00188 00189 // ivars cannot be set via read-only properties, so we'll skip them 00190 if (PD->isReadOnly()) 00191 continue; 00192 00193 // ivar must be released if and only if the kind of setter was not 'assign' 00194 bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign; 00195 if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) 00196 != requiresRelease) { 00197 const char *name = nullptr; 00198 std::string buf; 00199 llvm::raw_string_ostream os(buf); 00200 00201 if (requiresRelease) { 00202 name = LOpts.getGC() == LangOptions::NonGC 00203 ? "missing ivar release (leak)" 00204 : "missing ivar release (Hybrid MM, non-GC)"; 00205 00206 os << "The '" << *ID 00207 << "' instance variable was retained by a synthesized property but " 00208 "wasn't released in 'dealloc'"; 00209 } else { 00210 name = LOpts.getGC() == LangOptions::NonGC 00211 ? "extra ivar release (use-after-release)" 00212 : "extra ivar release (Hybrid MM, non-GC)"; 00213 00214 os << "The '" << *ID 00215 << "' instance variable was not retained by a synthesized property " 00216 "but was released in 'dealloc'"; 00217 } 00218 00219 PathDiagnosticLocation SDLoc = 00220 PathDiagnosticLocation::createBegin(I, BR.getSourceManager()); 00221 00222 BR.EmitBasicReport(MD, Checker, name, 00223 categories::CoreFoundationObjectiveC, os.str(), SDLoc); 00224 } 00225 } 00226 } 00227 00228 //===----------------------------------------------------------------------===// 00229 // ObjCDeallocChecker 00230 //===----------------------------------------------------------------------===// 00231 00232 namespace { 00233 class ObjCDeallocChecker : public Checker< 00234 check::ASTDecl<ObjCImplementationDecl> > { 00235 public: 00236 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, 00237 BugReporter &BR) const { 00238 if (mgr.getLangOpts().getGC() == LangOptions::GCOnly) 00239 return; 00240 checkObjCDealloc(this, cast<ObjCImplementationDecl>(D), mgr.getLangOpts(), 00241 BR); 00242 } 00243 }; 00244 } 00245 00246 void ento::registerObjCDeallocChecker(CheckerManager &mgr) { 00247 mgr.registerChecker<ObjCDeallocChecker>(); 00248 }