CheckObjCDealloc.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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. #include "ClangSACheckers.h"
  16. #include "clang/AST/Attr.h"
  17. #include "clang/AST/DeclObjC.h"
  18. #include "clang/AST/Expr.h"
  19. #include "clang/AST/ExprObjC.h"
  20. #include "clang/Basic/LangOptions.h"
  21. #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
  22. #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
  23. #include "clang/StaticAnalyzer/Core/Checker.h"
  24. #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
  25. #include "llvm/Support/raw_ostream.h"
  26. using namespace clang;
  27. using namespace ento;
  28. static bool scan_ivar_release(Stmt *S, ObjCIvarDecl *ID,
  29. const ObjCPropertyDecl *PD,
  30. Selector Release,
  31. IdentifierInfo* SelfII,
  32. ASTContext &Ctx) {
  33. // [mMyIvar release]
  34. if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S))
  35. if (ME->getSelector() == Release)
  36. if (ME->getInstanceReceiver())
  37. if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts())
  38. if (ObjCIvarRefExpr *E = dyn_cast<ObjCIvarRefExpr>(Receiver))
  39. if (E->getDecl() == ID)
  40. return true;
  41. // [self setMyIvar:nil];
  42. if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S))
  43. if (ME->getInstanceReceiver())
  44. if (Expr *Receiver = ME->getInstanceReceiver()->IgnoreParenCasts())
  45. if (DeclRefExpr *E = dyn_cast<DeclRefExpr>(Receiver))
  46. if (E->getDecl()->getIdentifier() == SelfII)
  47. if (ME->getMethodDecl() == PD->getSetterMethodDecl() &&
  48. ME->getNumArgs() == 1 &&
  49. ME->getArg(0)->isNullPointerConstant(Ctx,
  50. Expr::NPC_ValueDependentIsNull))
  51. return true;
  52. // self.myIvar = nil;
  53. if (BinaryOperator* BO = dyn_cast<BinaryOperator>(S))
  54. if (BO->isAssignmentOp())
  55. if (ObjCPropertyRefExpr *PRE =
  56. dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts()))
  57. if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD)
  58. if (BO->getRHS()->isNullPointerConstant(Ctx,
  59. Expr::NPC_ValueDependentIsNull)) {
  60. // This is only a 'release' if the property kind is not
  61. // 'assign'.
  62. return PD->getSetterKind() != ObjCPropertyDecl::Assign;
  63. }
  64. // Recurse to children.
  65. for (Stmt *SubStmt : S->children())
  66. if (SubStmt && scan_ivar_release(SubStmt, ID, PD, Release, SelfII, Ctx))
  67. return true;
  68. return false;
  69. }
  70. static void checkObjCDealloc(const CheckerBase *Checker,
  71. const ObjCImplementationDecl *D,
  72. const LangOptions &LOpts, BugReporter &BR) {
  73. assert (LOpts.getGC() != LangOptions::GCOnly);
  74. ASTContext &Ctx = BR.getContext();
  75. const ObjCInterfaceDecl *ID = D->getClassInterface();
  76. // Does the class contain any ivars that are pointers (or id<...>)?
  77. // If not, skip the check entirely.
  78. // NOTE: This is motivated by PR 2517:
  79. // http://llvm.org/bugs/show_bug.cgi?id=2517
  80. bool containsPointerIvar = false;
  81. for (const auto *Ivar : ID->ivars()) {
  82. QualType T = Ivar->getType();
  83. if (!T->isObjCObjectPointerType() ||
  84. Ivar->hasAttr<IBOutletAttr>() || // Skip IBOutlets.
  85. Ivar->hasAttr<IBOutletCollectionAttr>()) // Skip IBOutletCollections.
  86. continue;
  87. containsPointerIvar = true;
  88. break;
  89. }
  90. if (!containsPointerIvar)
  91. return;
  92. // Determine if the class subclasses NSObject.
  93. IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");
  94. IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase");
  95. for ( ; ID ; ID = ID->getSuperClass()) {
  96. IdentifierInfo *II = ID->getIdentifier();
  97. if (II == NSObjectII)
  98. break;
  99. // FIXME: For now, ignore classes that subclass SenTestCase, as these don't
  100. // need to implement -dealloc. They implement tear down in another way,
  101. // which we should try and catch later.
  102. // http://llvm.org/bugs/show_bug.cgi?id=3187
  103. if (II == SenTestCaseII)
  104. return;
  105. }
  106. if (!ID)
  107. return;
  108. // Get the "dealloc" selector.
  109. IdentifierInfo* II = &Ctx.Idents.get("dealloc");
  110. Selector S = Ctx.Selectors.getSelector(0, &II);
  111. const ObjCMethodDecl *MD = nullptr;
  112. // Scan the instance methods for "dealloc".
  113. for (const auto *I : D->instance_methods()) {
  114. if (I->getSelector() == S) {
  115. MD = I;
  116. break;
  117. }
  118. }
  119. PathDiagnosticLocation DLoc =
  120. PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
  121. if (!MD) { // No dealloc found.
  122. const char* name = LOpts.getGC() == LangOptions::NonGC
  123. ? "missing -dealloc"
  124. : "missing -dealloc (Hybrid MM, non-GC)";
  125. std::string buf;
  126. llvm::raw_string_ostream os(buf);
  127. os << "Objective-C class '" << *D << "' lacks a 'dealloc' instance method";
  128. BR.EmitBasicReport(D, Checker, name, categories::CoreFoundationObjectiveC,
  129. os.str(), DLoc);
  130. return;
  131. }
  132. // Get the "release" selector.
  133. IdentifierInfo* RII = &Ctx.Idents.get("release");
  134. Selector RS = Ctx.Selectors.getSelector(0, &RII);
  135. // Get the "self" identifier
  136. IdentifierInfo* SelfII = &Ctx.Idents.get("self");
  137. // Scan for missing and extra releases of ivars used by implementations
  138. // of synthesized properties
  139. for (const auto *I : D->property_impls()) {
  140. // We can only check the synthesized properties
  141. if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
  142. continue;
  143. ObjCIvarDecl *ID = I->getPropertyIvarDecl();
  144. if (!ID)
  145. continue;
  146. QualType T = ID->getType();
  147. if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars
  148. continue;
  149. const ObjCPropertyDecl *PD = I->getPropertyDecl();
  150. if (!PD)
  151. continue;
  152. // ivars cannot be set via read-only properties, so we'll skip them
  153. if (PD->isReadOnly())
  154. continue;
  155. // ivar must be released if and only if the kind of setter was not 'assign'
  156. bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign;
  157. if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx)
  158. != requiresRelease) {
  159. const char *name = nullptr;
  160. std::string buf;
  161. llvm::raw_string_ostream os(buf);
  162. if (requiresRelease) {
  163. name = LOpts.getGC() == LangOptions::NonGC
  164. ? "missing ivar release (leak)"
  165. : "missing ivar release (Hybrid MM, non-GC)";
  166. os << "The '" << *ID
  167. << "' instance variable was retained by a synthesized property but "
  168. "wasn't released in 'dealloc'";
  169. } else {
  170. name = LOpts.getGC() == LangOptions::NonGC
  171. ? "extra ivar release (use-after-release)"
  172. : "extra ivar release (Hybrid MM, non-GC)";
  173. os << "The '" << *ID
  174. << "' instance variable was not retained by a synthesized property "
  175. "but was released in 'dealloc'";
  176. }
  177. PathDiagnosticLocation SDLoc =
  178. PathDiagnosticLocation::createBegin(I, BR.getSourceManager());
  179. BR.EmitBasicReport(MD, Checker, name,
  180. categories::CoreFoundationObjectiveC, os.str(), SDLoc);
  181. }
  182. }
  183. }
  184. //===----------------------------------------------------------------------===//
  185. // ObjCDeallocChecker
  186. //===----------------------------------------------------------------------===//
  187. namespace {
  188. class ObjCDeallocChecker : public Checker<
  189. check::ASTDecl<ObjCImplementationDecl> > {
  190. public:
  191. void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
  192. BugReporter &BR) const {
  193. if (mgr.getLangOpts().getGC() == LangOptions::GCOnly)
  194. return;
  195. checkObjCDealloc(this, cast<ObjCImplementationDecl>(D), mgr.getLangOpts(),
  196. BR);
  197. }
  198. };
  199. }
  200. void ento::registerObjCDeallocChecker(CheckerManager &mgr) {
  201. mgr.registerChecker<ObjCDeallocChecker>();
  202. }