clang  3.7.0
IvarInvalidationChecker.cpp
Go to the documentation of this file.
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 
31 #include "ClangSACheckers.h"
32 #include "clang/AST/Attr.h"
33 #include "clang/AST/DeclObjC.h"
34 #include "clang/AST/StmtVisitor.h"
38 #include "llvm/ADT/DenseMap.h"
39 #include "llvm/ADT/SetVector.h"
40 #include "llvm/ADT/SmallString.h"
41 
42 using namespace clang;
43 using namespace ento;
44 
45 namespace {
46 
47 struct ChecksFilter {
48  /// Check for missing invalidation method declarations.
49  DefaultBool check_MissingInvalidationMethod;
50  /// Check that all ivars are invalidated.
51  DefaultBool check_InstanceVariableInvalidation;
52 
53  CheckName checkName_MissingInvalidationMethod;
54  CheckName checkName_InstanceVariableInvalidation;
55 };
56 
57 class IvarInvalidationCheckerImpl {
58 
60  typedef llvm::DenseMap<const ObjCMethodDecl*,
61  const ObjCIvarDecl*> MethToIvarMapTy;
62  typedef llvm::DenseMap<const ObjCPropertyDecl*,
63  const ObjCIvarDecl*> PropToIvarMapTy;
64  typedef llvm::DenseMap<const ObjCIvarDecl*,
65  const ObjCPropertyDecl*> IvarToPropMapTy;
66 
67 
68  struct InvalidationInfo {
69  /// Has the ivar been invalidated?
70  bool IsInvalidated;
71 
72  /// The methods which can be used to invalidate the ivar.
73  MethodSet InvalidationMethods;
74 
75  InvalidationInfo() : IsInvalidated(false) {}
76  void addInvalidationMethod(const ObjCMethodDecl *MD) {
77  InvalidationMethods.insert(MD);
78  }
79 
80  bool needsInvalidation() const {
81  return !InvalidationMethods.empty();
82  }
83 
84  bool hasMethod(const ObjCMethodDecl *MD) {
85  if (IsInvalidated)
86  return true;
87  for (MethodSet::iterator I = InvalidationMethods.begin(),
88  E = InvalidationMethods.end(); I != E; ++I) {
89  if (*I == MD) {
90  IsInvalidated = true;
91  return true;
92  }
93  }
94  return false;
95  }
96  };
97 
98  typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
99 
100  /// Statement visitor, which walks the method body and flags the ivars
101  /// referenced in it (either directly or via property).
102  class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
103  /// The set of Ivars which need to be invalidated.
104  IvarSet &IVars;
105 
106  /// Flag is set as the result of a message send to another
107  /// invalidation method.
108  bool &CalledAnotherInvalidationMethod;
109 
110  /// Property setter to ivar mapping.
111  const MethToIvarMapTy &PropertySetterToIvarMap;
112 
113  /// Property getter to ivar mapping.
114  const MethToIvarMapTy &PropertyGetterToIvarMap;
115 
116  /// Property to ivar mapping.
117  const PropToIvarMapTy &PropertyToIvarMap;
118 
119  /// The invalidation method being currently processed.
120  const ObjCMethodDecl *InvalidationMethod;
121 
122  ASTContext &Ctx;
123 
124  /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
125  const Expr *peel(const Expr *E) const;
126 
127  /// Does this expression represent zero: '0'?
128  bool isZero(const Expr *E) const;
129 
130  /// Mark the given ivar as invalidated.
131  void markInvalidated(const ObjCIvarDecl *Iv);
132 
133  /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
134  /// invalidated.
135  void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
136 
137  /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
138  /// it as invalidated.
139  void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
140 
141  /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
142  /// if yes, marks it as invalidated.
143  void checkObjCMessageExpr(const ObjCMessageExpr *ME);
144 
145  /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
146  void check(const Expr *E);
147 
148  public:
149  MethodCrawler(IvarSet &InIVars,
150  bool &InCalledAnotherInvalidationMethod,
151  const MethToIvarMapTy &InPropertySetterToIvarMap,
152  const MethToIvarMapTy &InPropertyGetterToIvarMap,
153  const PropToIvarMapTy &InPropertyToIvarMap,
154  ASTContext &InCtx)
155  : IVars(InIVars),
156  CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
157  PropertySetterToIvarMap(InPropertySetterToIvarMap),
158  PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
159  PropertyToIvarMap(InPropertyToIvarMap),
160  InvalidationMethod(nullptr),
161  Ctx(InCtx) {}
162 
163  void VisitStmt(const Stmt *S) { VisitChildren(S); }
164 
165  void VisitBinaryOperator(const BinaryOperator *BO);
166 
167  void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
168 
169  void VisitChildren(const Stmt *S) {
170  for (const Stmt *Child : S->children()) {
171  if (Child)
172  this->Visit(Child);
173  if (CalledAnotherInvalidationMethod)
174  return;
175  }
176  }
177  };
178 
179  /// Check if the any of the methods inside the interface are annotated with
180  /// the invalidation annotation, update the IvarInfo accordingly.
181  /// \param LookForPartial is set when we are searching for partial
182  /// invalidators.
183  static void containsInvalidationMethod(const ObjCContainerDecl *D,
184  InvalidationInfo &Out,
185  bool LookForPartial);
186 
187  /// Check if ivar should be tracked and add to TrackedIvars if positive.
188  /// Returns true if ivar should be tracked.
189  static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
190  const ObjCIvarDecl **FirstIvarDecl);
191 
192  /// Given the property declaration, and the list of tracked ivars, finds
193  /// the ivar backing the property when possible. Returns '0' when no such
194  /// ivar could be found.
195  static const ObjCIvarDecl *findPropertyBackingIvar(
196  const ObjCPropertyDecl *Prop,
197  const ObjCInterfaceDecl *InterfaceD,
198  IvarSet &TrackedIvars,
199  const ObjCIvarDecl **FirstIvarDecl);
200 
201  /// Print ivar name or the property if the given ivar backs a property.
202  static void printIvar(llvm::raw_svector_ostream &os,
203  const ObjCIvarDecl *IvarDecl,
204  const IvarToPropMapTy &IvarToPopertyMap);
205 
206  void reportNoInvalidationMethod(CheckName CheckName,
207  const ObjCIvarDecl *FirstIvarDecl,
208  const IvarToPropMapTy &IvarToPopertyMap,
209  const ObjCInterfaceDecl *InterfaceD,
210  bool MissingDeclaration) const;
211  void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
212  const IvarToPropMapTy &IvarToPopertyMap,
213  const ObjCMethodDecl *MethodD) const;
214 
215  AnalysisManager& Mgr;
216  BugReporter &BR;
217  /// Filter on the checks performed.
218  const ChecksFilter &Filter;
219 
220 public:
221  IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
222  BugReporter &InBR,
223  const ChecksFilter &InFilter) :
224  Mgr (InMgr), BR(InBR), Filter(InFilter) {}
225 
226  void visit(const ObjCImplementationDecl *D) const;
227 };
228 
229 static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
230  for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) {
231  if (!LookForPartial &&
232  Ann->getAnnotation() == "objc_instance_variable_invalidator")
233  return true;
234  if (LookForPartial &&
235  Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
236  return true;
237  }
238  return false;
239 }
240 
241 void IvarInvalidationCheckerImpl::containsInvalidationMethod(
242  const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
243 
244  if (!D)
245  return;
246 
247  assert(!isa<ObjCImplementationDecl>(D));
248  // TODO: Cache the results.
249 
250  // Check all methods.
251  for (const auto *MDI : D->methods())
252  if (isInvalidationMethod(MDI, Partial))
253  OutInfo.addInvalidationMethod(
254  cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
255 
256  // If interface, check all parent protocols and super.
257  if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
258 
259  // Visit all protocols.
260  for (const auto *I : InterfD->protocols())
261  containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
262 
263  // Visit all categories in case the invalidation method is declared in
264  // a category.
265  for (const auto *Ext : InterfD->visible_extensions())
266  containsInvalidationMethod(Ext, OutInfo, Partial);
267 
268  containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
269  return;
270  }
271 
272  // If protocol, check all parent protocols.
273  if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
274  for (const auto *I : ProtD->protocols()) {
275  containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
276  }
277  return;
278  }
279 
280  return;
281 }
282 
283 bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv,
284  IvarSet &TrackedIvars,
285  const ObjCIvarDecl **FirstIvarDecl) {
286  QualType IvQTy = Iv->getType();
287  const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
288  if (!IvTy)
289  return false;
290  const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
291 
292  InvalidationInfo Info;
293  containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
294  if (Info.needsInvalidation()) {
295  const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
296  TrackedIvars[I] = Info;
297  if (!*FirstIvarDecl)
298  *FirstIvarDecl = I;
299  return true;
300  }
301  return false;
302 }
303 
304 const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
305  const ObjCPropertyDecl *Prop,
306  const ObjCInterfaceDecl *InterfaceD,
307  IvarSet &TrackedIvars,
308  const ObjCIvarDecl **FirstIvarDecl) {
309  const ObjCIvarDecl *IvarD = nullptr;
310 
311  // Lookup for the synthesized case.
312  IvarD = Prop->getPropertyIvarDecl();
313  // We only track the ivars/properties that are defined in the current
314  // class (not the parent).
315  if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
316  if (TrackedIvars.count(IvarD)) {
317  return IvarD;
318  }
319  // If the ivar is synthesized we still want to track it.
320  if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
321  return IvarD;
322  }
323 
324  // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
325  StringRef PropName = Prop->getIdentifier()->getName();
326  for (IvarSet::const_iterator I = TrackedIvars.begin(),
327  E = TrackedIvars.end(); I != E; ++I) {
328  const ObjCIvarDecl *Iv = I->first;
329  StringRef IvarName = Iv->getName();
330 
331  if (IvarName == PropName)
332  return Iv;
333 
334  SmallString<128> PropNameWithUnderscore;
335  {
336  llvm::raw_svector_ostream os(PropNameWithUnderscore);
337  os << '_' << PropName;
338  }
339  if (IvarName == PropNameWithUnderscore)
340  return Iv;
341  }
342 
343  // Note, this is a possible source of false positives. We could look at the
344  // getter implementation to find the ivar when its name is not derived from
345  // the property name.
346  return nullptr;
347 }
348 
349 void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
350  const ObjCIvarDecl *IvarDecl,
351  const IvarToPropMapTy &IvarToPopertyMap) {
352  if (IvarDecl->getSynthesize()) {
353  const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
354  assert(PD &&"Do we synthesize ivars for something other than properties?");
355  os << "Property "<< PD->getName() << " ";
356  } else {
357  os << "Instance variable "<< IvarDecl->getName() << " ";
358  }
359 }
360 
361 // Check that the invalidatable interfaces with ivars/properties implement the
362 // invalidation methods.
363 void IvarInvalidationCheckerImpl::
364 visit(const ObjCImplementationDecl *ImplD) const {
365  // Collect all ivars that need cleanup.
366  IvarSet Ivars;
367  // Record the first Ivar needing invalidation; used in reporting when only
368  // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
369  // deterministic output.
370  const ObjCIvarDecl *FirstIvarDecl = nullptr;
371  const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
372 
373  // Collect ivars declared in this class, its extensions and its implementation
374  ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
375  for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
376  Iv= Iv->getNextIvar())
377  trackIvar(Iv, Ivars, &FirstIvarDecl);
378 
379  // Construct Property/Property Accessor to Ivar maps to assist checking if an
380  // ivar which is backing a property has been reset.
381  MethToIvarMapTy PropSetterToIvarMap;
382  MethToIvarMapTy PropGetterToIvarMap;
383  PropToIvarMapTy PropertyToIvarMap;
384  IvarToPropMapTy IvarToPopertyMap;
385 
388  InterfaceD->collectPropertiesToImplement(PropMap, PropOrder);
389 
390  for (ObjCInterfaceDecl::PropertyMap::iterator
391  I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
392  const ObjCPropertyDecl *PD = I->second;
393 
394  const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
395  &FirstIvarDecl);
396  if (!ID)
397  continue;
398 
399  // Store the mappings.
400  PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
401  PropertyToIvarMap[PD] = ID;
402  IvarToPopertyMap[ID] = PD;
403 
404  // Find the setter and the getter.
405  const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
406  if (SetterD) {
407  SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
408  PropSetterToIvarMap[SetterD] = ID;
409  }
410 
411  const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
412  if (GetterD) {
413  GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl());
414  PropGetterToIvarMap[GetterD] = ID;
415  }
416  }
417 
418  // If no ivars need invalidation, there is nothing to check here.
419  if (Ivars.empty())
420  return;
421 
422  // Find all partial invalidation methods.
423  InvalidationInfo PartialInfo;
424  containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true);
425 
426  // Remove ivars invalidated by the partial invalidation methods. They do not
427  // need to be invalidated in the regular invalidation methods.
428  bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false;
429  for (MethodSet::iterator
430  I = PartialInfo.InvalidationMethods.begin(),
431  E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
432  const ObjCMethodDecl *InterfD = *I;
433 
434  // Get the corresponding method in the @implementation.
435  const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
436  InterfD->isInstanceMethod());
437  if (D && D->hasBody()) {
438  AtImplementationContainsAtLeastOnePartialInvalidationMethod = true;
439 
440  bool CalledAnotherInvalidationMethod = false;
441  // The MethodCrowler is going to remove the invalidated ivars.
442  MethodCrawler(Ivars,
443  CalledAnotherInvalidationMethod,
444  PropSetterToIvarMap,
445  PropGetterToIvarMap,
446  PropertyToIvarMap,
447  BR.getContext()).VisitStmt(D->getBody());
448  // If another invalidation method was called, trust that full invalidation
449  // has occurred.
450  if (CalledAnotherInvalidationMethod)
451  Ivars.clear();
452  }
453  }
454 
455  // If all ivars have been invalidated by partial invalidators, there is
456  // nothing to check here.
457  if (Ivars.empty())
458  return;
459 
460  // Find all invalidation methods in this @interface declaration and parents.
461  InvalidationInfo Info;
462  containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
463 
464  // Report an error in case none of the invalidation methods are declared.
465  if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
466  if (Filter.check_MissingInvalidationMethod)
467  reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
468  FirstIvarDecl, IvarToPopertyMap, InterfaceD,
469  /*MissingDeclaration*/ true);
470  // If there are no invalidation methods, there is no ivar validation work
471  // to be done.
472  return;
473  }
474 
475  // Only check if Ivars are invalidated when InstanceVariableInvalidation
476  // has been requested.
477  if (!Filter.check_InstanceVariableInvalidation)
478  return;
479 
480  // Check that all ivars are invalidated by the invalidation methods.
481  bool AtImplementationContainsAtLeastOneInvalidationMethod = false;
482  for (MethodSet::iterator I = Info.InvalidationMethods.begin(),
483  E = Info.InvalidationMethods.end(); I != E; ++I) {
484  const ObjCMethodDecl *InterfD = *I;
485 
486  // Get the corresponding method in the @implementation.
487  const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
488  InterfD->isInstanceMethod());
489  if (D && D->hasBody()) {
490  AtImplementationContainsAtLeastOneInvalidationMethod = true;
491 
492  // Get a copy of ivars needing invalidation.
493  IvarSet IvarsI = Ivars;
494 
495  bool CalledAnotherInvalidationMethod = false;
496  MethodCrawler(IvarsI,
497  CalledAnotherInvalidationMethod,
498  PropSetterToIvarMap,
499  PropGetterToIvarMap,
500  PropertyToIvarMap,
501  BR.getContext()).VisitStmt(D->getBody());
502  // If another invalidation method was called, trust that full invalidation
503  // has occurred.
504  if (CalledAnotherInvalidationMethod)
505  continue;
506 
507  // Warn on the ivars that were not invalidated by the method.
508  for (IvarSet::const_iterator
509  I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I)
510  reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D);
511  }
512  }
513 
514  // Report an error in case none of the invalidation methods are implemented.
515  if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
516  if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
517  // Warn on the ivars that were not invalidated by the prrtial
518  // invalidation methods.
519  for (IvarSet::const_iterator
520  I = Ivars.begin(), E = Ivars.end(); I != E; ++I)
521  reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, nullptr);
522  } else {
523  // Otherwise, no invalidation methods were implemented.
524  reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
525  FirstIvarDecl, IvarToPopertyMap, InterfaceD,
526  /*MissingDeclaration*/ false);
527  }
528  }
529 }
530 
531 void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
532  CheckName CheckName, const ObjCIvarDecl *FirstIvarDecl,
533  const IvarToPropMapTy &IvarToPopertyMap,
534  const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const {
535  SmallString<128> sbuf;
536  llvm::raw_svector_ostream os(sbuf);
537  assert(FirstIvarDecl);
538  printIvar(os, FirstIvarDecl, IvarToPopertyMap);
539  os << "needs to be invalidated; ";
540  if (MissingDeclaration)
541  os << "no invalidation method is declared for ";
542  else
543  os << "no invalidation method is defined in the @implementation for ";
544  os << InterfaceD->getName();
545 
546  PathDiagnosticLocation IvarDecLocation =
547  PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
548 
549  BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation",
551  IvarDecLocation);
552 }
553 
554 void IvarInvalidationCheckerImpl::
555 reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
556  const IvarToPropMapTy &IvarToPopertyMap,
557  const ObjCMethodDecl *MethodD) const {
558  SmallString<128> sbuf;
559  llvm::raw_svector_ostream os(sbuf);
560  printIvar(os, IvarD, IvarToPopertyMap);
561  os << "needs to be invalidated or set to nil";
562  if (MethodD) {
563  PathDiagnosticLocation MethodDecLocation =
565  BR.getSourceManager(),
566  Mgr.getAnalysisDeclContext(MethodD));
567  BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
568  "Incomplete invalidation",
570  MethodDecLocation);
571  } else {
572  BR.EmitBasicReport(
573  IvarD, Filter.checkName_InstanceVariableInvalidation,
574  "Incomplete invalidation", categories::CoreFoundationObjectiveC,
575  os.str(),
576  PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager()));
577  }
578 }
579 
580 void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
581  const ObjCIvarDecl *Iv) {
582  IvarSet::iterator I = IVars.find(Iv);
583  if (I != IVars.end()) {
584  // If InvalidationMethod is present, we are processing the message send and
585  // should ensure we are invalidating with the appropriate method,
586  // otherwise, we are processing setting to 'nil'.
587  if (!InvalidationMethod ||
588  (InvalidationMethod && I->second.hasMethod(InvalidationMethod)))
589  IVars.erase(I);
590  }
591 }
592 
593 const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
594  E = E->IgnoreParenCasts();
595  if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
596  E = POE->getSyntacticForm()->IgnoreParenCasts();
597  if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
598  E = OVE->getSourceExpr()->IgnoreParenCasts();
599  return E;
600 }
601 
602 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
603  const ObjCIvarRefExpr *IvarRef) {
604  if (const Decl *D = IvarRef->getDecl())
605  markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
606 }
607 
608 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
609  const ObjCMessageExpr *ME) {
610  const ObjCMethodDecl *MD = ME->getMethodDecl();
611  if (MD) {
612  MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
613  MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
614  if (IvI != PropertyGetterToIvarMap.end())
615  markInvalidated(IvI->second);
616  }
617 }
618 
619 void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
620  const ObjCPropertyRefExpr *PA) {
621 
622  if (PA->isExplicitProperty()) {
623  const ObjCPropertyDecl *PD = PA->getExplicitProperty();
624  if (PD) {
625  PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
626  PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
627  if (IvI != PropertyToIvarMap.end())
628  markInvalidated(IvI->second);
629  return;
630  }
631  }
632 
633  if (PA->isImplicitProperty()) {
634  const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
635  if (MD) {
636  MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
637  MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
638  if (IvI != PropertyGetterToIvarMap.end())
639  markInvalidated(IvI->second);
640  return;
641  }
642  }
643 }
644 
645 bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
646  E = peel(E);
647 
649  != Expr::NPCK_NotNull);
650 }
651 
652 void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
653  E = peel(E);
654 
655  if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
656  checkObjCIvarRefExpr(IvarRef);
657  return;
658  }
659 
660  if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
661  checkObjCPropertyRefExpr(PropRef);
662  return;
663  }
664 
665  if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
666  checkObjCMessageExpr(MsgExpr);
667  return;
668  }
669 }
670 
671 void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
672  const BinaryOperator *BO) {
673  VisitStmt(BO);
674 
675  // Do we assign/compare against zero? If yes, check the variable we are
676  // assigning to.
677  BinaryOperatorKind Opcode = BO->getOpcode();
678  if (Opcode != BO_Assign &&
679  Opcode != BO_EQ &&
680  Opcode != BO_NE)
681  return;
682 
683  if (isZero(BO->getRHS())) {
684  check(BO->getLHS());
685  return;
686  }
687 
688  if (Opcode != BO_Assign && isZero(BO->getLHS())) {
689  check(BO->getRHS());
690  return;
691  }
692 }
693 
694 void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
695  const ObjCMessageExpr *ME) {
696  const ObjCMethodDecl *MD = ME->getMethodDecl();
697  const Expr *Receiver = ME->getInstanceReceiver();
698 
699  // Stop if we are calling '[self invalidate]'.
700  if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false))
701  if (Receiver->isObjCSelfExpr()) {
702  CalledAnotherInvalidationMethod = true;
703  return;
704  }
705 
706  // Check if we call a setter and set the property to 'nil'.
707  if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
708  MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
709  MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
710  if (IvI != PropertySetterToIvarMap.end()) {
711  markInvalidated(IvI->second);
712  return;
713  }
714  }
715 
716  // Check if we call the 'invalidation' routine on the ivar.
717  if (Receiver) {
718  InvalidationMethod = MD;
719  check(Receiver->IgnoreParenCasts());
720  InvalidationMethod = nullptr;
721  }
722 
723  VisitStmt(ME);
724 }
725 }
726 
727 // Register the checkers.
728 namespace {
729 
730 class IvarInvalidationChecker :
731  public Checker<check::ASTDecl<ObjCImplementationDecl> > {
732 public:
733  ChecksFilter Filter;
734 public:
735  void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
736  BugReporter &BR) const {
737  IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
738  Walker.visit(D);
739  }
740 };
741 }
742 
743 #define REGISTER_CHECKER(name) \
744  void ento::register##name(CheckerManager &mgr) { \
745  IvarInvalidationChecker *checker = \
746  mgr.registerChecker<IvarInvalidationChecker>(); \
747  checker->Filter.check_##name = true; \
748  checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \
749  }
750 
751 REGISTER_CHECKER(InstanceVariableInvalidation)
752 REGISTER_CHECKER(MissingInvalidationMethod)
753 
const char *const CoreFoundationObjectiveC
StringRef getName() const
Definition: Decl.h:168
IdentifierInfo * getIdentifier() const
Definition: Decl.h:163
A helper class which wraps a boolean value set to false by default.
Definition: Checker.h:523
ObjCMethodDecl * getMethod(Selector Sel, bool isInstance, bool AllowHidden=false) const
Definition: DeclObjC.cpp:68
bool isExplicitProperty() const
Definition: ExprObjC.h:626
NullPointerConstantKind isNullPointerConstant(ASTContext &Ctx, NullPointerConstantValueDependence NPC) const
Definition: Expr.cpp:3225
FieldDecl * getCanonicalDecl() override
Retrieves the canonical declaration of this field.
Definition: Decl.h:2435
const ObjCInterfaceDecl * getContainingInterface() const
Return the class interface that this ivar is logically contained in; this is either the interface whe...
Definition: DeclObjC.cpp:1631
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:89
method_range methods() const
Definition: DeclObjC.h:727
Expr * getLHS() const
Definition: Expr.h:2964
void collectPropertiesToImplement(PropertyMap &PM, PropertyDeclOrder &PO) const override
Definition: DeclObjC.cpp:324
BinaryOperatorKind
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:2918
Expr * IgnoreParenCasts() LLVM_READONLY
Definition: Expr.cpp:2439
Represents an Objective-C protocol declaration.
Definition: DeclObjC.h:1731
#define REGISTER_CHECKER(name)
Represents an ObjC class declaration.
Definition: DeclObjC.h:851
llvm::DenseMap< IdentifierInfo *, ObjCPropertyDecl * > PropertyMap
Definition: DeclObjC.h:783
QualType getType() const
Definition: Decl.h:538
virtual Decl * getCanonicalDecl()
Retrieves the "canonical" declaration of the given declaration.
Definition: DeclBase.h:733
ObjCMethodDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition: DeclObjC.cpp:826
ObjCIvarDecl * getDecl()
Definition: ExprObjC.h:500
ID
Defines the set of possible language-specific address spaces.
Definition: AddressSpaces.h:27
const ObjCMethodDecl * getMethodDecl() const
Definition: ExprObjC.h:1248
bool isObjCSelfExpr() const
Check if this expression is the ObjC 'self' implicit parameter.
Definition: Expr.cpp:3353
StringRef getName() const
Return the actual identifier string.
bool isInstanceMethod() const
Definition: DeclObjC.h:419
An expression that sends a message to the given Objective-C object or class.
Definition: ExprObjC.h:858
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: ExprObjC.h:1286
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
#define false
Definition: stdbool.h:33
Expression is not a Null pointer constant.
Definition: Expr.h:635
Stmt * getBody() const override
Retrieve the body of this method, if it has one.
Definition: DeclObjC.cpp:731
bool getSynthesize() const
Definition: DeclObjC.h:1658
static PathDiagnosticLocation createEnd(const Stmt *S, const SourceManager &SM, const LocationOrAnalysisDeclContext LAC)
const ObjCInterfaceDecl * getClassInterface() const
Definition: DeclObjC.h:2093
Specifies that a value-dependent expression should be considered to never be a null pointer constant...
Definition: Expr.h:666
Represents one property declaration in an Objective-C interface.
Definition: DeclObjC.h:2424
llvm::SmallVector< ObjCPropertyDecl *, 8 > PropertyDeclOrder
Definition: DeclObjC.h:788
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
Definition: ExprObjC.h:1152
ObjCIvarDecl * getNextIvar()
Definition: DeclObjC.h:1645
bool isImplicitProperty() const
Definition: ExprObjC.h:625
ObjCIvarDecl * getPropertyIvarDecl() const
Definition: DeclObjC.h:2598
Selector getSelector() const
Definition: DeclObjC.h:328
ObjCMethodDecl * getGetterMethodDecl() const
Definition: DeclObjC.h:2581
const T * getAs() const
Definition: Type.h:5555
ObjCInterfaceDecl * getInterfaceDecl() const
Definition: Type.h:4835
ObjCMethodDecl * getSetterMethodDecl() const
Definition: DeclObjC.h:2584
bool hasBody() const override
Determine whether this method has a body.
Definition: DeclObjC.h:486
unsigned getNumArgs() const
Return the number of actual arguments in this message, not counting the receiver. ...
Definition: ExprObjC.h:1274
ObjCPropertyDecl * getExplicitProperty() const
Definition: ExprObjC.h:628
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:474
Opcode getOpcode() const
Definition: Expr.h:2961
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
Definition: DeclBase.h:470
Expr * getRHS() const
Definition: Expr.h:2966
ObjCIvarDecl * all_declared_ivar_begin()
Definition: DeclObjC.cpp:1433
ObjCMethodDecl * getImplicitPropertySetter() const
Definition: ExprObjC.h:638