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"
35#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
36#include "clang/StaticAnalyzer/Core/Checker.h"
37#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
38#include "llvm/ADT/DenseMap.h"
39#include "llvm/ADT/SetVector.h"
40#include "llvm/ADT/SmallString.h"
41
42using namespace clang;
43using namespace ento;
44
45namespace {
46
47struct 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
57class IvarInvalidationCheckerImpl {
58
59  typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
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 (Stmt::const_child_range I = S->children(); I; ++I) {
171        if (*I)
172          this->Visit(*I);
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
220public:
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
229static 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
241void 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
283bool 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
304const 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.str())
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
349void 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.
363void IvarInvalidationCheckerImpl::
364visit(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
386  ObjCInterfaceDecl::PropertyMap PropMap;
387  ObjCInterfaceDecl::PropertyDeclOrder PropOrder;
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
531void 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",
550                     categories::CoreFoundationObjectiveC, os.str(),
551                     IvarDecLocation);
552}
553
554void IvarInvalidationCheckerImpl::
555reportIvarNeedsInvalidation(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 =
564                           PathDiagnosticLocation::createEnd(MethodD->getBody(),
565                           BR.getSourceManager(),
566                           Mgr.getAnalysisDeclContext(MethodD));
567    BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
568                       "Incomplete invalidation",
569                       categories::CoreFoundationObjectiveC, os.str(),
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
580void 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
593const 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
602void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
603    const ObjCIvarRefExpr *IvarRef) {
604  if (const Decl *D = IvarRef->getDecl())
605    markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
606}
607
608void 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
619void 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
645bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
646  E = peel(E);
647
648  return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
649           != Expr::NPCK_NotNull);
650}
651
652void 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
671void 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
694void 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.
728namespace {
729
730class IvarInvalidationChecker :
731  public Checker<check::ASTDecl<ObjCImplementationDecl> > {
732public:
733  ChecksFilter Filter;
734public:
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
751REGISTER_CHECKER(InstanceVariableInvalidation)
752REGISTER_CHECKER(MissingInvalidationMethod)
753
754