IvarInvalidationChecker.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. //===- IvarInvalidationChecker.cpp ------------------------------*- C++ -*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This checker implements annotation driven invalidation checking. If a class
  10. // contains a method annotated with 'objc_instance_variable_invalidator',
  11. // - (void) foo
  12. // __attribute__((annotate("objc_instance_variable_invalidator")));
  13. // all the "ivalidatable" instance variables of this class should be
  14. // invalidated. We call an instance variable ivalidatable if it is an object of
  15. // a class which contains an invalidation method. There could be multiple
  16. // methods annotated with such annotations per class, either one can be used
  17. // to invalidate the ivar. An ivar or property are considered to be
  18. // invalidated if they are being assigned 'nil' or an invalidation method has
  19. // been called on them. An invalidation method should either invalidate all
  20. // the ivars or call another invalidation method (on self).
  21. //
  22. // Partial invalidor annotation allows to address cases when ivars are
  23. // invalidated by other methods, which might or might not be called from
  24. // the invalidation method. The checker checks that each invalidation
  25. // method and all the partial methods cumulatively invalidate all ivars.
  26. // __attribute__((annotate("objc_instance_variable_invalidator_partial")));
  27. //
  28. //===----------------------------------------------------------------------===//
  29. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  30. #include "clang/AST/Attr.h"
  31. #include "clang/AST/DeclObjC.h"
  32. #include "clang/AST/StmtVisitor.h"
  33. #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
  34. #include "clang/StaticAnalyzer/Core/Checker.h"
  35. #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
  36. #include "llvm/ADT/DenseMap.h"
  37. #include "llvm/ADT/SetVector.h"
  38. #include "llvm/ADT/SmallString.h"
  39. using namespace clang;
  40. using namespace ento;
  41. namespace {
  42. struct ChecksFilter {
  43. /// Check for missing invalidation method declarations.
  44. DefaultBool check_MissingInvalidationMethod;
  45. /// Check that all ivars are invalidated.
  46. DefaultBool check_InstanceVariableInvalidation;
  47. CheckerNameRef checkName_MissingInvalidationMethod;
  48. CheckerNameRef checkName_InstanceVariableInvalidation;
  49. };
  50. class IvarInvalidationCheckerImpl {
  51. typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
  52. typedef llvm::DenseMap<const ObjCMethodDecl*,
  53. const ObjCIvarDecl*> MethToIvarMapTy;
  54. typedef llvm::DenseMap<const ObjCPropertyDecl*,
  55. const ObjCIvarDecl*> PropToIvarMapTy;
  56. typedef llvm::DenseMap<const ObjCIvarDecl*,
  57. const ObjCPropertyDecl*> IvarToPropMapTy;
  58. struct InvalidationInfo {
  59. /// Has the ivar been invalidated?
  60. bool IsInvalidated;
  61. /// The methods which can be used to invalidate the ivar.
  62. MethodSet InvalidationMethods;
  63. InvalidationInfo() : IsInvalidated(false) {}
  64. void addInvalidationMethod(const ObjCMethodDecl *MD) {
  65. InvalidationMethods.insert(MD);
  66. }
  67. bool needsInvalidation() const {
  68. return !InvalidationMethods.empty();
  69. }
  70. bool hasMethod(const ObjCMethodDecl *MD) {
  71. if (IsInvalidated)
  72. return true;
  73. for (MethodSet::iterator I = InvalidationMethods.begin(),
  74. E = InvalidationMethods.end(); I != E; ++I) {
  75. if (*I == MD) {
  76. IsInvalidated = true;
  77. return true;
  78. }
  79. }
  80. return false;
  81. }
  82. };
  83. typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
  84. /// Statement visitor, which walks the method body and flags the ivars
  85. /// referenced in it (either directly or via property).
  86. class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
  87. /// The set of Ivars which need to be invalidated.
  88. IvarSet &IVars;
  89. /// Flag is set as the result of a message send to another
  90. /// invalidation method.
  91. bool &CalledAnotherInvalidationMethod;
  92. /// Property setter to ivar mapping.
  93. const MethToIvarMapTy &PropertySetterToIvarMap;
  94. /// Property getter to ivar mapping.
  95. const MethToIvarMapTy &PropertyGetterToIvarMap;
  96. /// Property to ivar mapping.
  97. const PropToIvarMapTy &PropertyToIvarMap;
  98. /// The invalidation method being currently processed.
  99. const ObjCMethodDecl *InvalidationMethod;
  100. ASTContext &Ctx;
  101. /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
  102. const Expr *peel(const Expr *E) const;
  103. /// Does this expression represent zero: '0'?
  104. bool isZero(const Expr *E) const;
  105. /// Mark the given ivar as invalidated.
  106. void markInvalidated(const ObjCIvarDecl *Iv);
  107. /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
  108. /// invalidated.
  109. void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
  110. /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
  111. /// it as invalidated.
  112. void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
  113. /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
  114. /// if yes, marks it as invalidated.
  115. void checkObjCMessageExpr(const ObjCMessageExpr *ME);
  116. /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
  117. void check(const Expr *E);
  118. public:
  119. MethodCrawler(IvarSet &InIVars,
  120. bool &InCalledAnotherInvalidationMethod,
  121. const MethToIvarMapTy &InPropertySetterToIvarMap,
  122. const MethToIvarMapTy &InPropertyGetterToIvarMap,
  123. const PropToIvarMapTy &InPropertyToIvarMap,
  124. ASTContext &InCtx)
  125. : IVars(InIVars),
  126. CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
  127. PropertySetterToIvarMap(InPropertySetterToIvarMap),
  128. PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
  129. PropertyToIvarMap(InPropertyToIvarMap),
  130. InvalidationMethod(nullptr),
  131. Ctx(InCtx) {}
  132. void VisitStmt(const Stmt *S) { VisitChildren(S); }
  133. void VisitBinaryOperator(const BinaryOperator *BO);
  134. void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
  135. void VisitChildren(const Stmt *S) {
  136. for (const auto *Child : S->children()) {
  137. if (Child)
  138. this->Visit(Child);
  139. if (CalledAnotherInvalidationMethod)
  140. return;
  141. }
  142. }
  143. };
  144. /// Check if the any of the methods inside the interface are annotated with
  145. /// the invalidation annotation, update the IvarInfo accordingly.
  146. /// \param LookForPartial is set when we are searching for partial
  147. /// invalidators.
  148. static void containsInvalidationMethod(const ObjCContainerDecl *D,
  149. InvalidationInfo &Out,
  150. bool LookForPartial);
  151. /// Check if ivar should be tracked and add to TrackedIvars if positive.
  152. /// Returns true if ivar should be tracked.
  153. static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
  154. const ObjCIvarDecl **FirstIvarDecl);
  155. /// Given the property declaration, and the list of tracked ivars, finds
  156. /// the ivar backing the property when possible. Returns '0' when no such
  157. /// ivar could be found.
  158. static const ObjCIvarDecl *findPropertyBackingIvar(
  159. const ObjCPropertyDecl *Prop,
  160. const ObjCInterfaceDecl *InterfaceD,
  161. IvarSet &TrackedIvars,
  162. const ObjCIvarDecl **FirstIvarDecl);
  163. /// Print ivar name or the property if the given ivar backs a property.
  164. static void printIvar(llvm::raw_svector_ostream &os,
  165. const ObjCIvarDecl *IvarDecl,
  166. const IvarToPropMapTy &IvarToPopertyMap);
  167. void reportNoInvalidationMethod(CheckerNameRef CheckName,
  168. const ObjCIvarDecl *FirstIvarDecl,
  169. const IvarToPropMapTy &IvarToPopertyMap,
  170. const ObjCInterfaceDecl *InterfaceD,
  171. bool MissingDeclaration) const;
  172. void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
  173. const IvarToPropMapTy &IvarToPopertyMap,
  174. const ObjCMethodDecl *MethodD) const;
  175. AnalysisManager& Mgr;
  176. BugReporter &BR;
  177. /// Filter on the checks performed.
  178. const ChecksFilter &Filter;
  179. public:
  180. IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
  181. BugReporter &InBR,
  182. const ChecksFilter &InFilter) :
  183. Mgr (InMgr), BR(InBR), Filter(InFilter) {}
  184. void visit(const ObjCImplementationDecl *D) const;
  185. };
  186. static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
  187. for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) {
  188. if (!LookForPartial &&
  189. Ann->getAnnotation() == "objc_instance_variable_invalidator")
  190. return true;
  191. if (LookForPartial &&
  192. Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
  193. return true;
  194. }
  195. return false;
  196. }
  197. void IvarInvalidationCheckerImpl::containsInvalidationMethod(
  198. const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
  199. if (!D)
  200. return;
  201. assert(!isa<ObjCImplementationDecl>(D));
  202. // TODO: Cache the results.
  203. // Check all methods.
  204. for (const auto *MDI : D->methods())
  205. if (isInvalidationMethod(MDI, Partial))
  206. OutInfo.addInvalidationMethod(
  207. cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
  208. // If interface, check all parent protocols and super.
  209. if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
  210. // Visit all protocols.
  211. for (const auto *I : InterfD->protocols())
  212. containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
  213. // Visit all categories in case the invalidation method is declared in
  214. // a category.
  215. for (const auto *Ext : InterfD->visible_extensions())
  216. containsInvalidationMethod(Ext, OutInfo, Partial);
  217. containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
  218. return;
  219. }
  220. // If protocol, check all parent protocols.
  221. if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
  222. for (const auto *I : ProtD->protocols()) {
  223. containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
  224. }
  225. return;
  226. }
  227. }
  228. bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv,
  229. IvarSet &TrackedIvars,
  230. const ObjCIvarDecl **FirstIvarDecl) {
  231. QualType IvQTy = Iv->getType();
  232. const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
  233. if (!IvTy)
  234. return false;
  235. const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
  236. InvalidationInfo Info;
  237. containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
  238. if (Info.needsInvalidation()) {
  239. const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
  240. TrackedIvars[I] = Info;
  241. if (!*FirstIvarDecl)
  242. *FirstIvarDecl = I;
  243. return true;
  244. }
  245. return false;
  246. }
  247. const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
  248. const ObjCPropertyDecl *Prop,
  249. const ObjCInterfaceDecl *InterfaceD,
  250. IvarSet &TrackedIvars,
  251. const ObjCIvarDecl **FirstIvarDecl) {
  252. const ObjCIvarDecl *IvarD = nullptr;
  253. // Lookup for the synthesized case.
  254. IvarD = Prop->getPropertyIvarDecl();
  255. // We only track the ivars/properties that are defined in the current
  256. // class (not the parent).
  257. if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
  258. if (TrackedIvars.count(IvarD)) {
  259. return IvarD;
  260. }
  261. // If the ivar is synthesized we still want to track it.
  262. if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
  263. return IvarD;
  264. }
  265. // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
  266. StringRef PropName = Prop->getIdentifier()->getName();
  267. for (IvarSet::const_iterator I = TrackedIvars.begin(),
  268. E = TrackedIvars.end(); I != E; ++I) {
  269. const ObjCIvarDecl *Iv = I->first;
  270. StringRef IvarName = Iv->getName();
  271. if (IvarName == PropName)
  272. return Iv;
  273. SmallString<128> PropNameWithUnderscore;
  274. {
  275. llvm::raw_svector_ostream os(PropNameWithUnderscore);
  276. os << '_' << PropName;
  277. }
  278. if (IvarName == PropNameWithUnderscore)
  279. return Iv;
  280. }
  281. // Note, this is a possible source of false positives. We could look at the
  282. // getter implementation to find the ivar when its name is not derived from
  283. // the property name.
  284. return nullptr;
  285. }
  286. void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
  287. const ObjCIvarDecl *IvarDecl,
  288. const IvarToPropMapTy &IvarToPopertyMap) {
  289. if (IvarDecl->getSynthesize()) {
  290. const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
  291. assert(PD &&"Do we synthesize ivars for something other than properties?");
  292. os << "Property "<< PD->getName() << " ";
  293. } else {
  294. os << "Instance variable "<< IvarDecl->getName() << " ";
  295. }
  296. }
  297. // Check that the invalidatable interfaces with ivars/properties implement the
  298. // invalidation methods.
  299. void IvarInvalidationCheckerImpl::
  300. visit(const ObjCImplementationDecl *ImplD) const {
  301. // Collect all ivars that need cleanup.
  302. IvarSet Ivars;
  303. // Record the first Ivar needing invalidation; used in reporting when only
  304. // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
  305. // deterministic output.
  306. const ObjCIvarDecl *FirstIvarDecl = nullptr;
  307. const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
  308. // Collect ivars declared in this class, its extensions and its implementation
  309. ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
  310. for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
  311. Iv= Iv->getNextIvar())
  312. trackIvar(Iv, Ivars, &FirstIvarDecl);
  313. // Construct Property/Property Accessor to Ivar maps to assist checking if an
  314. // ivar which is backing a property has been reset.
  315. MethToIvarMapTy PropSetterToIvarMap;
  316. MethToIvarMapTy PropGetterToIvarMap;
  317. PropToIvarMapTy PropertyToIvarMap;
  318. IvarToPropMapTy IvarToPopertyMap;
  319. ObjCInterfaceDecl::PropertyMap PropMap;
  320. ObjCInterfaceDecl::PropertyDeclOrder PropOrder;
  321. InterfaceD->collectPropertiesToImplement(PropMap, PropOrder);
  322. for (ObjCInterfaceDecl::PropertyMap::iterator
  323. I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
  324. const ObjCPropertyDecl *PD = I->second;
  325. if (PD->isClassProperty())
  326. continue;
  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 = SetterD->getCanonicalDecl();
  339. PropSetterToIvarMap[SetterD] = ID;
  340. }
  341. const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
  342. if (GetterD) {
  343. GetterD = 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. CheckerNameRef 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 || I->second.hasMethod(InvalidationMethod))
  498. IVars.erase(I);
  499. }
  500. }
  501. const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
  502. E = E->IgnoreParenCasts();
  503. if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
  504. E = POE->getSyntacticForm()->IgnoreParenCasts();
  505. if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
  506. E = OVE->getSourceExpr()->IgnoreParenCasts();
  507. return E;
  508. }
  509. void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
  510. const ObjCIvarRefExpr *IvarRef) {
  511. if (const Decl *D = IvarRef->getDecl())
  512. markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
  513. }
  514. void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
  515. const ObjCMessageExpr *ME) {
  516. const ObjCMethodDecl *MD = ME->getMethodDecl();
  517. if (MD) {
  518. MD = MD->getCanonicalDecl();
  519. MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
  520. if (IvI != PropertyGetterToIvarMap.end())
  521. markInvalidated(IvI->second);
  522. }
  523. }
  524. void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
  525. const ObjCPropertyRefExpr *PA) {
  526. if (PA->isExplicitProperty()) {
  527. const ObjCPropertyDecl *PD = PA->getExplicitProperty();
  528. if (PD) {
  529. PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
  530. PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
  531. if (IvI != PropertyToIvarMap.end())
  532. markInvalidated(IvI->second);
  533. return;
  534. }
  535. }
  536. if (PA->isImplicitProperty()) {
  537. const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
  538. if (MD) {
  539. MD = MD->getCanonicalDecl();
  540. MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
  541. if (IvI != PropertyGetterToIvarMap.end())
  542. markInvalidated(IvI->second);
  543. return;
  544. }
  545. }
  546. }
  547. bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
  548. E = peel(E);
  549. return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
  550. != Expr::NPCK_NotNull);
  551. }
  552. void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
  553. E = peel(E);
  554. if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
  555. checkObjCIvarRefExpr(IvarRef);
  556. return;
  557. }
  558. if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
  559. checkObjCPropertyRefExpr(PropRef);
  560. return;
  561. }
  562. if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
  563. checkObjCMessageExpr(MsgExpr);
  564. return;
  565. }
  566. }
  567. void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
  568. const BinaryOperator *BO) {
  569. VisitStmt(BO);
  570. // Do we assign/compare against zero? If yes, check the variable we are
  571. // assigning to.
  572. BinaryOperatorKind Opcode = BO->getOpcode();
  573. if (Opcode != BO_Assign &&
  574. Opcode != BO_EQ &&
  575. Opcode != BO_NE)
  576. return;
  577. if (isZero(BO->getRHS())) {
  578. check(BO->getLHS());
  579. return;
  580. }
  581. if (Opcode != BO_Assign && isZero(BO->getLHS())) {
  582. check(BO->getRHS());
  583. return;
  584. }
  585. }
  586. void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
  587. const ObjCMessageExpr *ME) {
  588. const ObjCMethodDecl *MD = ME->getMethodDecl();
  589. const Expr *Receiver = ME->getInstanceReceiver();
  590. // Stop if we are calling '[self invalidate]'.
  591. if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false))
  592. if (Receiver->isObjCSelfExpr()) {
  593. CalledAnotherInvalidationMethod = true;
  594. return;
  595. }
  596. // Check if we call a setter and set the property to 'nil'.
  597. if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
  598. MD = MD->getCanonicalDecl();
  599. MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
  600. if (IvI != PropertySetterToIvarMap.end()) {
  601. markInvalidated(IvI->second);
  602. return;
  603. }
  604. }
  605. // Check if we call the 'invalidation' routine on the ivar.
  606. if (Receiver) {
  607. InvalidationMethod = MD;
  608. check(Receiver->IgnoreParenCasts());
  609. InvalidationMethod = nullptr;
  610. }
  611. VisitStmt(ME);
  612. }
  613. } // end anonymous namespace
  614. // Register the checkers.
  615. namespace {
  616. class IvarInvalidationChecker :
  617. public Checker<check::ASTDecl<ObjCImplementationDecl> > {
  618. public:
  619. ChecksFilter Filter;
  620. public:
  621. void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
  622. BugReporter &BR) const {
  623. IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
  624. Walker.visit(D);
  625. }
  626. };
  627. } // end anonymous namespace
  628. void ento::registerIvarInvalidationModeling(CheckerManager &mgr) {
  629. mgr.registerChecker<IvarInvalidationChecker>();
  630. }
  631. bool ento::shouldRegisterIvarInvalidationModeling(const CheckerManager &mgr) {
  632. return true;
  633. }
  634. #define REGISTER_CHECKER(name) \
  635. void ento::register##name(CheckerManager &mgr) { \
  636. IvarInvalidationChecker *checker = \
  637. mgr.getChecker<IvarInvalidationChecker>(); \
  638. checker->Filter.check_##name = true; \
  639. checker->Filter.checkName_##name = mgr.getCurrentCheckerName(); \
  640. } \
  641. \
  642. bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }
  643. REGISTER_CHECKER(InstanceVariableInvalidation)
  644. REGISTER_CHECKER(MissingInvalidationMethod)