blob: e41ecf4b650babc33196b03047ba06f45cb3d52a [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// TODO: move this somewhere?
47struct DefaultBool {
48 bool val;
49 DefaultBool() : val(false) {}
50 operator bool() const { return val; }
51 DefaultBool &operator=(bool b) { val = b; return *this; }
52};
53
54struct ChecksFilter {
55 /// Check for missing invalidation method declarations.
56 DefaultBool check_MissingInvalidationMethod;
57 /// Check that all ivars are invalidated.
58 DefaultBool check_InstanceVariableInvalidation;
59};
60
61class IvarInvalidationCheckerImpl :
Anna Zaksb1fc6732013-01-10 20:59:51 +000062 public Checker<check::ASTDecl<ObjCImplementationDecl> > {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000063
Anna Zaksb1fc6732013-01-10 20:59:51 +000064 typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000065 typedef llvm::DenseMap<const ObjCMethodDecl*,
66 const ObjCIvarDecl*> MethToIvarMapTy;
67 typedef llvm::DenseMap<const ObjCPropertyDecl*,
68 const ObjCIvarDecl*> PropToIvarMapTy;
Anna Zaks377945c2012-09-27 21:57:14 +000069 typedef llvm::DenseMap<const ObjCIvarDecl*,
70 const ObjCPropertyDecl*> IvarToPropMapTy;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000071
Anna Zaks31f69cc2012-09-29 00:20:38 +000072
Anna Zaksb1fc6732013-01-10 20:59:51 +000073 struct InvalidationInfo {
Anna Zaks31f69cc2012-09-29 00:20:38 +000074 /// Has the ivar been invalidated?
75 bool IsInvalidated;
76
77 /// The methods which can be used to invalidate the ivar.
78 MethodSet InvalidationMethods;
79
Anna Zaksb1fc6732013-01-10 20:59:51 +000080 InvalidationInfo() : IsInvalidated(false) {}
Anna Zaks31f69cc2012-09-29 00:20:38 +000081 void addInvalidationMethod(const ObjCMethodDecl *MD) {
82 InvalidationMethods.insert(MD);
83 }
84
85 bool needsInvalidation() const {
86 return !InvalidationMethods.empty();
87 }
88
Anna Zaks26db7db2013-02-08 23:55:43 +000089 bool hasMethod(const ObjCMethodDecl *MD) {
Anna Zaks31f69cc2012-09-29 00:20:38 +000090 if (IsInvalidated)
91 return true;
92 for (MethodSet::iterator I = InvalidationMethods.begin(),
93 E = InvalidationMethods.end(); I != E; ++I) {
94 if (*I == MD) {
95 IsInvalidated = true;
96 return true;
97 }
98 }
99 return false;
100 }
Anna Zaks31f69cc2012-09-29 00:20:38 +0000101 };
102
Anna Zaksb1fc6732013-01-10 20:59:51 +0000103 typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
Anna Zaks31f69cc2012-09-29 00:20:38 +0000104
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000105 /// Statement visitor, which walks the method body and flags the ivars
106 /// referenced in it (either directly or via property).
107 class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000108 /// The set of Ivars which need to be invalidated.
109 IvarSet &IVars;
110
Anna Zaks31f69cc2012-09-29 00:20:38 +0000111 /// Flag is set as the result of a message send to another
112 /// invalidation method.
113 bool &CalledAnotherInvalidationMethod;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000114
Anna Zaks31f69cc2012-09-29 00:20:38 +0000115 /// Property setter to ivar mapping.
116 const MethToIvarMapTy &PropertySetterToIvarMap;
117
118 /// Property getter to ivar mapping.
119 const MethToIvarMapTy &PropertyGetterToIvarMap;
120
121 /// Property to ivar mapping.
122 const PropToIvarMapTy &PropertyToIvarMap;
123
124 /// The invalidation method being currently processed.
125 const ObjCMethodDecl *InvalidationMethod;
126
Anna Zaksb9733ac2012-10-01 20:33:58 +0000127 ASTContext &Ctx;
128
129 /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
Anna Zaks31f69cc2012-09-29 00:20:38 +0000130 const Expr *peel(const Expr *E) const;
131
132 /// Does this expression represent zero: '0'?
133 bool isZero(const Expr *E) const;
134
135 /// Mark the given ivar as invalidated.
136 void markInvalidated(const ObjCIvarDecl *Iv);
137
138 /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
139 /// invalidated.
140 void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
141
142 /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
143 /// it as invalidated.
144 void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
145
146 /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
147 /// if yes, marks it as invalidated.
148 void checkObjCMessageExpr(const ObjCMessageExpr *ME);
149
150 /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
151 void check(const Expr *E);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000152
153 public:
Anna Zaksbbff82f2012-10-01 20:34:04 +0000154 MethodCrawler(IvarSet &InIVars,
Anna Zaks31f69cc2012-09-29 00:20:38 +0000155 bool &InCalledAnotherInvalidationMethod,
156 const MethToIvarMapTy &InPropertySetterToIvarMap,
157 const MethToIvarMapTy &InPropertyGetterToIvarMap,
Anna Zaksb9733ac2012-10-01 20:33:58 +0000158 const PropToIvarMapTy &InPropertyToIvarMap,
159 ASTContext &InCtx)
Anna Zaksbbff82f2012-10-01 20:34:04 +0000160 : IVars(InIVars),
Anna Zaks31f69cc2012-09-29 00:20:38 +0000161 CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
162 PropertySetterToIvarMap(InPropertySetterToIvarMap),
163 PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
164 PropertyToIvarMap(InPropertyToIvarMap),
Anna Zaksb9733ac2012-10-01 20:33:58 +0000165 InvalidationMethod(0),
166 Ctx(InCtx) {}
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000167
168 void VisitStmt(const Stmt *S) { VisitChildren(S); }
169
Anna Zaks31f69cc2012-09-29 00:20:38 +0000170 void VisitBinaryOperator(const BinaryOperator *BO);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000171
172 void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
173
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000174 void VisitChildren(const Stmt *S) {
Anna Zaks31f69cc2012-09-29 00:20:38 +0000175 for (Stmt::const_child_range I = S->children(); I; ++I) {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000176 if (*I)
Anna Zaksb087bbf2012-09-27 19:45:08 +0000177 this->Visit(*I);
Anna Zaks31f69cc2012-09-29 00:20:38 +0000178 if (CalledAnotherInvalidationMethod)
179 return;
180 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000181 }
182 };
183
184 /// Check if the any of the methods inside the interface are annotated with
Anna Zaks31f69cc2012-09-29 00:20:38 +0000185 /// the invalidation annotation, update the IvarInfo accordingly.
Anna Zaks26db7db2013-02-08 23:55:43 +0000186 /// \param LookForPartial is set when we are searching for partial
187 /// invalidators.
Anna Zaks31f69cc2012-09-29 00:20:38 +0000188 static void containsInvalidationMethod(const ObjCContainerDecl *D,
Anna Zaks26db7db2013-02-08 23:55:43 +0000189 InvalidationInfo &Out,
190 bool LookForPartial);
Anna Zaks377945c2012-09-27 21:57:14 +0000191
192 /// Check if ivar should be tracked and add to TrackedIvars if positive.
193 /// Returns true if ivar should be tracked.
Anna Zaks664566c2013-01-10 22:44:16 +0000194 static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
195 const ObjCIvarDecl **FirstIvarDecl);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000196
197 /// Given the property declaration, and the list of tracked ivars, finds
198 /// the ivar backing the property when possible. Returns '0' when no such
199 /// ivar could be found.
200 static const ObjCIvarDecl *findPropertyBackingIvar(
201 const ObjCPropertyDecl *Prop,
202 const ObjCInterfaceDecl *InterfaceD,
Anna Zaks664566c2013-01-10 22:44:16 +0000203 IvarSet &TrackedIvars,
204 const ObjCIvarDecl **FirstIvarDecl);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000205
Anna Zaksb1fc6732013-01-10 20:59:51 +0000206 /// Print ivar name or the property if the given ivar backs a property.
207 static void printIvar(llvm::raw_svector_ostream &os,
208 const ObjCIvarDecl *IvarDecl,
Anna Zaks2b174c32013-02-08 23:55:45 +0000209 const IvarToPropMapTy &IvarToPopertyMap);
210
Anna Zaks722cd9e2013-02-08 23:55:47 +0000211 void reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
212 const IvarToPropMapTy &IvarToPopertyMap,
213 const ObjCInterfaceDecl *InterfaceD,
214 bool MissingDeclaration) const;
215 void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
216 const IvarToPropMapTy &IvarToPopertyMap,
217 const ObjCMethodDecl *MethodD) const;
Anna Zaks2b174c32013-02-08 23:55:45 +0000218
Anna Zaks722cd9e2013-02-08 23:55:47 +0000219 AnalysisManager& Mgr;
220 BugReporter &BR;
221 /// Filter on the checks performed.
222 const ChecksFilter &Filter;
Anna Zaks26db7db2013-02-08 23:55:43 +0000223
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000224public:
Anna Zaks722cd9e2013-02-08 23:55:47 +0000225 IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
226 BugReporter &InBR,
227 const ChecksFilter &InFilter) :
228 Mgr (InMgr), BR(InBR), Filter(InFilter) {}
229
230 void visit(const ObjCImplementationDecl *D) const;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000231};
232
Anna Zaks26db7db2013-02-08 23:55:43 +0000233static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
Anna Zaksb087bbf2012-09-27 19:45:08 +0000234 for (specific_attr_iterator<AnnotateAttr>
235 AI = M->specific_attr_begin<AnnotateAttr>(),
236 AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
237 const AnnotateAttr *Ann = *AI;
Anna Zaks26db7db2013-02-08 23:55:43 +0000238 if (!LookForPartial &&
239 Ann->getAnnotation() == "objc_instance_variable_invalidator")
240 return true;
241 if (LookForPartial &&
242 Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
Anna Zaksb087bbf2012-09-27 19:45:08 +0000243 return true;
244 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000245 return false;
246}
247
Anna Zaks722cd9e2013-02-08 23:55:47 +0000248void IvarInvalidationCheckerImpl::containsInvalidationMethod(
Anna Zaks26db7db2013-02-08 23:55:43 +0000249 const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000250
251 if (!D)
Anna Zaks31f69cc2012-09-29 00:20:38 +0000252 return;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000253
Anna Zaksb1fc6732013-01-10 20:59:51 +0000254 assert(!isa<ObjCImplementationDecl>(D));
255 // TODO: Cache the results.
256
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000257 // Check all methods.
258 for (ObjCContainerDecl::method_iterator
259 I = D->meth_begin(),
260 E = D->meth_end(); I != E; ++I) {
261 const ObjCMethodDecl *MDI = *I;
Anna Zaks26db7db2013-02-08 23:55:43 +0000262 if (isInvalidationMethod(MDI, Partial))
Anna Zaks31f69cc2012-09-29 00:20:38 +0000263 OutInfo.addInvalidationMethod(
264 cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000265 }
266
267 // If interface, check all parent protocols and super.
Anna Zaksae81e172013-01-11 03:52:37 +0000268 if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
269
270 // Visit all protocols.
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000271 for (ObjCInterfaceDecl::protocol_iterator
Anna Zaksae81e172013-01-11 03:52:37 +0000272 I = InterfD->protocol_begin(),
273 E = InterfD->protocol_end(); I != E; ++I) {
Anna Zaks26db7db2013-02-08 23:55:43 +0000274 containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000275 }
Anna Zaksae81e172013-01-11 03:52:37 +0000276
277 // Visit all categories in case the invalidation method is declared in
278 // a category.
Douglas Gregord3297242013-01-16 23:00:23 +0000279 for (ObjCInterfaceDecl::visible_extensions_iterator
280 Ext = InterfD->visible_extensions_begin(),
281 ExtEnd = InterfD->visible_extensions_end();
282 Ext != ExtEnd; ++Ext) {
Anna Zaks26db7db2013-02-08 23:55:43 +0000283 containsInvalidationMethod(*Ext, OutInfo, Partial);
Anna Zaksae81e172013-01-11 03:52:37 +0000284 }
285
Anna Zaks26db7db2013-02-08 23:55:43 +0000286 containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
Anna Zaks31f69cc2012-09-29 00:20:38 +0000287 return;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000288 }
289
290 // If protocol, check all parent protocols.
291 if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
292 for (ObjCInterfaceDecl::protocol_iterator
293 I = ProtD->protocol_begin(),
294 E = ProtD->protocol_end(); I != E; ++I) {
Anna Zaks26db7db2013-02-08 23:55:43 +0000295 containsInvalidationMethod((*I)->getDefinition(), OutInfo, Partial);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000296 }
Anna Zaks31f69cc2012-09-29 00:20:38 +0000297 return;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000298 }
299
Anna Zaksae81e172013-01-11 03:52:37 +0000300 return;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000301}
302
Anna Zaks722cd9e2013-02-08 23:55:47 +0000303bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv,
Anna Zaks664566c2013-01-10 22:44:16 +0000304 IvarSet &TrackedIvars,
305 const ObjCIvarDecl **FirstIvarDecl) {
Anna Zaks377945c2012-09-27 21:57:14 +0000306 QualType IvQTy = Iv->getType();
307 const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
308 if (!IvTy)
309 return false;
310 const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
Anna Zaks31f69cc2012-09-29 00:20:38 +0000311
Anna Zaksb1fc6732013-01-10 20:59:51 +0000312 InvalidationInfo Info;
Anna Zaks26db7db2013-02-08 23:55:43 +0000313 containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
Anna Zaks31f69cc2012-09-29 00:20:38 +0000314 if (Info.needsInvalidation()) {
Anna Zaks664566c2013-01-10 22:44:16 +0000315 const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
316 TrackedIvars[I] = Info;
317 if (!*FirstIvarDecl)
318 *FirstIvarDecl = I;
Anna Zaks377945c2012-09-27 21:57:14 +0000319 return true;
320 }
321 return false;
322}
323
Anna Zaks722cd9e2013-02-08 23:55:47 +0000324const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000325 const ObjCPropertyDecl *Prop,
326 const ObjCInterfaceDecl *InterfaceD,
Anna Zaks664566c2013-01-10 22:44:16 +0000327 IvarSet &TrackedIvars,
328 const ObjCIvarDecl **FirstIvarDecl) {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000329 const ObjCIvarDecl *IvarD = 0;
330
331 // Lookup for the synthesized case.
332 IvarD = Prop->getPropertyIvarDecl();
Anna Zaks5879fb32013-01-07 19:12:56 +0000333 // We only track the ivars/properties that are defined in the current
334 // class (not the parent).
335 if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
Anna Zaks377945c2012-09-27 21:57:14 +0000336 if (TrackedIvars.count(IvarD)) {
337 return IvarD;
338 }
339 // If the ivar is synthesized we still want to track it.
Anna Zaks664566c2013-01-10 22:44:16 +0000340 if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
Anna Zaks377945c2012-09-27 21:57:14 +0000341 return IvarD;
342 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000343
344 // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
345 StringRef PropName = Prop->getIdentifier()->getName();
346 for (IvarSet::const_iterator I = TrackedIvars.begin(),
347 E = TrackedIvars.end(); I != E; ++I) {
348 const ObjCIvarDecl *Iv = I->first;
349 StringRef IvarName = Iv->getName();
350
351 if (IvarName == PropName)
352 return Iv;
353
354 SmallString<128> PropNameWithUnderscore;
355 {
356 llvm::raw_svector_ostream os(PropNameWithUnderscore);
357 os << '_' << PropName;
358 }
359 if (IvarName == PropNameWithUnderscore.str())
360 return Iv;
361 }
362
363 // Note, this is a possible source of false positives. We could look at the
364 // getter implementation to find the ivar when its name is not derived from
365 // the property name.
366 return 0;
367}
368
Anna Zaks722cd9e2013-02-08 23:55:47 +0000369void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
Anna Zaks2b174c32013-02-08 23:55:45 +0000370 const ObjCIvarDecl *IvarDecl,
371 const IvarToPropMapTy &IvarToPopertyMap) {
Anna Zaksb1fc6732013-01-10 20:59:51 +0000372 if (IvarDecl->getSynthesize()) {
Anna Zaks2b174c32013-02-08 23:55:45 +0000373 const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
Anna Zaksb1fc6732013-01-10 20:59:51 +0000374 assert(PD &&"Do we synthesize ivars for something other than properties?");
375 os << "Property "<< PD->getName() << " ";
376 } else {
377 os << "Instance variable "<< IvarDecl->getName() << " ";
378 }
379}
380
381// Check that the invalidatable interfaces with ivars/properties implement the
382// invalidation methods.
Anna Zaks722cd9e2013-02-08 23:55:47 +0000383void IvarInvalidationCheckerImpl::
384visit(const ObjCImplementationDecl *ImplD) const {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000385 // Collect all ivars that need cleanup.
386 IvarSet Ivars;
Anna Zaks664566c2013-01-10 22:44:16 +0000387 // Record the first Ivar needing invalidation; used in reporting when only
388 // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
389 // deterministic output.
390 const ObjCIvarDecl *FirstIvarDecl = 0;
Anna Zaksb1fc6732013-01-10 20:59:51 +0000391 const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
Anna Zakse0c50fa2012-10-16 19:36:37 +0000392
393 // Collect ivars declared in this class, its extensions and its implementation
394 ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
395 for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
396 Iv= Iv->getNextIvar())
Anna Zaks664566c2013-01-10 22:44:16 +0000397 trackIvar(Iv, Ivars, &FirstIvarDecl);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000398
Anna Zaks377945c2012-09-27 21:57:14 +0000399 // Construct Property/Property Accessor to Ivar maps to assist checking if an
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000400 // ivar which is backing a property has been reset.
Anna Zaks31f69cc2012-09-29 00:20:38 +0000401 MethToIvarMapTy PropSetterToIvarMap;
402 MethToIvarMapTy PropGetterToIvarMap;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000403 PropToIvarMapTy PropertyToIvarMap;
Anna Zaks377945c2012-09-27 21:57:14 +0000404 IvarToPropMapTy IvarToPopertyMap;
Anna Zaksc3c26b72012-10-18 19:17:57 +0000405
406 ObjCInterfaceDecl::PropertyMap PropMap;
407 InterfaceD->collectPropertiesToImplement(PropMap);
408
409 for (ObjCInterfaceDecl::PropertyMap::iterator
410 I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
411 const ObjCPropertyDecl *PD = I->second;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000412
Anna Zaks664566c2013-01-10 22:44:16 +0000413 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
414 &FirstIvarDecl);
Anna Zaks2b174c32013-02-08 23:55:45 +0000415 if (!ID)
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000416 continue;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000417
418 // Store the mappings.
419 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000420 PropertyToIvarMap[PD] = ID;
Anna Zaks377945c2012-09-27 21:57:14 +0000421 IvarToPopertyMap[ID] = PD;
422
423 // Find the setter and the getter.
424 const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
425 if (SetterD) {
426 SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
Anna Zaks31f69cc2012-09-29 00:20:38 +0000427 PropSetterToIvarMap[SetterD] = ID;
Anna Zaks377945c2012-09-27 21:57:14 +0000428 }
429
430 const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
431 if (GetterD) {
432 GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl());
Anna Zaks31f69cc2012-09-29 00:20:38 +0000433 PropGetterToIvarMap[GetterD] = ID;
Anna Zaks377945c2012-09-27 21:57:14 +0000434 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000435 }
436
Anna Zaksb1fc6732013-01-10 20:59:51 +0000437 // If no ivars need invalidation, there is nothing to check here.
438 if (Ivars.empty())
Anna Zaks31f69cc2012-09-29 00:20:38 +0000439 return;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000440
Anna Zaks26db7db2013-02-08 23:55:43 +0000441 // Find all partial invalidation methods.
442 InvalidationInfo PartialInfo;
443 containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true);
444
445 // Remove ivars invalidated by the partial invalidation methods. They do not
446 // need to be invalidated in the regular invalidation methods.
447 for (MethodSet::iterator
448 I = PartialInfo.InvalidationMethods.begin(),
449 E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
450 const ObjCMethodDecl *InterfD = *I;
451
452 // Get the corresponding method in the @implementation.
453 const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
454 InterfD->isInstanceMethod());
455 if (D && D->hasBody()) {
456 bool CalledAnotherInvalidationMethod = false;
457 // The MethodCrowler is going to remove the invalidated ivars.
458 MethodCrawler(Ivars,
459 CalledAnotherInvalidationMethod,
460 PropSetterToIvarMap,
461 PropGetterToIvarMap,
462 PropertyToIvarMap,
463 BR.getContext()).VisitStmt(D->getBody());
464 // If another invalidation method was called, trust that full invalidation
465 // has occurred.
466 if (CalledAnotherInvalidationMethod)
467 Ivars.clear();
468 }
469 }
470
471 // If all ivars have been invalidated by partial invalidators, there is
472 // nothing to check here.
473 if (Ivars.empty())
474 return;
475
Anna Zaksb1fc6732013-01-10 20:59:51 +0000476 // Find all invalidation methods in this @interface declaration and parents.
477 InvalidationInfo Info;
Anna Zaks26db7db2013-02-08 23:55:43 +0000478 containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000479
Anna Zaksb1fc6732013-01-10 20:59:51 +0000480 // Report an error in case none of the invalidation methods are declared.
Anna Zaks722cd9e2013-02-08 23:55:47 +0000481 if (!Info.needsInvalidation() && Filter.check_MissingInvalidationMethod) {
482 reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
Anna Zaks2b174c32013-02-08 23:55:45 +0000483 /*MissingDeclaration*/ true);
Anna Zaksb1fc6732013-01-10 20:59:51 +0000484 return;
485 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000486
Anna Zaksb1fc6732013-01-10 20:59:51 +0000487 // Check that all ivars are invalidated by the invalidation methods.
488 bool AtImplementationContainsAtLeastOneInvalidationMethod = false;
489 for (MethodSet::iterator I = Info.InvalidationMethods.begin(),
490 E = Info.InvalidationMethods.end(); I != E; ++I) {
491 const ObjCMethodDecl *InterfD = *I;
492
493 // Get the corresponding method in the @implementation.
494 const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
495 InterfD->isInstanceMethod());
496 if (D && D->hasBody()) {
497 AtImplementationContainsAtLeastOneInvalidationMethod = true;
498
Anna Zaks722cd9e2013-02-08 23:55:47 +0000499 // Only check if Ivars are invalidated when InstanceVariableInvalidation
500 // has been requested.
501 if (!Filter.check_InstanceVariableInvalidation)
502 break;
503
Anna Zaksb1fc6732013-01-10 20:59:51 +0000504 // Get a copy of ivars needing invalidation.
505 IvarSet IvarsI = Ivars;
506
507 bool CalledAnotherInvalidationMethod = false;
508 MethodCrawler(IvarsI,
509 CalledAnotherInvalidationMethod,
510 PropSetterToIvarMap,
511 PropGetterToIvarMap,
512 PropertyToIvarMap,
513 BR.getContext()).VisitStmt(D->getBody());
514 // If another invalidation method was called, trust that full invalidation
515 // has occurred.
516 if (CalledAnotherInvalidationMethod)
517 continue;
518
519 // Warn on the ivars that were not invalidated by the method.
Anna Zaks2b174c32013-02-08 23:55:45 +0000520 for (IvarSet::const_iterator
521 I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I)
Anna Zaks722cd9e2013-02-08 23:55:47 +0000522 reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000523 }
524 }
Anna Zaksb1fc6732013-01-10 20:59:51 +0000525
526 // Report an error in case none of the invalidation methods are implemented.
Anna Zaks722cd9e2013-02-08 23:55:47 +0000527 if (!AtImplementationContainsAtLeastOneInvalidationMethod &&
528 Filter.check_MissingInvalidationMethod)
529 reportNoInvalidationMethod(FirstIvarDecl, IvarToPopertyMap, InterfaceD,
Anna Zaks2b174c32013-02-08 23:55:45 +0000530 /*MissingDeclaration*/ false);
531}
Anna Zaksb1fc6732013-01-10 20:59:51 +0000532
Anna Zaks722cd9e2013-02-08 23:55:47 +0000533void IvarInvalidationCheckerImpl::
Anna Zaks2b174c32013-02-08 23:55:45 +0000534reportNoInvalidationMethod(const ObjCIvarDecl *FirstIvarDecl,
535 const IvarToPropMapTy &IvarToPopertyMap,
536 const ObjCInterfaceDecl *InterfaceD,
Anna Zaks722cd9e2013-02-08 23:55:47 +0000537 bool MissingDeclaration) const {
Anna Zaks2b174c32013-02-08 23:55:45 +0000538 SmallString<128> sbuf;
539 llvm::raw_svector_ostream os(sbuf);
540 assert(FirstIvarDecl);
541 printIvar(os, FirstIvarDecl, IvarToPopertyMap);
542 os << "needs to be invalidated; ";
543 if (MissingDeclaration)
544 os << "no invalidation method is declared for ";
545 else
546 os << "no invalidation method is defined in the @implementation for ";
547 os << InterfaceD->getName();
548
549 PathDiagnosticLocation IvarDecLocation =
550 PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
551
552 BR.EmitBasicReport(FirstIvarDecl, "Incomplete invalidation",
553 categories::CoreFoundationObjectiveC, os.str(),
554 IvarDecLocation);
555}
556
Anna Zaks722cd9e2013-02-08 23:55:47 +0000557void IvarInvalidationCheckerImpl::
Anna Zaks2b174c32013-02-08 23:55:45 +0000558reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
559 const IvarToPropMapTy &IvarToPopertyMap,
Anna Zaks722cd9e2013-02-08 23:55:47 +0000560 const ObjCMethodDecl *MethodD) const {
Anna Zaks2b174c32013-02-08 23:55:45 +0000561 SmallString<128> sbuf;
562 llvm::raw_svector_ostream os(sbuf);
563 printIvar(os, IvarD, IvarToPopertyMap);
564 os << "needs to be invalidated or set to nil";
565 PathDiagnosticLocation MethodDecLocation =
566 PathDiagnosticLocation::createEnd(MethodD->getBody(),
567 BR.getSourceManager(),
568 Mgr.getAnalysisDeclContext(MethodD));
569 BR.EmitBasicReport(MethodD, "Incomplete invalidation",
570 categories::CoreFoundationObjectiveC, os.str(),
571 MethodDecLocation);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000572}
573
Anna Zaks722cd9e2013-02-08 23:55:47 +0000574void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
Anna Zaks31f69cc2012-09-29 00:20:38 +0000575 const ObjCIvarDecl *Iv) {
576 IvarSet::iterator I = IVars.find(Iv);
577 if (I != IVars.end()) {
578 // If InvalidationMethod is present, we are processing the message send and
579 // should ensure we are invalidating with the appropriate method,
580 // otherwise, we are processing setting to 'nil'.
Anna Zaks26db7db2013-02-08 23:55:43 +0000581 if (!InvalidationMethod ||
582 (InvalidationMethod && I->second.hasMethod(InvalidationMethod)))
583 IVars.erase(I);
Anna Zaks31f69cc2012-09-29 00:20:38 +0000584 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000585}
586
Anna Zaks722cd9e2013-02-08 23:55:47 +0000587const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
Anna Zaks31f69cc2012-09-29 00:20:38 +0000588 E = E->IgnoreParenCasts();
589 if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
590 E = POE->getSyntacticForm()->IgnoreParenCasts();
591 if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
592 E = OVE->getSourceExpr()->IgnoreParenCasts();
593 return E;
594}
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000595
Anna Zaks722cd9e2013-02-08 23:55:47 +0000596void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
Anna Zaks31f69cc2012-09-29 00:20:38 +0000597 const ObjCIvarRefExpr *IvarRef) {
598 if (const Decl *D = IvarRef->getDecl())
599 markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
600}
601
Anna Zaks722cd9e2013-02-08 23:55:47 +0000602void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000603 const ObjCMessageExpr *ME) {
604 const ObjCMethodDecl *MD = ME->getMethodDecl();
605 if (MD) {
606 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
Anna Zaks31f69cc2012-09-29 00:20:38 +0000607 MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
608 if (IvI != PropertyGetterToIvarMap.end())
609 markInvalidated(IvI->second);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000610 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000611}
612
Anna Zaks722cd9e2013-02-08 23:55:47 +0000613void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000614 const ObjCPropertyRefExpr *PA) {
615
616 if (PA->isExplicitProperty()) {
617 const ObjCPropertyDecl *PD = PA->getExplicitProperty();
618 if (PD) {
619 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
Anna Zaks31f69cc2012-09-29 00:20:38 +0000620 PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
621 if (IvI != PropertyToIvarMap.end())
622 markInvalidated(IvI->second);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000623 return;
624 }
625 }
626
627 if (PA->isImplicitProperty()) {
628 const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
629 if (MD) {
630 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
Anna Zaks31f69cc2012-09-29 00:20:38 +0000631 MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
632 if (IvI != PropertyGetterToIvarMap.end())
633 markInvalidated(IvI->second);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000634 return;
635 }
636 }
Anna Zaks31f69cc2012-09-29 00:20:38 +0000637}
638
Anna Zaks722cd9e2013-02-08 23:55:47 +0000639bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
Anna Zaks31f69cc2012-09-29 00:20:38 +0000640 E = peel(E);
Anna Zaks31f69cc2012-09-29 00:20:38 +0000641
Anna Zaksb9733ac2012-10-01 20:33:58 +0000642 return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
643 != Expr::NPCK_NotNull);
Anna Zaks31f69cc2012-09-29 00:20:38 +0000644}
645
Anna Zaks722cd9e2013-02-08 23:55:47 +0000646void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
Anna Zaks31f69cc2012-09-29 00:20:38 +0000647 E = peel(E);
648
Anna Zaks31f69cc2012-09-29 00:20:38 +0000649 if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
650 checkObjCIvarRefExpr(IvarRef);
651 return;
652 }
653
654 if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
655 checkObjCPropertyRefExpr(PropRef);
656 return;
657 }
658
659 if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
660 checkObjCMessageExpr(MsgExpr);
661 return;
662 }
663}
664
Anna Zaks722cd9e2013-02-08 23:55:47 +0000665void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
Anna Zaks31f69cc2012-09-29 00:20:38 +0000666 const BinaryOperator *BO) {
Anna Zaksb9733ac2012-10-01 20:33:58 +0000667 VisitStmt(BO);
668
Anna Zaks65032552013-01-10 23:34:16 +0000669 // Do we assign/compare against zero? If yes, check the variable we are
670 // assigning to.
671 BinaryOperatorKind Opcode = BO->getOpcode();
672 if (Opcode != BO_Assign &&
673 Opcode != BO_EQ &&
674 Opcode != BO_NE)
Anna Zaks31f69cc2012-09-29 00:20:38 +0000675 return;
676
Anna Zaks65032552013-01-10 23:34:16 +0000677 if (isZero(BO->getRHS())) {
678 check(BO->getLHS());
679 return;
680 }
Anna Zaks31f69cc2012-09-29 00:20:38 +0000681
Anna Zaks65032552013-01-10 23:34:16 +0000682 if (Opcode != BO_Assign && isZero(BO->getLHS())) {
683 check(BO->getRHS());
684 return;
685 }
Anna Zaks31f69cc2012-09-29 00:20:38 +0000686}
687
Anna Zaks722cd9e2013-02-08 23:55:47 +0000688void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
Anna Zaks664566c2013-01-10 22:44:16 +0000689 const ObjCMessageExpr *ME) {
Anna Zaks31f69cc2012-09-29 00:20:38 +0000690 const ObjCMethodDecl *MD = ME->getMethodDecl();
691 const Expr *Receiver = ME->getInstanceReceiver();
692
693 // Stop if we are calling '[self invalidate]'.
Anna Zaks26db7db2013-02-08 23:55:43 +0000694 if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false))
Anna Zaksbbff82f2012-10-01 20:34:04 +0000695 if (Receiver->isObjCSelfExpr()) {
696 CalledAnotherInvalidationMethod = true;
697 return;
Anna Zaks31f69cc2012-09-29 00:20:38 +0000698 }
699
700 // Check if we call a setter and set the property to 'nil'.
701 if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
702 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
703 MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
704 if (IvI != PropertySetterToIvarMap.end()) {
705 markInvalidated(IvI->second);
706 return;
707 }
708 }
709
710 // Check if we call the 'invalidation' routine on the ivar.
711 if (Receiver) {
712 InvalidationMethod = MD;
713 check(Receiver->IgnoreParenCasts());
714 InvalidationMethod = 0;
715 }
716
717 VisitStmt(ME);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000718}
719}
720
Anna Zaks722cd9e2013-02-08 23:55:47 +0000721// Register the checkers.
722namespace {
723
724class IvarInvalidationChecker :
725 public Checker<check::ASTDecl<ObjCImplementationDecl> > {
726public:
727 ChecksFilter Filter;
728public:
729 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
730 BugReporter &BR) const {
731 IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
732 Walker.visit(D);
733 }
734};
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000735}
Anna Zaks722cd9e2013-02-08 23:55:47 +0000736
737#define REGISTER_CHECKER(name) \
738void ento::register##name(CheckerManager &mgr) {\
739 mgr.registerChecker<IvarInvalidationChecker>()->Filter.check_##name = true;\
740}
741
742REGISTER_CHECKER(InstanceVariableInvalidation)
743REGISTER_CHECKER(MissingInvalidationMethod)
744