blob: 0bf8073de97d3254ae528a6f8f6906fa5d573530 [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)
Anna Zaksb087bbf2012-09-27 19:45:08 +000077 this->Visit(*I);
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000078 }
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
Anna Zaksb087bbf2012-09-27 19:45:08 +000097 // TODO: We are currently ignoring the ivars coming from class extensions.
Anna Zaks5bf5c2e2012-09-26 18:55:16 +000098};
99
100bool isInvalidationMethod(const ObjCMethodDecl *M) {
Anna Zaksb087bbf2012-09-27 19:45:08 +0000101 for (specific_attr_iterator<AnnotateAttr>
102 AI = M->specific_attr_begin<AnnotateAttr>(),
103 AE = M->specific_attr_end<AnnotateAttr>(); AI != AE; ++AI) {
104 const AnnotateAttr *Ann = *AI;
105 if (Ann->getAnnotation() == "objc_instance_variable_invalidator")
106 return true;
107 }
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000108 return false;
109}
110
111bool IvarInvalidationChecker::containsInvalidationMethod (
112 const ObjCContainerDecl *D) const {
113
114 // TODO: Cache the results.
115
116 if (!D)
117 return false;
118
119 // Check all methods.
120 for (ObjCContainerDecl::method_iterator
121 I = D->meth_begin(),
122 E = D->meth_end(); I != E; ++I) {
123 const ObjCMethodDecl *MDI = *I;
124 if (isInvalidationMethod(MDI))
125 return true;
126 }
127
128 // If interface, check all parent protocols and super.
129 // TODO: Visit all categories in case the invalidation method is declared in
130 // a category.
131 if (const ObjCInterfaceDecl *InterfaceD = dyn_cast<ObjCInterfaceDecl>(D)) {
132 for (ObjCInterfaceDecl::protocol_iterator
133 I = InterfaceD->protocol_begin(),
134 E = InterfaceD->protocol_end(); I != E; ++I) {
135 if (containsInvalidationMethod(*I))
136 return true;
137 }
138 return containsInvalidationMethod(InterfaceD->getSuperClass());
139 }
140
141 // If protocol, check all parent protocols.
142 if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
143 for (ObjCInterfaceDecl::protocol_iterator
144 I = ProtD->protocol_begin(),
145 E = ProtD->protocol_end(); I != E; ++I) {
146 if (containsInvalidationMethod(*I))
147 return true;
148 }
149 return false;
150 }
151
152 llvm_unreachable("One of the casts above should have succeeded.");
153}
154
155const ObjCIvarDecl *IvarInvalidationChecker::findPropertyBackingIvar(
156 const ObjCPropertyDecl *Prop,
157 const ObjCInterfaceDecl *InterfaceD,
158 IvarSet TrackedIvars) {
159 const ObjCIvarDecl *IvarD = 0;
160
161 // Lookup for the synthesized case.
162 IvarD = Prop->getPropertyIvarDecl();
163 if (IvarD)
164 return IvarD;
165
166 // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
167 StringRef PropName = Prop->getIdentifier()->getName();
168 for (IvarSet::const_iterator I = TrackedIvars.begin(),
169 E = TrackedIvars.end(); I != E; ++I) {
170 const ObjCIvarDecl *Iv = I->first;
171 StringRef IvarName = Iv->getName();
172
173 if (IvarName == PropName)
174 return Iv;
175
176 SmallString<128> PropNameWithUnderscore;
177 {
178 llvm::raw_svector_ostream os(PropNameWithUnderscore);
179 os << '_' << PropName;
180 }
181 if (IvarName == PropNameWithUnderscore.str())
182 return Iv;
183 }
184
185 // Note, this is a possible source of false positives. We could look at the
186 // getter implementation to find the ivar when its name is not derived from
187 // the property name.
188 return 0;
189}
190
191void IvarInvalidationChecker::checkASTDecl(const ObjCMethodDecl *D,
192 AnalysisManager& Mgr,
193 BugReporter &BR) const {
194 // We are only interested in checking the cleanup methods.
195 if (!D->hasBody() || !isInvalidationMethod(D))
196 return;
197
198 // Collect all ivars that need cleanup.
199 IvarSet Ivars;
200 const ObjCInterfaceDecl *InterfaceD = D->getClassInterface();
201 for (ObjCInterfaceDecl::ivar_iterator
202 II = InterfaceD->ivar_begin(),
203 IE = InterfaceD->ivar_end(); II != IE; ++II) {
204 const ObjCIvarDecl *Iv = *II;
205 QualType IvQTy = Iv->getType();
206 const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
207 if (!IvTy)
208 continue;
Anna Zaksb087bbf2012-09-27 19:45:08 +0000209 const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000210 if (containsInvalidationMethod(IvInterf))
211 Ivars[cast<ObjCIvarDecl>(Iv->getCanonicalDecl())] = false;
212 }
213
214 // Construct Property/Property Setter to Ivar maps to assist checking if an
215 // ivar which is backing a property has been reset.
216 MethToIvarMapTy PropSetterToIvarMap;
217 PropToIvarMapTy PropertyToIvarMap;
218 for (ObjCInterfaceDecl::prop_iterator
219 I = InterfaceD->prop_begin(),
220 E = InterfaceD->prop_end(); I != E; ++I) {
221 const ObjCPropertyDecl *PD = *I;
222
223 const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars);
224 if (!ID) {
225 continue;
226 }
227 // Find the setter.
228 const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
229 // If we don't know the setter, do not track this ivar.
Anna Zaksb087bbf2012-09-27 19:45:08 +0000230 if (!SetterD)
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000231 continue;
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000232
233 // Store the mappings.
234 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
235 SetterD = cast<ObjCMethodDecl>(SetterD->getCanonicalDecl());
236 PropertyToIvarMap[PD] = ID;
237 PropSetterToIvarMap[SetterD] = ID;
238 }
239
240
241 // Check which ivars have been accessed by the method.
242 // We assume that if ivar was at least accessed, it was not forgotten.
243 MethodCrawler(InterfaceD, Ivars,
244 PropSetterToIvarMap, PropertyToIvarMap).VisitStmt(D->getBody());
245
246 // Warn on the ivars that were not accessed by the method.
247 for (IvarSet::const_iterator I = Ivars.begin(), E = Ivars.end(); I != E; ++I){
248 if (I->second == false) {
249 const ObjCIvarDecl *IvarDecl = I->first;
250
251 PathDiagnosticLocation IvarDecLocation =
Anna Zaksb087bbf2012-09-27 19:45:08 +0000252 PathDiagnosticLocation::createEnd(D->getBody(), BR.getSourceManager(),
253 Mgr.getAnalysisDeclContext(D));
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000254
255 SmallString<128> sbuf;
256 llvm::raw_svector_ostream os(sbuf);
Anna Zaksb087bbf2012-09-27 19:45:08 +0000257 os << "Instance variable "<< IvarDecl->getName()
258 << " needs to be invalidated";
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000259
Anna Zaksb087bbf2012-09-27 19:45:08 +0000260 BR.EmitBasicReport(D,
Anna Zaks5bf5c2e2012-09-26 18:55:16 +0000261 "Incomplete invalidation",
262 categories::CoreFoundationObjectiveC, os.str(),
263 IvarDecLocation);
264 }
265 }
266}
267
268/// Handle the case when an ivar is directly accessed.
269void IvarInvalidationChecker::MethodCrawler::VisitObjCIvarRefExpr(
270 const ObjCIvarRefExpr *IvarRef) {
271 const Decl *D = IvarRef->getDecl();
272 if (D)
273 IVars[cast<ObjCIvarDecl>(D->getCanonicalDecl())] = true;
274 VisitStmt(IvarRef);
275}
276
277
278/// Handle the case when the property backing ivar is set via a direct call
279/// to the setter.
280void IvarInvalidationChecker::MethodCrawler::VisitObjCMessageExpr(
281 const ObjCMessageExpr *ME) {
282 const ObjCMethodDecl *MD = ME->getMethodDecl();
283 if (MD) {
284 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
285 IVars[PropertySetterToIvarMap[MD]] = true;
286 }
287 VisitStmt(ME);
288}
289
290/// Handle the case when the property backing ivar is set via the dot syntax.
291void IvarInvalidationChecker::MethodCrawler::VisitObjCPropertyRefExpr(
292 const ObjCPropertyRefExpr *PA) {
293
294 if (PA->isExplicitProperty()) {
295 const ObjCPropertyDecl *PD = PA->getExplicitProperty();
296 if (PD) {
297 PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
298 IVars[PropertyToIvarMap[PD]] = true;
299 VisitStmt(PA);
300 return;
301 }
302 }
303
304 if (PA->isImplicitProperty()) {
305 const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
306 if (MD) {
307 MD = cast<ObjCMethodDecl>(MD->getCanonicalDecl());
308 IVars[PropertySetterToIvarMap[MD]] = true;
309 VisitStmt(PA);
310 return;
311 }
312 }
313 VisitStmt(PA);
314}
315}
316
317// Register the checker.
318void ento::registerIvarInvalidationChecker(CheckerManager &mgr) {
319 mgr.registerChecker<IvarInvalidationChecker>();
320}