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