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