blob: 6262cb8ef9ddea3e16708711a71097d5b0c1defc [file] [log] [blame]
Anna Zaks31f69cc2012-09-29 00:20:38 +00001//=- IvarInvalidationChecker.cpp - -*- C++ -------------------------------*-==//
Anna Zaks5bf5c2e2012-09-26 18:55:16 +00002//
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
Anna Zaks31f69cc2012-09-29 00:20:38 +000016// 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).
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000022//
Anna Zaks26db7db2013-02-08 23:55:43 +000023// 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//
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000029//===----------------------------------------------------------------------===//
30
31#include "ClangSACheckers.h"
Benjamin Kramer2fa67ef2012-12-01 15:09:41 +000032#include "clang/AST/Attr.h"
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000033#include "clang/AST/DeclObjC.h"
34#include "clang/AST/StmtVisitor.h"
Chandler Carruth55fc8732012-12-04 09:13:33 +000035#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
36#include "clang/StaticAnalyzer/Core/Checker.h"
37#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000038#include "llvm/ADT/DenseMap.h"
Anna Zaksb1fc6732013-01-10 20:59:51 +000039#include "llvm/ADT/SetVector.h"
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000040#include "llvm/ADT/SmallString.h"
41
42using namespace clang;
43using namespace ento;
44
45namespace {
Anna Zaks722cd9e2013-02-08 23:55:47 +000046
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
54class IvarInvalidationCheckerImpl :
Anna Zaksb1fc6732013-01-10 20:59:51 +000055 public Checker<check::ASTDecl<ObjCImplementationDecl> > {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000056
Anna Zaksb1fc6732013-01-10 20:59:51 +000057 typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000058 typedef llvm::DenseMap<const ObjCMethodDecl*,
59 const ObjCIvarDecl*> MethToIvarMapTy;
60 typedef llvm::DenseMap<const ObjCPropertyDecl*,
61 const ObjCIvarDecl*> PropToIvarMapTy;
Anna Zaks377945c2012-09-27 21:57:14 +000062 typedef llvm::DenseMap<const ObjCIvarDecl*,
63 const ObjCPropertyDecl*> IvarToPropMapTy;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000064
Anna Zaks31f69cc2012-09-29 00:20:38 +000065
Anna Zaksb1fc6732013-01-10 20:59:51 +000066 struct InvalidationInfo {
Anna Zaks31f69cc2012-09-29 00:20:38 +000067 /// Has the ivar been invalidated?
68 bool IsInvalidated;
69
70 /// The methods which can be used to invalidate the ivar.
71 MethodSet InvalidationMethods;
72
Anna Zaksb1fc6732013-01-10 20:59:51 +000073 InvalidationInfo() : IsInvalidated(false) {}
Anna Zaks31f69cc2012-09-29 00:20:38 +000074 void addInvalidationMethod(const ObjCMethodDecl *MD) {
75 InvalidationMethods.insert(MD);
76 }
77
78 bool needsInvalidation() const {
79 return !InvalidationMethods.empty();
80 }
81
Anna Zaks26db7db2013-02-08 23:55:43 +000082 bool hasMethod(const ObjCMethodDecl *MD) {
Anna Zaks31f69cc2012-09-29 00:20:38 +000083 if (IsInvalidated)
84 return true;
85 for (MethodSet::iterator I = InvalidationMethods.begin(),
86 E = InvalidationMethods.end(); I != E; ++I) {
87 if (*I == MD) {
88 IsInvalidated = true;
89 return true;
90 }
91 }
92 return false;
93 }
Anna Zaks31f69cc2012-09-29 00:20:38 +000094 };
95
Anna Zaksb1fc6732013-01-10 20:59:51 +000096 typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
Anna Zaks31f69cc2012-09-29 00:20:38 +000097
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000098 /// Statement visitor, which walks the method body and flags the ivars
99 /// referenced in it (either directly or via property).
100 class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000101 /// The set of Ivars which need to be invalidated.
102 IvarSet &IVars;
103
Anna Zaks31f69cc2012-09-29 00:20:38 +0000104 /// Flag is set as the result of a message send to another
105 /// invalidation method.
106 bool &CalledAnotherInvalidationMethod;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000107
Anna Zaks31f69cc2012-09-29 00:20:38 +0000108 /// Property setter to ivar mapping.
109 const MethToIvarMapTy &PropertySetterToIvarMap;
110
111 /// Property getter to ivar mapping.
112 const MethToIvarMapTy &PropertyGetterToIvarMap;
113
114 /// Property to ivar mapping.
115 const PropToIvarMapTy &PropertyToIvarMap;
116
117 /// The invalidation method being currently processed.
118 const ObjCMethodDecl *InvalidationMethod;
119
Anna Zaksb9733ac2012-10-01 20:33:58 +0000120 ASTContext &Ctx;
121
122 /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
Anna Zaks31f69cc2012-09-29 00:20:38 +0000123 const Expr *peel(const Expr *E) const;
124
125 /// Does this expression represent zero: '0'?
126 bool isZero(const Expr *E) const;
127
128 /// Mark the given ivar as invalidated.
129 void markInvalidated(const ObjCIvarDecl *Iv);
130
131 /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
132 /// invalidated.
133 void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
134
135 /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
136 /// it as invalidated.
137 void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
138
139 /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
140 /// if yes, marks it as invalidated.
141 void checkObjCMessageExpr(const ObjCMessageExpr *ME);
142
143 /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
144 void check(const Expr *E);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000145
146 public:
Anna Zaksbbff82f2012-10-01 20:34:04 +0000147 MethodCrawler(IvarSet &InIVars,
Anna Zaks31f69cc2012-09-29 00:20:38 +0000148 bool &InCalledAnotherInvalidationMethod,
149 const MethToIvarMapTy &InPropertySetterToIvarMap,
150 const MethToIvarMapTy &InPropertyGetterToIvarMap,
Anna Zaksb9733ac2012-10-01 20:33:58 +0000151 const PropToIvarMapTy &InPropertyToIvarMap,
152 ASTContext &InCtx)
Anna Zaksbbff82f2012-10-01 20:34:04 +0000153 : IVars(InIVars),
Anna Zaks31f69cc2012-09-29 00:20:38 +0000154 CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
155 PropertySetterToIvarMap(InPropertySetterToIvarMap),
156 PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
157 PropertyToIvarMap(InPropertyToIvarMap),
Anna Zaksb9733ac2012-10-01 20:33:58 +0000158 InvalidationMethod(0),
159 Ctx(InCtx) {}
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000160
161 void VisitStmt(const Stmt *S) { VisitChildren(S); }
162
Anna Zaks31f69cc2012-09-29 00:20:38 +0000163 void VisitBinaryOperator(const BinaryOperator *BO);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000164
165 void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
166
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000167 void VisitChildren(const Stmt *S) {
Anna Zaks31f69cc2012-09-29 00:20:38 +0000168 for (Stmt::const_child_range I = S->children(); I; ++I) {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000169 if (*I)
Anna Zaksb087bbf2012-09-27 19:45:08 +0000170 this->Visit(*I);
Anna Zaks31f69cc2012-09-29 00:20:38 +0000171 if (CalledAnotherInvalidationMethod)
172 return;
173 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000174 }
175 };
176
177 /// Check if the any of the methods inside the interface are annotated with
Anna Zaks31f69cc2012-09-29 00:20:38 +0000178 /// the invalidation annotation, update the IvarInfo accordingly.
Anna Zaks26db7db2013-02-08 23:55:43 +0000179 /// \param LookForPartial is set when we are searching for partial
180 /// invalidators.
Anna Zaks31f69cc2012-09-29 00:20:38 +0000181 static void containsInvalidationMethod(const ObjCContainerDecl *D,
Anna Zaks26db7db2013-02-08 23:55:43 +0000182 InvalidationInfo &Out,
183 bool LookForPartial);
Anna Zaks377945c2012-09-27 21:57:14 +0000184
185 /// Check if ivar should be tracked and add to TrackedIvars if positive.
186 /// Returns true if ivar should be tracked.
Anna Zaks664566c2013-01-10 22:44:16 +0000187 static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
188 const ObjCIvarDecl **FirstIvarDecl);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000189
190 /// Given the property declaration, and the list of tracked ivars, finds
191 /// the ivar backing the property when possible. Returns '0' when no such
192 /// ivar could be found.
193 static const ObjCIvarDecl *findPropertyBackingIvar(
194 const ObjCPropertyDecl *Prop,
195 const ObjCInterfaceDecl *InterfaceD,
Anna Zaks664566c2013-01-10 22:44:16 +0000196 IvarSet &TrackedIvars,
197 const ObjCIvarDecl **FirstIvarDecl);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000198
Anna Zaksb1fc6732013-01-10 20:59:51 +0000199 /// Print ivar name or the property if the given ivar backs a property.
200 static void printIvar(llvm::raw_svector_ostream &os,
201 const ObjCIvarDecl *IvarDecl,
Anna Zaks2b174c32013-02-08 23:55:45 +0000202 const IvarToPropMapTy &IvarToPopertyMap);
203
Anna Zaks722cd9e2013-02-08 23:55:47 +0000204 void reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
205 const IvarToPropMapTy &IvarToPopertyMap,
206 const ObjCInterfaceDecl *InterfaceD,
207 bool MissingDeclaration) const;
208 void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
209 const IvarToPropMapTy &IvarToPopertyMap,
210 const ObjCMethodDecl *MethodD) const;
Anna Zaks2b174c32013-02-08 23:55:45 +0000211
Anna Zaks722cd9e2013-02-08 23:55:47 +0000212 AnalysisManager& Mgr;
213 BugReporter &BR;
214 /// Filter on the checks performed.
215 const ChecksFilter &Filter;
Anna Zaks26db7db2013-02-08 23:55:43 +0000216
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000217public:
Anna Zaks722cd9e2013-02-08 23:55:47 +0000218 IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
219 BugReporter &InBR,
220 const ChecksFilter &InFilter) :
221 Mgr (InMgr), BR(InBR), Filter(InFilter) {}
222
223 void visit(const ObjCImplementationDecl *D) const;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000224};
225
Anna Zaks26db7db2013-02-08 23:55:43 +0000226static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
Anna Zaksb087bbf2012-09-27 19:45:08 +0000227 for (specific_attr_iterator<AnnotateAttr>
228 AI = M->specific_attr_begin<AnnotateAttr>(),
229 AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
230 const AnnotateAttr *Ann = *AI;
Anna Zaks26db7db2013-02-08 23:55:43 +0000231 if (!LookForPartial &&
232 Ann->getAnnotation() == "objc_instance_variable_invalidator")
233 return true;
234 if (LookForPartial &&
235 Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
Anna Zaksb087bbf2012-09-27 19:45:08 +0000236 return true;
237 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000238 return false;
239}
240
Anna Zaks722cd9e2013-02-08 23:55:47 +0000241void IvarInvalidationCheckerImpl::containsInvalidationMethod(
Anna Zaks26db7db2013-02-08 23:55:43 +0000242 const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000243
244 if (!D)
Anna Zaks31f69cc2012-09-29 00:20:38 +0000245 return;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000246
Anna Zaksb1fc6732013-01-10 20:59:51 +0000247 assert(!isa<ObjCImplementationDecl>(D));
248 // TODO: Cache the results.
249
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000250 // Check all methods.
251 for (ObjCContainerDecl::method_iterator
252 I = D->meth_begin(),
253 E = D->meth_end(); I != E; ++I) {
254 const ObjCMethodDecl *MDI = *I;
Anna Zaks26db7db2013-02-08 23:55:43 +0000255 if (isInvalidationMethod(MDI, Partial))
Anna Zaks31f69cc2012-09-29 00:20:38 +0000256 OutInfo.addInvalidationMethod(
257 cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000258 }
259
260 // If interface, check all parent protocols and super.
Anna Zaksae81e172013-01-11 03:52:37 +0000261 if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
262
263 // Visit all protocols.
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000264 for (ObjCInterfaceDecl::protocol_iterator
Anna Zaksae81e172013-01-11 03:52:37 +0000265 I = InterfD->protocol_begin(),
266 E = InterfD->protocol_end(); I != E; ++I) {
Anna Zaks26db7db2013-02-08 23:55:43 +0000267 containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000268 }
Anna Zaksae81e172013-01-11 03:52:37 +0000269
270 // Visit all categories in case the invalidation method is declared in
271 // a category.
Douglas Gregord3297242013-01-16 23:00:23 +0000272 for (ObjCInterfaceDecl::visible_extensions_iterator
273 Ext = InterfD->visible_extensions_begin(),
274 ExtEnd = InterfD->visible_extensions_end();
275 Ext != ExtEnd; ++Ext) {
Anna Zaks26db7db2013-02-08 23:55:43 +0000276 containsInvalidationMethod(*Ext, OutInfo, Partial);
Anna Zaksae81e172013-01-11 03:52:37 +0000277 }
278
Anna Zaks26db7db2013-02-08 23:55:43 +0000279 containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
Anna Zaks31f69cc2012-09-29 00:20:38 +0000280 return;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000281 }
282
283 // If protocol, check all parent protocols.
284 if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
285 for (ObjCInterfaceDecl::protocol_iterator
286 I = ProtD->protocol_begin(),
287 E = ProtD->protocol_end(); I != E; ++I) {
Anna Zaks26db7db2013-02-08 23:55:43 +0000288 containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000289 }
Anna Zaks31f69cc2012-09-29 00:20:38 +0000290 return;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000291 }
292
Anna Zaksae81e172013-01-11 03:52:37 +0000293 return;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000294}
295
Anna Zaks722cd9e2013-02-08 23:55:47 +0000296bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv,
Anna Zaks664566c2013-01-10 22:44:16 +0000297 IvarSet &TrackedIvars,
298 const ObjCIvarDecl **FirstIvarDecl) {
Anna Zaks377945c2012-09-27 21:57:14 +0000299 QualType IvQTy = Iv->getType();
300 const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
301 if (!IvTy)
302 return false;
303 const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
Anna Zaks31f69cc2012-09-29 00:20:38 +0000304
Anna Zaksb1fc6732013-01-10 20:59:51 +0000305 InvalidationInfo Info;
Anna Zaks26db7db2013-02-08 23:55:43 +0000306 containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
Anna Zaks31f69cc2012-09-29 00:20:38 +0000307 if (Info.needsInvalidation()) {
Anna Zaks664566c2013-01-10 22:44:16 +0000308 const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
309 TrackedIvars[I] = Info;
310 if (!*FirstIvarDecl)
311 *FirstIvarDecl = I;
Anna Zaks377945c2012-09-27 21:57:14 +0000312 return true;
313 }
314 return false;
315}
316
Anna Zaks722cd9e2013-02-08 23:55:47 +0000317const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000318 const ObjCPropertyDecl *Prop,
319 const ObjCInterfaceDecl *InterfaceD,
Anna Zaks664566c2013-01-10 22:44:16 +0000320 IvarSet &TrackedIvars,
321 const ObjCIvarDecl **FirstIvarDecl) {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000322 const ObjCIvarDecl *IvarD = 0;
323
324 // Lookup for the synthesized case.
325 IvarD = Prop->getPropertyIvarDecl();
Anna Zaks5879fb32013-01-07 19:12:56 +0000326 // We only track the ivars/properties that are defined in the current
327 // class (not the parent).
328 if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
Anna Zaks377945c2012-09-27 21:57:14 +0000329 if (TrackedIvars.count(IvarD)) {
330 return IvarD;
331 }
332 // If the ivar is synthesized we still want to track it.
Anna Zaks664566c2013-01-10 22:44:16 +0000333 if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
Anna Zaks377945c2012-09-27 21:57:14 +0000334 return IvarD;
335 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000336
337 // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
338 StringRef PropName = Prop->getIdentifier()->getName();
339 for (IvarSet::const_iterator I = TrackedIvars.begin(),
340 E = TrackedIvars.end(); I != E; ++I) {
341 const ObjCIvarDecl *Iv = I->first;
342 StringRef IvarName = Iv->getName();
343
344 if (IvarName == PropName)
345 return Iv;
346
347 SmallString<128> PropNameWithUnderscore;
348 {
349 llvm::raw_svector_ostream os(PropNameWithUnderscore);
350 os << '_' << PropName;
351 }
352 if (IvarName == PropNameWithUnderscore.str())
353 return Iv;
354 }
355
356 // Note, this is a possible source of false positives. We could look at the
357 // getter implementation to find the ivar when its name is not derived from
358 // the property name.
359 return 0;
360}
361
Anna Zaks722cd9e2013-02-08 23:55:47 +0000362void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
Anna Zaks2b174c32013-02-08 23:55:45 +0000363 const ObjCIvarDecl *IvarDecl,
364 const IvarToPropMapTy &IvarToPopertyMap) {
Anna Zaksb1fc6732013-01-10 20:59:51 +0000365 if (IvarDecl->getSynthesize()) {
Anna Zaks2b174c32013-02-08 23:55:45 +0000366 const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
Anna Zaksb1fc6732013-01-10 20:59:51 +0000367 assert(PD &&"Do we synthesize ivars for something other than properties?");
368 os << "Property "<< PD->getName() << " ";
369 } else {
370 os << "Instance variable "<< IvarDecl->getName() << " ";
371 }
372}
373
374// Check that the invalidatable interfaces with ivars/properties implement the
375// invalidation methods.
Anna Zaks722cd9e2013-02-08 23:55:47 +0000376void IvarInvalidationCheckerImpl::
377visit(const ObjCImplementationDecl *ImplD) const {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000378 // Collect all ivars that need cleanup.
379 IvarSet Ivars;
Anna Zaks664566c2013-01-10 22:44:16 +0000380 // Record the first Ivar needing invalidation; used in reporting when only
381 // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
382 // deterministic output.
383 const ObjCIvarDecl *FirstIvarDecl = 0;
Anna Zaksb1fc6732013-01-10 20:59:51 +0000384 const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
Anna Zakse0c50fa2012-10-16 19:36:37 +0000385
386 // Collect ivars declared in this class, its extensions and its implementation
387 ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
388 for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
389 Iv= Iv->getNextIvar())
Anna Zaks664566c2013-01-10 22:44:16 +0000390 trackIvar(Iv, Ivars, &FirstIvarDecl);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000391
Anna Zaks377945c2012-09-27 21:57:14 +0000392 // Construct Property/Property Accessor to Ivar maps to assist checking if an
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000393 // ivar which is backing a property has been reset.
Anna Zaks31f69cc2012-09-29 00:20:38 +0000394 MethToIvarMapTy PropSetterToIvarMap;
395 MethToIvarMapTy PropGetterToIvarMap;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000396 PropToIvarMapTy PropertyToIvarMap;
Anna Zaks377945c2012-09-27 21:57:14 +0000397 IvarToPropMapTy IvarToPopertyMap;
Anna Zaksc3c26b72012-10-18 19:17:57 +0000398
399 ObjCInterfaceDecl::PropertyMap PropMap;
400 InterfaceD->collectPropertiesToImplement(PropMap);
401
402 for (ObjCInterfaceDecl::PropertyMap::iterator
403 I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
404 const ObjCPropertyDecl *PD = I->second;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000405
Anna Zaks664566c2013-01-10 22:44:16 +0000406 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
407 &FirstIvarDecl);
Anna Zaks2b174c32013-02-08 23:55:45 +0000408 if (!ID)
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000409 continue;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000410
411 // Store the mappings.
412 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000413 PropertyToIvarMap[PD] = ID;
Anna Zaks377945c2012-09-27 21:57:14 +0000414 IvarToPopertyMap[ID] = PD;
415
416 // Find the setter and the getter.
417 const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
418 if (SetterD) {
419 SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
Anna Zaks31f69cc2012-09-29 00:20:38 +0000420 PropSetterToIvarMap[SetterD] = ID;
Anna Zaks377945c2012-09-27 21:57:14 +0000421 }
422
423 const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
424 if (GetterD) {
425 GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl());
Anna Zaks31f69cc2012-09-29 00:20:38 +0000426 PropGetterToIvarMap[GetterD] = ID;
Anna Zaks377945c2012-09-27 21:57:14 +0000427 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000428 }
429
Anna Zaksb1fc6732013-01-10 20:59:51 +0000430 // If no ivars need invalidation, there is nothing to check here.
431 if (Ivars.empty())
Anna Zaks31f69cc2012-09-29 00:20:38 +0000432 return;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000433
Anna Zaks26db7db2013-02-08 23:55:43 +0000434 // Find all partial invalidation methods.
435 InvalidationInfo PartialInfo;
436 containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true);
437
438 // Remove ivars invalidated by the partial invalidation methods. They do not
439 // need to be invalidated in the regular invalidation methods.
440 for (MethodSet::iterator
441 I = PartialInfo.InvalidationMethods.begin(),
442 E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
443 const ObjCMethodDecl *InterfD = *I;
444
445 // Get the corresponding method in the @implementation.
446 const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
447 InterfD->isInstanceMethod());
448 if (D && D->hasBody()) {
449 bool CalledAnotherInvalidationMethod = false;
450 // The MethodCrowler is going to remove the invalidated ivars.
451 MethodCrawler(Ivars,
452 CalledAnotherInvalidationMethod,
453 PropSetterToIvarMap,
454 PropGetterToIvarMap,
455 PropertyToIvarMap,
456 BR.getContext()).VisitStmt(D->getBody());
457 // If another invalidation method was called, trust that full invalidation
458 // has occurred.
459 if (CalledAnotherInvalidationMethod)
460 Ivars.clear();
461 }
462 }
463
464 // If all ivars have been invalidated by partial invalidators, there is
465 // nothing to check here.
466 if (Ivars.empty())
467 return;
468
Anna Zaksb1fc6732013-01-10 20:59:51 +0000469 // Find all invalidation methods in this @interface declaration and parents.
470 InvalidationInfo Info;
Anna Zaks26db7db2013-02-08 23:55:43 +0000471 containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000472
Anna Zaksb1fc6732013-01-10 20:59:51 +0000473 // Report an error in case none of the invalidation methods are declared.
Anna Zaks722cd9e2013-02-08 23:55:47 +0000474 if (!Info.needsInvalidation() && Filter.check_MissingInvalidationMethod) {
475 reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
Anna Zaks2b174c32013-02-08 23:55:45 +0000476 /*MissingDeclaration*/ true);
Anna Zaksb1fc6732013-01-10 20:59:51 +0000477 return;
478 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000479
Anna Zaksb1fc6732013-01-10 20:59:51 +0000480 // 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
Anna Zaks722cd9e2013-02-08 23:55:47 +0000492 // Only check if Ivars are invalidated when InstanceVariableInvalidation
493 // has been requested.
494 if (!Filter.check_InstanceVariableInvalidation)
495 break;
496
Anna Zaksb1fc6732013-01-10 20:59:51 +0000497 // Get a copy of ivars needing invalidation.
498 IvarSet IvarsI = Ivars;
499
500 bool CalledAnotherInvalidationMethod = false;
501 MethodCrawler(IvarsI,
502 CalledAnotherInvalidationMethod,
503 PropSetterToIvarMap,
504 PropGetterToIvarMap,
505 PropertyToIvarMap,
506 BR.getContext()).VisitStmt(D->getBody());
507 // If another invalidation method was called, trust that full invalidation
508 // has occurred.
509 if (CalledAnotherInvalidationMethod)
510 continue;
511
512 // Warn on the ivars that were not invalidated by the method.
Anna Zaks2b174c32013-02-08 23:55:45 +0000513 for (IvarSet::const_iterator
514 I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I)
Anna Zaks722cd9e2013-02-08 23:55:47 +0000515 reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000516 }
517 }
Anna Zaksb1fc6732013-01-10 20:59:51 +0000518
519 // Report an error in case none of the invalidation methods are implemented.
Anna Zaks722cd9e2013-02-08 23:55:47 +0000520 if (!AtImplementationContainsAtLeastOneInvalidationMethod &&
521 Filter.check_MissingInvalidationMethod)
522 reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
Anna Zaks2b174c32013-02-08 23:55:45 +0000523 /*MissingDeclaration*/ false);
524}
Anna Zaksb1fc6732013-01-10 20:59:51 +0000525
Anna Zaks722cd9e2013-02-08 23:55:47 +0000526void IvarInvalidationCheckerImpl::
Anna Zaks2b174c32013-02-08 23:55:45 +0000527reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
528 const IvarToPropMapTy &IvarToPopertyMap,
529 const ObjCInterfaceDecl *InterfaceD,
Anna Zaks722cd9e2013-02-08 23:55:47 +0000530 bool MissingDeclaration) const {
Anna Zaks2b174c32013-02-08 23:55:45 +0000531 SmallString<128> sbuf;
532 llvm::raw_svector_ostream os(sbuf);
533 assert(FirstIvarDecl);
534 printIvar(os, FirstIvarDecl, IvarToPopertyMap);
535 os << "needs to be invalidated; ";
536 if (MissingDeclaration)
537 os << "no invalidation method is declared for ";
538 else
539 os << "no invalidation method is defined in the @implementation for ";
540 os << InterfaceD->getName();
541
542 PathDiagnosticLocation IvarDecLocation =
543 PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
544
545 BR.EmitBasicReport(FirstIvarDecl, "Incomplete invalidation",
546 categories::CoreFoundationObjectiveC, os.str(),
547 IvarDecLocation);
548}
549
Anna Zaks722cd9e2013-02-08 23:55:47 +0000550void IvarInvalidationCheckerImpl::
Anna Zaks2b174c32013-02-08 23:55:45 +0000551reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
552 const IvarToPropMapTy &IvarToPopertyMap,
Anna Zaks722cd9e2013-02-08 23:55:47 +0000553 const ObjCMethodDecl *MethodD) const {
Anna Zaks2b174c32013-02-08 23:55:45 +0000554 SmallString<128> sbuf;
555 llvm::raw_svector_ostream os(sbuf);
556 printIvar(os, IvarD, IvarToPopertyMap);
557 os << "needs to be invalidated or set to nil";
558 PathDiagnosticLocation MethodDecLocation =
559 PathDiagnosticLocation::createEnd(MethodD->getBody(),
560 BR.getSourceManager(),
561 Mgr.getAnalysisDeclContext(MethodD));
562 BR.EmitBasicReport(MethodD, "Incomplete invalidation",
563 categories::CoreFoundationObjectiveC, os.str(),
564 MethodDecLocation);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000565}
566
Anna Zaks722cd9e2013-02-08 23:55:47 +0000567void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
Anna Zaks31f69cc2012-09-29 00:20:38 +0000568 const ObjCIvarDecl *Iv) {
569 IvarSet::iterator I = IVars.find(Iv);
570 if (I != IVars.end()) {
571 // If InvalidationMethod is present, we are processing the message send and
572 // should ensure we are invalidating with the appropriate method,
573 // otherwise, we are processing setting to 'nil'.
Anna Zaks26db7db2013-02-08 23:55:43 +0000574 if (!InvalidationMethod ||
575 (InvalidationMethod && I->second.hasMethod(InvalidationMethod)))
576 IVars.erase(I);
Anna Zaks31f69cc2012-09-29 00:20:38 +0000577 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000578}
579
Anna Zaks722cd9e2013-02-08 23:55:47 +0000580const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
Anna Zaks31f69cc2012-09-29 00:20:38 +0000581 E = E->IgnoreParenCasts();
582 if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
583 E = POE->getSyntacticForm()->IgnoreParenCasts();
584 if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
585 E = OVE->getSourceExpr()->IgnoreParenCasts();
586 return E;
587}
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000588
Anna Zaks722cd9e2013-02-08 23:55:47 +0000589void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
Anna Zaks31f69cc2012-09-29 00:20:38 +0000590 const ObjCIvarRefExpr *IvarRef) {
591 if (const Decl *D = IvarRef->getDecl())
592 markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
593}
594
Anna Zaks722cd9e2013-02-08 23:55:47 +0000595void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000596 const ObjCMessageExpr *ME) {
597 const ObjCMethodDecl *MD = ME->getMethodDecl();
598 if (MD) {
599 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
Anna Zaks31f69cc2012-09-29 00:20:38 +0000600 MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
601 if (IvI != PropertyGetterToIvarMap.end())
602 markInvalidated(IvI->second);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000603 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000604}
605
Anna Zaks722cd9e2013-02-08 23:55:47 +0000606void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000607 const ObjCPropertyRefExpr *PA) {
608
609 if (PA->isExplicitProperty()) {
610 const ObjCPropertyDecl *PD = PA->getExplicitProperty();
611 if (PD) {
612 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
Anna Zaks31f69cc2012-09-29 00:20:38 +0000613 PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
614 if (IvI != PropertyToIvarMap.end())
615 markInvalidated(IvI->second);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000616 return;
617 }
618 }
619
620 if (PA->isImplicitProperty()) {
621 const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
622 if (MD) {
623 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
Anna Zaks31f69cc2012-09-29 00:20:38 +0000624 MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
625 if (IvI != PropertyGetterToIvarMap.end())
626 markInvalidated(IvI->second);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000627 return;
628 }
629 }
Anna Zaks31f69cc2012-09-29 00:20:38 +0000630}
631
Anna Zaks722cd9e2013-02-08 23:55:47 +0000632bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
Anna Zaks31f69cc2012-09-29 00:20:38 +0000633 E = peel(E);
Anna Zaks31f69cc2012-09-29 00:20:38 +0000634
Anna Zaksb9733ac2012-10-01 20:33:58 +0000635 return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
636 != Expr::NPCK_NotNull);
Anna Zaks31f69cc2012-09-29 00:20:38 +0000637}
638
Anna Zaks722cd9e2013-02-08 23:55:47 +0000639void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
Anna Zaks31f69cc2012-09-29 00:20:38 +0000640 E = peel(E);
641
Anna Zaks31f69cc2012-09-29 00:20:38 +0000642 if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
643 checkObjCIvarRefExpr(IvarRef);
644 return;
645 }
646
647 if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
648 checkObjCPropertyRefExpr(PropRef);
649 return;
650 }
651
652 if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
653 checkObjCMessageExpr(MsgExpr);
654 return;
655 }
656}
657
Anna Zaks722cd9e2013-02-08 23:55:47 +0000658void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
Anna Zaks31f69cc2012-09-29 00:20:38 +0000659 const BinaryOperator *BO) {
Anna Zaksb9733ac2012-10-01 20:33:58 +0000660 VisitStmt(BO);
661
Anna Zaks65032552013-01-10 23:34:16 +0000662 // Do we assign/compare against zero? If yes, check the variable we are
663 // assigning to.
664 BinaryOperatorKind Opcode = BO->getOpcode();
665 if (Opcode != BO_Assign &&
666 Opcode != BO_EQ &&
667 Opcode != BO_NE)
Anna Zaks31f69cc2012-09-29 00:20:38 +0000668 return;
669
Anna Zaks65032552013-01-10 23:34:16 +0000670 if (isZero(BO->getRHS())) {
671 check(BO->getLHS());
672 return;
673 }
Anna Zaks31f69cc2012-09-29 00:20:38 +0000674
Anna Zaks65032552013-01-10 23:34:16 +0000675 if (Opcode != BO_Assign && isZero(BO->getLHS())) {
676 check(BO->getRHS());
677 return;
678 }
Anna Zaks31f69cc2012-09-29 00:20:38 +0000679}
680
Anna Zaks722cd9e2013-02-08 23:55:47 +0000681void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
Anna Zaks664566c2013-01-10 22:44:16 +0000682 const ObjCMessageExpr *ME) {
Anna Zaks31f69cc2012-09-29 00:20:38 +0000683 const ObjCMethodDecl *MD = ME->getMethodDecl();
684 const Expr *Receiver = ME->getInstanceReceiver();
685
686 // Stop if we are calling '[self invalidate]'.
Anna Zaks26db7db2013-02-08 23:55:43 +0000687 if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false))
Anna Zaksbbff82f2012-10-01 20:34:04 +0000688 if (Receiver->isObjCSelfExpr()) {
689 CalledAnotherInvalidationMethod = true;
690 return;
Anna Zaks31f69cc2012-09-29 00:20:38 +0000691 }
692
693 // Check if we call a setter and set the property to 'nil'.
694 if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
695 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
696 MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
697 if (IvI != PropertySetterToIvarMap.end()) {
698 markInvalidated(IvI->second);
699 return;
700 }
701 }
702
703 // Check if we call the 'invalidation' routine on the ivar.
704 if (Receiver) {
705 InvalidationMethod = MD;
706 check(Receiver->IgnoreParenCasts());
707 InvalidationMethod = 0;
708 }
709
710 VisitStmt(ME);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000711}
712}
713
Anna Zaks722cd9e2013-02-08 23:55:47 +0000714// Register the checkers.
715namespace {
716
717class IvarInvalidationChecker :
718 public Checker<check::ASTDecl<ObjCImplementationDecl> > {
719public:
720 ChecksFilter Filter;
721public:
722 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
723 BugReporter &BR) const {
724 IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
725 Walker.visit(D);
726 }
727};
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000728}
Anna Zaks722cd9e2013-02-08 23:55:47 +0000729
730#define REGISTER_CHECKER(name) \
731void ento::register##name(CheckerManager &mgr) {\
732 mgr.registerChecker<IvarInvalidationChecker>()->Filter.check_##name = true;\
733}
734
735REGISTER_CHECKER(InstanceVariableInvalidation)
736REGISTER_CHECKER(MissingInvalidationMethod)
737