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