blob: 97b1f7cfaa092973fa247518387ce112e48de5a1 [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;
44
45 /// Statement visitor, which walks the method body and flags the ivars
46 /// referenced in it (either directly or via property).
47 class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000048
49 /// The set of Ivars which need to be invalidated.
50 IvarSet &IVars;
51
52 /// Property setter to ivar mapping.
53 MethToIvarMapTy &PropertySetterToIvarMap;
54
55 // Property to ivar mapping.
56 PropToIvarMapTy &PropertyToIvarMap;
57
58 public:
59 MethodCrawler(const ObjCInterfaceDecl *InID,
60 IvarSet &InIVars, MethToIvarMapTy &InPropertySetterToIvarMap,
61 PropToIvarMapTy &InPropertyToIvarMap)
NAKAMURA Takumi78360112012-09-27 01:52:00 +000062 : IVars(InIVars),
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000063 PropertySetterToIvarMap(InPropertySetterToIvarMap),
64 PropertyToIvarMap(InPropertyToIvarMap) {}
65
66 void VisitStmt(const Stmt *S) { VisitChildren(S); }
67
68 void VisitObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
69
70 void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
71
72 void VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
73
74 void VisitChildren(const Stmt *S) {
75 for (Stmt::const_child_range I = S->children(); I; ++I)
76 if (*I)
77 static_cast<MethodCrawler*>(this)->Visit(*I);
78 }
79 };
80
81 /// Check if the any of the methods inside the interface are annotated with
82 /// the invalidation annotation.
83 bool containsInvalidationMethod(const ObjCContainerDecl *D) const;
84
85 /// Given the property declaration, and the list of tracked ivars, finds
86 /// the ivar backing the property when possible. Returns '0' when no such
87 /// ivar could be found.
88 static const ObjCIvarDecl *findPropertyBackingIvar(
89 const ObjCPropertyDecl *Prop,
90 const ObjCInterfaceDecl *InterfaceD,
91 IvarSet TrackedIvars);
92
93public:
94 void checkASTDecl(const ObjCMethodDecl *D, AnalysisManager& Mgr,
95 BugReporter &BR) const;
96
97};
98
99bool isInvalidationMethod(const ObjCMethodDecl *M) {
100 const AnnotateAttr *Ann = M->getAttr<AnnotateAttr>();
101 if (!Ann)
102 return false;
103 if (Ann->getAnnotation() == "objc_instance_variable_invalidator")
104 return true;
105 return false;
106}
107
108bool IvarInvalidationChecker::containsInvalidationMethod (
109 const ObjCContainerDecl *D) const {
110
111 // TODO: Cache the results.
112
113 if (!D)
114 return false;
115
116 // Check all methods.
117 for (ObjCContainerDecl::method_iterator
118 I = D->meth_begin(),
119 E = D->meth_end(); I != E; ++I) {
120 const ObjCMethodDecl *MDI = *I;
121 if (isInvalidationMethod(MDI))
122 return true;
123 }
124
125 // If interface, check all parent protocols and super.
126 // TODO: Visit all categories in case the invalidation method is declared in
127 // a category.
128 if (const ObjCInterfaceDecl *InterfaceD = dyn_cast<ObjCInterfaceDecl>(D)) {
129 for (ObjCInterfaceDecl::protocol_iterator
130 I = InterfaceD->protocol_begin(),
131 E = InterfaceD->protocol_end(); I != E; ++I) {
132 if (containsInvalidationMethod(*I))
133 return true;
134 }
135 return containsInvalidationMethod(InterfaceD->getSuperClass());
136 }
137
138 // If protocol, check all parent protocols.
139 if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
140 for (ObjCInterfaceDecl::protocol_iterator
141 I = ProtD->protocol_begin(),
142 E = ProtD->protocol_end(); I != E; ++I) {
143 if (containsInvalidationMethod(*I))
144 return true;
145 }
146 return false;
147 }
148
149 llvm_unreachable("One of the casts above should have succeeded.");
150}
151
152const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar(
153 const ObjCPropertyDecl *Prop,
154 const ObjCInterfaceDecl *InterfaceD,
155 IvarSet TrackedIvars) {
156 const ObjCIvarDecl *IvarD = 0;
157
158 // Lookup for the synthesized case.
159 IvarD = Prop->getPropertyIvarDecl();
160 if (IvarD)
161 return IvarD;
162
163 // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
164 StringRef PropName = Prop->getIdentifier()->getName();
165 for (IvarSet::const_iterator I = TrackedIvars.begin(),
166 E = TrackedIvars.end(); I != E; ++I) {
167 const ObjCIvarDecl *Iv = I->first;
168 StringRef IvarName = Iv->getName();
169
170 if (IvarName == PropName)
171 return Iv;
172
173 SmallString<128> PropNameWithUnderscore;
174 {
175 llvm::raw_svector_ostream os(PropNameWithUnderscore);
176 os << '_' << PropName;
177 }
178 if (IvarName == PropNameWithUnderscore.str())
179 return Iv;
180 }
181
182 // Note, this is a possible source of false positives. We could look at the
183 // getter implementation to find the ivar when its name is not derived from
184 // the property name.
185 return 0;
186}
187
188void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D,
189 AnalysisManager& Mgr,
190 BugReporter &BR) const {
191 // We are only interested in checking the cleanup methods.
192 if (!D->hasBody() || !isInvalidationMethod(D))
193 return;
194
195 // Collect all ivars that need cleanup.
196 IvarSet Ivars;
197 const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
198 for (ObjCInterfaceDecl::ivar_iterator
199 II = InterfaceD->ivar_begin(),
200 IE = InterfaceD->ivar_end(); II != IE; ++II) {
201 const ObjCIvarDecl *Iv = *II;
202 QualType IvQTy = Iv->getType();
203 const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
204 if (!IvTy)
205 continue;
206 const ObjCInterfaceDecl *IvInterf = IvTy->getObjectType()->getInterface();
207 if (containsInvalidationMethod(IvInterf))
208 Ivars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = false;
209 }
210
211 // Construct Property/Property Setter to Ivar maps to assist checking if an
212 // ivar which is backing a property has been reset.
213 MethToIvarMapTy PropSetterToIvarMap;
214 PropToIvarMapTy PropertyToIvarMap;
215 for (ObjCInterfaceDecl::prop_iterator
216 I = InterfaceD->prop_begin(),
217 E = InterfaceD->prop_end(); I != E; ++I) {
218 const ObjCPropertyDecl *PD = *I;
219
220 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars);
221 if (!ID) {
222 continue;
223 }
224 // Find the setter.
225 const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
226 // If we don't know the setter, do not track this ivar.
227 if (!SetterD) {
228 Ivars[cast<ObjCIvarDecl>(ID->getCanonicalDecl())] = true;
229 continue;
230 }
231
232 // Store the mappings.
233 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
234 SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
235 PropertyToIvarMap[PD] = ID;
236 PropSetterToIvarMap[SetterD] = ID;
237 }
238
239
240 // Check which ivars have been accessed by the method.
241 // We assume that if ivar was at least accessed, it was not forgotten.
242 MethodCrawler(InterfaceD, Ivars,
243 PropSetterToIvarMap, PropertyToIvarMap).VisitStmt(D->getBody());
244
245 // Warn on the ivars that were not accessed by the method.
246 for (IvarSet::const_iterator I = Ivars.begin(), E = Ivars.end(); I != E; ++I){
247 if (I->second == false) {
248 const ObjCIvarDecl *IvarDecl = I->first;
249
250 PathDiagnosticLocation IvarDecLocation =
251 PathDiagnosticLocation::createBegin(IvarDecl, BR.getSourceManager());
252
253 SmallString<128> sbuf;
254 llvm::raw_svector_ostream os(sbuf);
255 os << "Ivar needs to be invalidated in the '" <<
256 D->getSelector().getAsString()<< "' method";
257
258 BR.EmitBasicReport(IvarDecl,
259 "Incomplete invalidation",
260 categories::CoreFoundationObjectiveC, os.str(),
261 IvarDecLocation);
262 }
263 }
264}
265
266/// Handle the case when an ivar is directly accessed.
267void IvarInvalidationChecker::MethodCrawler::VisitObjCIvarRefExpr(
268 const ObjCIvarRefExpr *IvarRef) {
269 const Decl *D = IvarRef->getDecl();
270 if (D)
271 IVars[cast<ObjCIvarDecl>(D->getCanonicalDecl())] = true;
272 VisitStmt(IvarRef);
273}
274
275
276/// Handle the case when the property backing ivar is set via a direct call
277/// to the setter.
278void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr(
279 const ObjCMessageExpr *ME) {
280 const ObjCMethodDecl *MD = ME->getMethodDecl();
281 if (MD) {
282 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
283 IVars[PropertySetterToIvarMap[MD]] = true;
284 }
285 VisitStmt(ME);
286}
287
288/// Handle the case when the property backing ivar is set via the dot syntax.
289void IvarInvalidationChecker::MethodCrawler::VisitObjCPropertyRefExpr(
290 const ObjCPropertyRefExpr *PA) {
291
292 if (PA->isExplicitProperty()) {
293 const ObjCPropertyDecl *PD = PA->getExplicitProperty();
294 if (PD) {
295 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
296 IVars[PropertyToIvarMap[PD]] = true;
297 VisitStmt(PA);
298 return;
299 }
300 }
301
302 if (PA->isImplicitProperty()) {
303 const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
304 if (MD) {
305 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
306 IVars[PropertySetterToIvarMap[MD]] = true;
307 VisitStmt(PA);
308 return;
309 }
310 }
311 VisitStmt(PA);
312}
313}
314
315// Register the checker.
316void ento::registerIvarInvalidationChecker(CheckerManager &mgr) {
317 mgr.registerChecker<IvarInvalidationChecker>();
318}