blob: 836860e7c88109535c6d2807a4419fb4c5459445 [file] [log] [blame]
Anna Zaks5bf5c2e2012-09-26 18:55:16 +00001//=- IvarInvalidationChecker.cpp - -*- C++ ----*-==//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This checker implements annotation driven invalidation checking. If a class
11// contains a method annotated with 'objc_instance_variable_invalidator',
12// - (void) foo
13// __attribute__((annotate("objc_instance_variable_invalidator")));
14// all the "ivalidatable" instance variables of this class should be
15// invalidated. We call an instance variable ivalidatable if it is an object of
16// a class which contains an invalidation method.
17//
18// Note, this checker currently only checks if an ivar was accessed by the
19// method, we do not currently support any deeper invalidation checking.
20//
21//===----------------------------------------------------------------------===//
22
23#include "ClangSACheckers.h"
24#include "clang/StaticAnalyzer/Core/Checker.h"
25#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
26#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
27#include "clang/AST/DeclObjC.h"
28#include "clang/AST/StmtVisitor.h"
29#include "llvm/ADT/DenseMap.h"
30#include "llvm/ADT/SmallString.h"
31
32using namespace clang;
33using namespace ento;
34
35namespace {
36class IvarInvalidationChecker :
37 public Checker<check::ASTDecl<ObjCMethodDecl> > {
38
39 typedef llvm::DenseMap<const ObjCIvarDecl*, bool> IvarSet;
40 typedef llvm::DenseMap<const ObjCMethodDecl*,
41 const ObjCIvarDecl*> MethToIvarMapTy;
42 typedef llvm::DenseMap<const ObjCPropertyDecl*,
43 const ObjCIvarDecl*> PropToIvarMapTy;
Anna Zaks377945c2012-09-27 21:57:14 +000044 typedef llvm::DenseMap<const ObjCIvarDecl*,
45 const ObjCPropertyDecl*> IvarToPropMapTy;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000046
47 /// Statement visitor, which walks the method body and flags the ivars
48 /// referenced in it (either directly or via property).
49 class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000050
51 /// The set of Ivars which need to be invalidated.
52 IvarSet &IVars;
53
Anna Zaks377945c2012-09-27 21:57:14 +000054 /// Property setter/getter to ivar mapping.
55 MethToIvarMapTy &PropertyAccessorToIvarMap;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000056
57 // Property to ivar mapping.
58 PropToIvarMapTy &PropertyToIvarMap;
59
60 public:
61 MethodCrawler(const ObjCInterfaceDecl *InID,
Anna Zaks377945c2012-09-27 21:57:14 +000062 IvarSet &InIVars,
63 MethToIvarMapTy &InPropertyAccessorToIvarMap,
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000064 PropToIvarMapTy &InPropertyToIvarMap)
NAKAMURA Takumi78360112012-09-27 01:52:00 +000065 : IVars(InIVars),
Anna Zaks377945c2012-09-27 21:57:14 +000066 PropertyAccessorToIvarMap(InPropertyAccessorToIvarMap),
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000067 PropertyToIvarMap(InPropertyToIvarMap) {}
68
69 void VisitStmt(const Stmt *S) { VisitChildren(S); }
70
71 void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
72
73 void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
74
75 void VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
76
77 void VisitChildren(const Stmt *S) {
78 for (Stmt::const_child_range I = S->children(); I; ++I)
79 if (*I)
Anna Zaksb087bbf2012-09-27 19:45:08 +000080 this->Visit(*I);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000081 }
82 };
83
84 /// Check if the any of the methods inside the interface are annotated with
85 /// the invalidation annotation.
Anna Zaks377945c2012-09-27 21:57:14 +000086 static bool containsInvalidationMethod(const ObjCContainerDecl *D);
87
88 /// Check if ivar should be tracked and add to TrackedIvars if positive.
89 /// Returns true if ivar should be tracked.
90 static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000091
92 /// Given the property declaration, and the list of tracked ivars, finds
93 /// the ivar backing the property when possible. Returns '0' when no such
94 /// ivar could be found.
95 static const ObjCIvarDecl *findPropertyBackingIvar(
96 const ObjCPropertyDecl *Prop,
97 const ObjCInterfaceDecl *InterfaceD,
Anna Zaks377945c2012-09-27 21:57:14 +000098 IvarSet &TrackedIvars);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000099
100public:
101 void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager& Mgr,
102 BugReporter &BR) const;
103
Anna Zaksb087bbf2012-09-27 19:45:08 +0000104 // TODO: We are currently ignoring the ivars coming from class extensions.
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000105};
106
107bool isInvalidationMethod(const ObjCMethodDecl *M) {
Anna Zaksb087bbf2012-09-27 19:45:08 +0000108 for (specific_attr_iterator<AnnotateAttr>
109 AI = M->specific_attr_begin<AnnotateAttr>(),
110 AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
111 const AnnotateAttr *Ann = *AI;
112 if (Ann->getAnnotation() == "objc_instance_variable_invalidator")
113 return true;
114 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000115 return false;
116}
117
118bool IvarInvalidationChecker::containsInvalidationMethod (
Anna Zaks377945c2012-09-27 21:57:14 +0000119 const ObjCContainerDecl *D) {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000120
121 // TODO: Cache the results.
122
123 if (!D)
124 return false;
125
126 // Check all methods.
127 for (ObjCContainerDecl::method_iterator
128 I = D->meth_begin(),
129 E = D->meth_end(); I != E; ++I) {
130 const ObjCMethodDecl *MDI = *I;
131 if (isInvalidationMethod(MDI))
132 return true;
133 }
134
135 // If interface, check all parent protocols and super.
136 // TODO: Visit all categories in case the invalidation method is declared in
137 // a category.
138 if (const ObjCInterfaceDecl *InterfaceD = dyn_cast<ObjCInterfaceDecl>(D)) {
139 for (ObjCInterfaceDecl::protocol_iterator
140 I = InterfaceD->protocol_begin(),
141 E = InterfaceD->protocol_end(); I != E; ++I) {
142 if (containsInvalidationMethod(*I))
143 return true;
144 }
145 return containsInvalidationMethod(InterfaceD->getSuperClass());
146 }
147
148 // If protocol, check all parent protocols.
149 if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
150 for (ObjCInterfaceDecl::protocol_iterator
151 I = ProtD->protocol_begin(),
152 E = ProtD->protocol_end(); I != E; ++I) {
153 if (containsInvalidationMethod(*I))
154 return true;
155 }
156 return false;
157 }
158
159 llvm_unreachable("One of the casts above should have succeeded.");
160}
161
Anna Zaks377945c2012-09-27 21:57:14 +0000162bool IvarInvalidationChecker::trackIvar(const ObjCIvarDecl *Iv,
163 IvarSet &TrackedIvars) {
164 QualType IvQTy = Iv->getType();
165 const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
166 if (!IvTy)
167 return false;
168 const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
169 if (containsInvalidationMethod(IvInterf)) {
170 TrackedIvars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = false;
171 return true;
172 }
173 return false;
174}
175
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000176const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar(
177 const ObjCPropertyDecl *Prop,
178 const ObjCInterfaceDecl *InterfaceD,
Anna Zaks377945c2012-09-27 21:57:14 +0000179 IvarSet &TrackedIvars) {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000180 const ObjCIvarDecl *IvarD = 0;
181
182 // Lookup for the synthesized case.
183 IvarD = Prop->getPropertyIvarDecl();
Anna Zaks377945c2012-09-27 21:57:14 +0000184 if (IvarD) {
185 if (TrackedIvars.count(IvarD)) {
186 return IvarD;
187 }
188 // If the ivar is synthesized we still want to track it.
189 if (trackIvar(IvarD, TrackedIvars))
190 return IvarD;
191 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000192
193 // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
194 StringRef PropName = Prop->getIdentifier()->getName();
195 for (IvarSet::const_iterator I = TrackedIvars.begin(),
196 E = TrackedIvars.end(); I != E; ++I) {
197 const ObjCIvarDecl *Iv = I->first;
198 StringRef IvarName = Iv->getName();
199
200 if (IvarName == PropName)
201 return Iv;
202
203 SmallString<128> PropNameWithUnderscore;
204 {
205 llvm::raw_svector_ostream os(PropNameWithUnderscore);
206 os << '_' << PropName;
207 }
208 if (IvarName == PropNameWithUnderscore.str())
209 return Iv;
210 }
211
212 // Note, this is a possible source of false positives. We could look at the
213 // getter implementation to find the ivar when its name is not derived from
214 // the property name.
215 return 0;
216}
217
218void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D,
219 AnalysisManager& Mgr,
220 BugReporter &BR) const {
221 // We are only interested in checking the cleanup methods.
222 if (!D->hasBody() || !isInvalidationMethod(D))
223 return;
224
225 // Collect all ivars that need cleanup.
226 IvarSet Ivars;
227 const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
228 for (ObjCInterfaceDecl::ivar_iterator
229 II = InterfaceD->ivar_begin(),
230 IE = InterfaceD->ivar_end(); II != IE; ++II) {
231 const ObjCIvarDecl *Iv = *II;
Anna Zaks377945c2012-09-27 21:57:14 +0000232 trackIvar(Iv, Ivars);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000233 }
234
Anna Zaks377945c2012-09-27 21:57:14 +0000235 // Construct Property/Property Accessor to Ivar maps to assist checking if an
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000236 // ivar which is backing a property has been reset.
Anna Zaks377945c2012-09-27 21:57:14 +0000237 MethToIvarMapTy PropAccessorToIvarMap;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000238 PropToIvarMapTy PropertyToIvarMap;
Anna Zaks377945c2012-09-27 21:57:14 +0000239 IvarToPropMapTy IvarToPopertyMap;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000240 for (ObjCInterfaceDecl::prop_iterator
241 I = InterfaceD->prop_begin(),
242 E = InterfaceD->prop_end(); I != E; ++I) {
243 const ObjCPropertyDecl *PD = *I;
244
245 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars);
246 if (!ID) {
247 continue;
248 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000249
250 // Store the mappings.
251 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000252 PropertyToIvarMap[PD] = ID;
Anna Zaks377945c2012-09-27 21:57:14 +0000253 IvarToPopertyMap[ID] = PD;
254
255 // Find the setter and the getter.
256 const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
257 if (SetterD) {
258 SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
259 PropAccessorToIvarMap[SetterD] = ID;
260 }
261
262 const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
263 if (GetterD) {
264 GetterD = cast<ObjCMethodDecl>(GetterD->getCanonicalDecl());
265 PropAccessorToIvarMap[GetterD] = ID;
266 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000267 }
268
269
270 // Check which ivars have been accessed by the method.
271 // We assume that if ivar was at least accessed, it was not forgotten.
272 MethodCrawler(InterfaceD, Ivars,
Anna Zaks377945c2012-09-27 21:57:14 +0000273 PropAccessorToIvarMap, PropertyToIvarMap).VisitStmt(D->getBody());
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000274
275 // Warn on the ivars that were not accessed by the method.
276 for (IvarSet::const_iterator I = Ivars.begin(), E = Ivars.end(); I != E; ++I){
277 if (I->second == false) {
278 const ObjCIvarDecl *IvarDecl = I->first;
279
280 PathDiagnosticLocation IvarDecLocation =
Anna Zaksb087bbf2012-09-27 19:45:08 +0000281 PathDiagnosticLocation::createEnd(D->getBody(), BR.getSourceManager(),
282 Mgr.getAnalysisDeclContext(D));
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000283
284 SmallString<128> sbuf;
285 llvm::raw_svector_ostream os(sbuf);
Anna Zaks377945c2012-09-27 21:57:14 +0000286
287 // Construct the warning message.
288 if (IvarDecl->getSynthesize()) {
289 const ObjCPropertyDecl *PD = IvarToPopertyMap[IvarDecl];
290 assert(PD &&
291 "Do we synthesize ivars for something other than properties?");
292 os << "Property "<< PD->getName() << " needs to be invalidated";
293 } else {
294 os << "Instance variable "<< IvarDecl->getName()
295 << " needs to be invalidated";
296 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000297
Anna Zaksb087bbf2012-09-27 19:45:08 +0000298 BR.EmitBasicReport(D,
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000299 "Incomplete invalidation",
300 categories::CoreFoundationObjectiveC, os.str(),
301 IvarDecLocation);
302 }
303 }
304}
305
306/// Handle the case when an ivar is directly accessed.
307void IvarInvalidationChecker::MethodCrawler::VisitObjCIvarRefExpr(
308 const ObjCIvarRefExpr *IvarRef) {
309 const Decl *D = IvarRef->getDecl();
310 if (D)
311 IVars[cast<ObjCIvarDecl>(D->getCanonicalDecl())] = true;
312 VisitStmt(IvarRef);
313}
314
315
316/// Handle the case when the property backing ivar is set via a direct call
317/// to the setter.
318void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr(
319 const ObjCMessageExpr *ME) {
320 const ObjCMethodDecl *MD = ME->getMethodDecl();
321 if (MD) {
322 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
Anna Zaks377945c2012-09-27 21:57:14 +0000323 IVars[PropertyAccessorToIvarMap[MD]] = true;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000324 }
325 VisitStmt(ME);
326}
327
328/// Handle the case when the property backing ivar is set via the dot syntax.
329void IvarInvalidationChecker::MethodCrawler::VisitObjCPropertyRefExpr(
330 const ObjCPropertyRefExpr *PA) {
331
332 if (PA->isExplicitProperty()) {
333 const ObjCPropertyDecl *PD = PA->getExplicitProperty();
334 if (PD) {
335 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
336 IVars[PropertyToIvarMap[PD]] = true;
337 VisitStmt(PA);
338 return;
339 }
340 }
341
342 if (PA->isImplicitProperty()) {
343 const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
344 if (MD) {
345 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
Anna Zaks377945c2012-09-27 21:57:14 +0000346 IVars[PropertyAccessorToIvarMap[MD]] = true;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000347 VisitStmt(PA);
348 return;
349 }
350 }
351 VisitStmt(PA);
352}
353}
354
355// Register the checker.
356void ento::registerIvarInvalidationChecker(CheckerManager &mgr) {
357 mgr.registerChecker<IvarInvalidationChecker>();
358}