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