IvarInvalidationChecker.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  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. There could be multiple
  17. // methods annotated with such annotations per class, either one can be used
  18. // to invalidate the ivar. An ivar or property are considered to be
  19. // invalidated if they are being assigned 'nil' or an invalidation method has
  20. // been called on them. An invalidation method should either invalidate all
  21. // the ivars or call another invalidation method (on self).
  22. //
  23. // Partial invalidor annotation allows to addess cases when ivars are
  24. // invalidated by other methods, which might or might not be called from
  25. // the invalidation method. The checker checks that each invalidation
  26. // method and all the partial methods cumulatively invalidate all ivars.
  27. // __attribute__((annotate("objc_instance_variable_invalidator_partial")));
  28. //
  29. //===----------------------------------------------------------------------===//
  30. #include "ClangSACheckers.h"
  31. #include "clang/AST/Attr.h"
  32. #include "clang/AST/DeclObjC.h"
  33. #include "clang/AST/StmtVisitor.h"
  34. #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
  35. #include "clang/StaticAnalyzer/Core/Checker.h"
  36. #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
  37. #include "llvm/ADT/DenseMap.h"
  38. #include "llvm/ADT/SetVector.h"
  39. #include "llvm/ADT/SmallString.h"
  40. using namespace clang;
  41. using namespace ento;
  42. namespace {
  43. struct ChecksFilter {
  44. /// Check for missing invalidation method declarations.
  45. DefaultBool check_MissingInvalidationMethod;
  46. /// Check that all ivars are invalidated.
  47. DefaultBool check_InstanceVariableInvalidation;
  48. CheckName checkName_MissingInvalidationMethod;
  49. CheckName checkName_InstanceVariableInvalidation;
  50. };
  51. class IvarInvalidationCheckerImpl {
  52. typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
  53. typedef llvm::DenseMap<const ObjCMethodDecl*,
  54. const ObjCIvarDecl*> MethToIvarMapTy;
  55. typedef llvm::DenseMap<const ObjCPropertyDecl*,
  56. const ObjCIvarDecl*> PropToIvarMapTy;
  57. typedef llvm::DenseMap<const ObjCIvarDecl*,
  58. const ObjCPropertyDecl*> IvarToPropMapTy;
  59. struct InvalidationInfo {
  60. /// Has the ivar been invalidated?
  61. bool IsInvalidated;
  62. /// The methods which can be used to invalidate the ivar.
  63. MethodSet InvalidationMethods;
  64. InvalidationInfo() : IsInvalidated(false) {}
  65. void addInvalidationMethod(const ObjCMethodDecl *MD) {
  66. InvalidationMethods.insert(MD);
  67. }
  68. bool needsInvalidation() const {
  69. return !InvalidationMethods.empty();
  70. }
  71. bool hasMethod(const ObjCMethodDecl *MD) {
  72. if (IsInvalidated)
  73. return true;
  74. for (MethodSet::iterator I = InvalidationMethods.begin(),
  75. E = InvalidationMethods.end(); I != E; ++I) {
  76. if (*I == MD) {
  77. IsInvalidated = true;
  78. return true;
  79. }
  80. }
  81. return false;
  82. }
  83. };
  84. typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
  85. /// Statement visitor, which walks the method body and flags the ivars
  86. /// referenced in it (either directly or via property).
  87. class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
  88. /// The set of Ivars which need to be invalidated.
  89. IvarSet &IVars;
  90. /// Flag is set as the result of a message send to another
  91. /// invalidation method.
  92. bool &CalledAnotherInvalidationMethod;
  93. /// Property setter to ivar mapping.
  94. const MethToIvarMapTy &PropertySetterToIvarMap;
  95. /// Property getter to ivar mapping.
  96. const MethToIvarMapTy &PropertyGetterToIvarMap;
  97. /// Property to ivar mapping.
  98. const PropToIvarMapTy &PropertyToIvarMap;
  99. /// The invalidation method being currently processed.
  100. const ObjCMethodDecl *InvalidationMethod;
  101. ASTContext &Ctx;
  102. /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
  103. const Expr *peel(const Expr *E) const;
  104. /// Does this expression represent zero: '0'?
  105. bool isZero(const Expr *E) const;
  106. /// Mark the given ivar as invalidated.
  107. void markInvalidated(const ObjCIvarDecl *Iv);
  108. /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
  109. /// invalidated.
  110. void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
  111. /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
  112. /// it as invalidated.
  113. void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
  114. /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
  115. /// if yes, marks it as invalidated.
  116. void checkObjCMessageExpr(const ObjCMessageExpr *ME);
  117. /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
  118. void check(const Expr *E);
  119. public:
  120. MethodCrawler(IvarSet &InIVars,
  121. bool &InCalledAnotherInvalidationMethod,
  122. const MethToIvarMapTy &InPropertySetterToIvarMap,
  123. const MethToIvarMapTy &InPropertyGetterToIvarMap,
  124. const PropToIvarMapTy &InPropertyToIvarMap,
  125. ASTContext &InCtx)
  126. : IVars(InIVars),
  127. CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
  128. PropertySetterToIvarMap(InPropertySetterToIvarMap),
  129. PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
  130. PropertyToIvarMap(InPropertyToIvarMap),
  131. InvalidationMethod(nullptr),
  132. Ctx(InCtx) {}
  133. void VisitStmt(const Stmt *S) { VisitChildren(S); }
  134. void VisitBinaryOperator(const BinaryOperator *BO);
  135. void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
  136. void VisitChildren(const Stmt *S) {
  137. for (const Stmt *Child : S->children()) {
  138. if (Child)
  139. this->Visit(Child);
  140. if (CalledAnotherInvalidationMethod)
  141. return;
  142. }
  143. }
  144. };
  145. /// Check if the any of the methods inside the interface are annotated with
  146. /// the invalidation annotation, update the IvarInfo accordingly.
  147. /// \param LookForPartial is set when we are searching for partial
  148. /// invalidators.
  149. static void containsInvalidationMethod(const ObjCContainerDecl *D,
  150. InvalidationInfo &Out,
  151. bool LookForPartial);
  152. /// Check if ivar should be tracked and add to TrackedIvars if positive.
  153. /// Returns true if ivar should be tracked.
  154. static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
  155. const ObjCIvarDecl **FirstIvarDecl);
  156. /// Given the property declaration, and the list of tracked ivars, finds
  157. /// the ivar backing the property when possible. Returns '0' when no such
  158. /// ivar could be found.
  159. static const ObjCIvarDecl *findPropertyBackingIvar(
  160. const ObjCPropertyDecl *Prop,
  161. const ObjCInterfaceDecl *InterfaceD,
  162. IvarSet &TrackedIvars,
  163. const ObjCIvarDecl **FirstIvarDecl);
  164. /// Print ivar name or the property if the given ivar backs a property.
  165. static void printIvar(llvm::raw_svector_ostream &os,
  166. const ObjCIvarDecl *IvarDecl,
  167. const IvarToPropMapTy &IvarToPopertyMap);
  168. void reportNoInvalidationMethod(CheckName CheckName,
  169. const ObjCIvarDecl *FirstIvarDecl,
  170. const IvarToPropMapTy &IvarToPopertyMap,
  171. const ObjCInterfaceDecl *InterfaceD,
  172. bool MissingDeclaration) const;
  173. void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
  174. const IvarToPropMapTy &IvarToPopertyMap,
  175. const ObjCMethodDecl *MethodD) const;
  176. AnalysisManager& Mgr;
  177. BugReporter &BR;
  178. /// Filter on the checks performed.
  179. const ChecksFilter &Filter;
  180. public:
  181. IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
  182. BugReporter &InBR,
  183. const ChecksFilter &InFilter) :
  184. Mgr (InMgr), BR(InBR), Filter(InFilter) {}
  185. void visit(const ObjCImplementationDecl *D) const;
  186. };
  187. static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
  188. for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) {
  189. if (!LookForPartial &&
  190. Ann->getAnnotation() == "objc_instance_variable_invalidator")
  191. return true;
  192. if (LookForPartial &&
  193. Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
  194. return true;
  195. }
  196. return false;
  197. }
  198. void IvarInvalidationCheckerImpl::containsInvalidationMethod(
  199. const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
  200. if (!D)
  201. return;
  202. assert(!isa<ObjCImplementationDecl>(D));
  203. // TODO: Cache the results.
  204. // Check all methods.
  205. for (const auto *MDI : D->methods())
  206. if (isInvalidationMethod(MDI, Partial))
  207. OutInfo.addInvalidationMethod(
  208. cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
  209. // If interface, check all parent protocols and super.
  210. if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
  211. // Visit all protocols.
  212. for (const auto *I : InterfD->protocols())
  213. containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
  214. // Visit all categories in case the invalidation method is declared in
  215. // a category.
  216. for (const auto *Ext : InterfD->visible_extensions())
  217. containsInvalidationMethod(Ext, OutInfo, Partial);
  218. containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
  219. return;
  220. }
  221. // If protocol, check all parent protocols.
  222. if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
  223. for (const auto *I : ProtD->protocols()) {
  224. containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
  225. }
  226. return;
  227. }
  228. return;
  229. }
  230. bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv,
  231. IvarSet &TrackedIvars,
  232. const ObjCIvarDecl **FirstIvarDecl) {
  233. QualType IvQTy = Iv->getType();
  234. const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
  235. if (!IvTy)
  236. return false;
  237. const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
  238. InvalidationInfo Info;
  239. containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
  240. if (Info.needsInvalidation()) {
  241. const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
  242. TrackedIvars[I] = Info;
  243. if (!*FirstIvarDecl)
  244. *FirstIvarDecl = I;
  245. return true;
  246. }
  247. return false;
  248. }
  249. const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
  250. const ObjCPropertyDecl *Prop,
  251. const ObjCInterfaceDecl *InterfaceD,
  252. IvarSet &TrackedIvars,
  253. const ObjCIvarDecl **FirstIvarDecl) {
  254. const ObjCIvarDecl *IvarD = nullptr;
  255. // Lookup for the synthesized case.
  256. IvarD = Prop->getPropertyIvarDecl();
  257. // We only track the ivars/properties that are defined in the current
  258. // class (not the parent).
  259. if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
  260. if (TrackedIvars.count(IvarD)) {
  261. return IvarD;
  262. }
  263. // If the ivar is synthesized we still want to track it.
  264. if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
  265. return IvarD;
  266. }
  267. // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
  268. StringRef PropName = Prop->getIdentifier()->getName();
  269. for (IvarSet::const_iterator I = TrackedIvars.begin(),
  270. E = TrackedIvars.end(); I != E; ++I) {
  271. const ObjCIvarDecl *Iv = I->first;
  272. StringRef IvarName = Iv->getName();
  273. if (IvarName == PropName)
  274. return Iv;
  275. SmallString<128> PropNameWithUnderscore;
  276. {
  277. llvm::raw_svector_ostream os(PropNameWithUnderscore);
  278. os << '_' << PropName;
  279. }
  280. if (IvarName == PropNameWithUnderscore)
  281. return Iv;
  282. }
  283. // Note, this is a possible source of false positives. We could look at the
  284. // getter implementation to find the ivar when its name is not derived from
  285. // the property name.
  286. return nullptr;
  287. }
  288. void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
  289. const ObjCIvarDecl *IvarDecl,
  290. const IvarToPropMapTy &IvarToPopertyMap) {
  291. if (IvarDecl->getSynthesize()) {
  292. const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
  293. assert(PD &&"Do we synthesize ivars for something other than properties?");
  294. os << "Property "<< PD->getName() << " ";
  295. } else {
  296. os << "Instance variable "<< IvarDecl->getName() << " ";
  297. }
  298. }
  299. // Check that the invalidatable interfaces with ivars/properties implement the
  300. // invalidation methods.
  301. void IvarInvalidationCheckerImpl::
  302. visit(const ObjCImplementationDecl *ImplD) const {
  303. // Collect all ivars that need cleanup.
  304. IvarSet Ivars;
  305. // Record the first Ivar needing invalidation; used in reporting when only
  306. // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
  307. // deterministic output.
  308. const ObjCIvarDecl *FirstIvarDecl = nullptr;
  309. const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
  310. // Collect ivars declared in this class, its extensions and its implementation
  311. ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
  312. for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
  313. Iv= Iv->getNextIvar())
  314. trackIvar(Iv, Ivars, &FirstIvarDecl);
  315. // Construct Property/Property Accessor to Ivar maps to assist checking if an
  316. // ivar which is backing a property has been reset.
  317. MethToIvarMapTy PropSetterToIvarMap;
  318. MethToIvarMapTy PropGetterToIvarMap;
  319. PropToIvarMapTy PropertyToIvarMap;
  320. IvarToPropMapTy IvarToPopertyMap;
  321. ObjCInterfaceDecl::PropertyMap PropMap;
  322. ObjCInterfaceDecl::PropertyDeclOrder PropOrder;
  323. InterfaceD->collectPropertiesToImplement(PropMap, PropOrder);
  324. for (ObjCInterfaceDecl::PropertyMap::iterator
  325. I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
  326. const ObjCPropertyDecl *PD = I->second;
  327. const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
  328. &FirstIvarDecl);
  329. if (!ID)
  330. continue;
  331. // Store the mappings.
  332. PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
  333. PropertyToIvarMap[PD] = ID;
  334. IvarToPopertyMap[ID] = PD;
  335. // Find the setter and the getter.
  336. const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
  337. if (SetterD) {
  338. SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
  339. PropSetterToIvarMap[SetterD] = ID;
  340. }
  341. const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
  342. if (GetterD) {
  343. GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl());
  344. PropGetterToIvarMap[GetterD] = ID;
  345. }
  346. }
  347. // If no ivars need invalidation, there is nothing to check here.
  348. if (Ivars.empty())
  349. return;
  350. // Find all partial invalidation methods.
  351. InvalidationInfo PartialInfo;
  352. containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true);
  353. // Remove ivars invalidated by the partial invalidation methods. They do not
  354. // need to be invalidated in the regular invalidation methods.
  355. bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false;
  356. for (MethodSet::iterator
  357. I = PartialInfo.InvalidationMethods.begin(),
  358. E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
  359. const ObjCMethodDecl *InterfD = *I;
  360. // Get the corresponding method in the @implementation.
  361. const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
  362. InterfD->isInstanceMethod());
  363. if (D && D->hasBody()) {
  364. AtImplementationContainsAtLeastOnePartialInvalidationMethod = true;
  365. bool CalledAnotherInvalidationMethod = false;
  366. // The MethodCrowler is going to remove the invalidated ivars.
  367. MethodCrawler(Ivars,
  368. CalledAnotherInvalidationMethod,
  369. PropSetterToIvarMap,
  370. PropGetterToIvarMap,
  371. PropertyToIvarMap,
  372. BR.getContext()).VisitStmt(D->getBody());
  373. // If another invalidation method was called, trust that full invalidation
  374. // has occurred.
  375. if (CalledAnotherInvalidationMethod)
  376. Ivars.clear();
  377. }
  378. }
  379. // If all ivars have been invalidated by partial invalidators, there is
  380. // nothing to check here.
  381. if (Ivars.empty())
  382. return;
  383. // Find all invalidation methods in this @interface declaration and parents.
  384. InvalidationInfo Info;
  385. containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
  386. // Report an error in case none of the invalidation methods are declared.
  387. if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
  388. if (Filter.check_MissingInvalidationMethod)
  389. reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
  390. FirstIvarDecl, IvarToPopertyMap, InterfaceD,
  391. /*MissingDeclaration*/ true);
  392. // If there are no invalidation methods, there is no ivar validation work
  393. // to be done.
  394. return;
  395. }
  396. // Only check if Ivars are invalidated when InstanceVariableInvalidation
  397. // has been requested.
  398. if (!Filter.check_InstanceVariableInvalidation)
  399. return;
  400. // Check that all ivars are invalidated by the invalidation methods.
  401. bool AtImplementationContainsAtLeastOneInvalidationMethod = false;
  402. for (MethodSet::iterator I = Info.InvalidationMethods.begin(),
  403. E = Info.InvalidationMethods.end(); I != E; ++I) {
  404. const ObjCMethodDecl *InterfD = *I;
  405. // Get the corresponding method in the @implementation.
  406. const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
  407. InterfD->isInstanceMethod());
  408. if (D && D->hasBody()) {
  409. AtImplementationContainsAtLeastOneInvalidationMethod = true;
  410. // Get a copy of ivars needing invalidation.
  411. IvarSet IvarsI = Ivars;
  412. bool CalledAnotherInvalidationMethod = false;
  413. MethodCrawler(IvarsI,
  414. CalledAnotherInvalidationMethod,
  415. PropSetterToIvarMap,
  416. PropGetterToIvarMap,
  417. PropertyToIvarMap,
  418. BR.getContext()).VisitStmt(D->getBody());
  419. // If another invalidation method was called, trust that full invalidation
  420. // has occurred.
  421. if (CalledAnotherInvalidationMethod)
  422. continue;
  423. // Warn on the ivars that were not invalidated by the method.
  424. for (IvarSet::const_iterator
  425. I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I)
  426. reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D);
  427. }
  428. }
  429. // Report an error in case none of the invalidation methods are implemented.
  430. if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
  431. if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
  432. // Warn on the ivars that were not invalidated by the prrtial
  433. // invalidation methods.
  434. for (IvarSet::const_iterator
  435. I = Ivars.begin(), E = Ivars.end(); I != E; ++I)
  436. reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, nullptr);
  437. } else {
  438. // Otherwise, no invalidation methods were implemented.
  439. reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
  440. FirstIvarDecl, IvarToPopertyMap, InterfaceD,
  441. /*MissingDeclaration*/ false);
  442. }
  443. }
  444. }
  445. void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
  446. CheckName CheckName, const ObjCIvarDecl *FirstIvarDecl,
  447. const IvarToPropMapTy &IvarToPopertyMap,
  448. const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const {
  449. SmallString<128> sbuf;
  450. llvm::raw_svector_ostream os(sbuf);
  451. assert(FirstIvarDecl);
  452. printIvar(os, FirstIvarDecl, IvarToPopertyMap);
  453. os << "needs to be invalidated; ";
  454. if (MissingDeclaration)
  455. os << "no invalidation method is declared for ";
  456. else
  457. os << "no invalidation method is defined in the @implementation for ";
  458. os << InterfaceD->getName();
  459. PathDiagnosticLocation IvarDecLocation =
  460. PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
  461. BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation",
  462. categories::CoreFoundationObjectiveC, os.str(),
  463. IvarDecLocation);
  464. }
  465. void IvarInvalidationCheckerImpl::
  466. reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
  467. const IvarToPropMapTy &IvarToPopertyMap,
  468. const ObjCMethodDecl *MethodD) const {
  469. SmallString<128> sbuf;
  470. llvm::raw_svector_ostream os(sbuf);
  471. printIvar(os, IvarD, IvarToPopertyMap);
  472. os << "needs to be invalidated or set to nil";
  473. if (MethodD) {
  474. PathDiagnosticLocation MethodDecLocation =
  475. PathDiagnosticLocation::createEnd(MethodD->getBody(),
  476. BR.getSourceManager(),
  477. Mgr.getAnalysisDeclContext(MethodD));
  478. BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
  479. "Incomplete invalidation",
  480. categories::CoreFoundationObjectiveC, os.str(),
  481. MethodDecLocation);
  482. } else {
  483. BR.EmitBasicReport(
  484. IvarD, Filter.checkName_InstanceVariableInvalidation,
  485. "Incomplete invalidation", categories::CoreFoundationObjectiveC,
  486. os.str(),
  487. PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager()));
  488. }
  489. }
  490. void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
  491. const ObjCIvarDecl *Iv) {
  492. IvarSet::iterator I = IVars.find(Iv);
  493. if (I != IVars.end()) {
  494. // If InvalidationMethod is present, we are processing the message send and
  495. // should ensure we are invalidating with the appropriate method,
  496. // otherwise, we are processing setting to 'nil'.
  497. if (!InvalidationMethod ||
  498. (InvalidationMethod && I->second.hasMethod(InvalidationMethod)))
  499. IVars.erase(I);
  500. }
  501. }
  502. const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
  503. E = E->IgnoreParenCasts();
  504. if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
  505. E = POE->getSyntacticForm()->IgnoreParenCasts();
  506. if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
  507. E = OVE->getSourceExpr()->IgnoreParenCasts();
  508. return E;
  509. }
  510. void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
  511. const ObjCIvarRefExpr *IvarRef) {
  512. if (const Decl *D = IvarRef->getDecl())
  513. markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
  514. }
  515. void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
  516. const ObjCMessageExpr *ME) {
  517. const ObjCMethodDecl *MD = ME->getMethodDecl();
  518. if (MD) {
  519. MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
  520. MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
  521. if (IvI != PropertyGetterToIvarMap.end())
  522. markInvalidated(IvI->second);
  523. }
  524. }
  525. void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
  526. const ObjCPropertyRefExpr *PA) {
  527. if (PA->isExplicitProperty()) {
  528. const ObjCPropertyDecl *PD = PA->getExplicitProperty();
  529. if (PD) {
  530. PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
  531. PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
  532. if (IvI != PropertyToIvarMap.end())
  533. markInvalidated(IvI->second);
  534. return;
  535. }
  536. }
  537. if (PA->isImplicitProperty()) {
  538. const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
  539. if (MD) {
  540. MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
  541. MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
  542. if (IvI != PropertyGetterToIvarMap.end())
  543. markInvalidated(IvI->second);
  544. return;
  545. }
  546. }
  547. }
  548. bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
  549. E = peel(E);
  550. return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
  551. != Expr::NPCK_NotNull);
  552. }
  553. void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
  554. E = peel(E);
  555. if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
  556. checkObjCIvarRefExpr(IvarRef);
  557. return;
  558. }
  559. if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
  560. checkObjCPropertyRefExpr(PropRef);
  561. return;
  562. }
  563. if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
  564. checkObjCMessageExpr(MsgExpr);
  565. return;
  566. }
  567. }
  568. void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
  569. const BinaryOperator *BO) {
  570. VisitStmt(BO);
  571. // Do we assign/compare against zero? If yes, check the variable we are
  572. // assigning to.
  573. BinaryOperatorKind Opcode = BO->getOpcode();
  574. if (Opcode != BO_Assign &&
  575. Opcode != BO_EQ &&
  576. Opcode != BO_NE)
  577. return;
  578. if (isZero(BO->getRHS())) {
  579. check(BO->getLHS());
  580. return;
  581. }
  582. if (Opcode != BO_Assign && isZero(BO->getLHS())) {
  583. check(BO->getRHS());
  584. return;
  585. }
  586. }
  587. void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
  588. const ObjCMessageExpr *ME) {
  589. const ObjCMethodDecl *MD = ME->getMethodDecl();
  590. const Expr *Receiver = ME->getInstanceReceiver();
  591. // Stop if we are calling '[self invalidate]'.
  592. if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false))
  593. if (Receiver->isObjCSelfExpr()) {
  594. CalledAnotherInvalidationMethod = true;
  595. return;
  596. }
  597. // Check if we call a setter and set the property to 'nil'.
  598. if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
  599. MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
  600. MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
  601. if (IvI != PropertySetterToIvarMap.end()) {
  602. markInvalidated(IvI->second);
  603. return;
  604. }
  605. }
  606. // Check if we call the 'invalidation' routine on the ivar.
  607. if (Receiver) {
  608. InvalidationMethod = MD;
  609. check(Receiver->IgnoreParenCasts());
  610. InvalidationMethod = nullptr;
  611. }
  612. VisitStmt(ME);
  613. }
  614. }
  615. // Register the checkers.
  616. namespace {
  617. class IvarInvalidationChecker :
  618. public Checker<check::ASTDecl<ObjCImplementationDecl> > {
  619. public:
  620. ChecksFilter Filter;
  621. public:
  622. void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
  623. BugReporter &BR) const {
  624. IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
  625. Walker.visit(D);
  626. }
  627. };
  628. }
  629. #define REGISTER_CHECKER(name) \
  630. void ento::register##name(CheckerManager &mgr) { \
  631. IvarInvalidationChecker *checker = \
  632. mgr.registerChecker<IvarInvalidationChecker>(); \
  633. checker->Filter.check_##name = true; \
  634. checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \
  635. }
  636. REGISTER_CHECKER(InstanceVariableInvalidation)
  637. REGISTER_CHECKER(MissingInvalidationMethod)