 |
|
 |
|
| Files: |
1 |
|
Branches Taken: |
74.2% |
89 / 120 |
| Generated: |
2010-02-10 01:31 |
|
Branches Executed: |
96.7% |
116 / 120 |
| |
|
Line Coverage: |
84.7% |
94 / 111 |
| |
 |
|
 |
1 : //==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- 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 file defines a CheckObjCDealloc, a checker that
11 : // analyzes an Objective-C class's implementation to determine if it
12 : // correctly implements -dealloc.
13 : //
14 : //===----------------------------------------------------------------------===//
15 :
16 : #include "clang/Checker/Checkers/LocalCheckers.h"
17 : #include "clang/Checker/BugReporter/PathDiagnostic.h"
18 : #include "clang/Checker/BugReporter/BugReporter.h"
19 : #include "clang/AST/ExprObjC.h"
20 : #include "clang/AST/Expr.h"
21 : #include "clang/AST/DeclObjC.h"
22 : #include "clang/Basic/LangOptions.h"
23 : #include "llvm/Support/raw_ostream.h"
24 :
25 : using namespace clang;
26 :
27 43: static bool scan_dealloc(Stmt* S, Selector Dealloc) {
28 :
8: branch 1 taken
35: branch 2 taken
29 43: if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S))
3: branch 2 taken
5: branch 3 taken
30 8: if (ME->getSelector() == Dealloc)
3: branch 1 taken
0: branch 2 not taken
31 3: if (ME->getReceiver())
3: branch 2 taken
0: branch 3 not taken
32 3: if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts())
33 3: return isa<ObjCSuperExpr>(Receiver);
34 :
35 : // Recurse to children.
36 :
40: branch 4 taken
37: branch 5 taken
37 77: for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I)
40: branch 1 taken
0: branch 2 not taken
3: branch 5 taken
37: branch 6 taken
3: branch 7 taken
37: branch 8 taken
38 40: if (*I && scan_dealloc(*I, Dealloc))
39 3: return true;
40 :
41 37: return false;
42 : }
43 :
44 : static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID,
45 : const ObjCPropertyDecl* PD,
46 : Selector Release,
47 : IdentifierInfo* SelfII,
48 141: ASTContext& Ctx) {
49 :
50 : // [mMyIvar release]
30: branch 1 taken
111: branch 2 taken
51 141: if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S))
18: branch 2 taken
12: branch 3 taken
52 30: if (ME->getSelector() == Release)
18: branch 1 taken
0: branch 2 not taken
53 18: if (ME->getReceiver())
18: branch 2 taken
0: branch 3 not taken
54 18: if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts())
18: branch 1 taken
0: branch 2 not taken
55 18: if (ObjCIvarRefExpr* E = dyn_cast<ObjCIvarRefExpr>(Receiver))
2: branch 1 taken
16: branch 2 taken
56 18: if (E->getDecl() == ID)
57 2: return true;
58 :
59 : // [self setMyIvar:nil];
28: branch 1 taken
111: branch 2 taken
60 139: if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S))
28: branch 1 taken
0: branch 2 not taken
61 28: if (ME->getReceiver())
28: branch 2 taken
0: branch 3 not taken
62 28: if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts())
7: branch 1 taken
21: branch 2 taken
63 28: if (DeclRefExpr* E = dyn_cast<DeclRefExpr>(Receiver))
7: branch 2 taken
0: branch 3 not taken
64 7: if (E->getDecl()->getIdentifier() == SelfII)
2: branch 2 taken
5: branch 3 taken
2: branch 5 taken
0: branch 6 not taken
1: branch 9 taken
1: branch 10 taken
1: branch 11 taken
6: branch 12 taken
65 7: if (ME->getMethodDecl() == PD->getSetterMethodDecl() &&
66 : ME->getNumArgs() == 1 &&
67 : ME->getArg(0)->isNullPointerConstant(Ctx,
68 : Expr::NPC_ValueDependentIsNull))
69 1: return true;
70 :
71 : // self.myIvar = nil;
10: branch 1 taken
128: branch 2 taken
72 138: if (BinaryOperator* BO = dyn_cast<BinaryOperator>(S))
10: branch 1 taken
0: branch 2 not taken
73 10: if (BO->isAssignmentOp())
7: branch 0 taken
3: branch 1 taken
74 10: if (ObjCPropertyRefExpr* PRE =
75 10: dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts()))
3: branch 1 taken
4: branch 2 taken
76 7: if (PRE->getProperty() == PD)
3: branch 2 taken
0: branch 3 not taken
77 3: if (BO->getRHS()->isNullPointerConstant(Ctx,
78 : Expr::NPC_ValueDependentIsNull)) {
79 : // This is only a 'release' if the property kind is not
80 : // 'assign'.
81 3: return PD->getSetterKind() != ObjCPropertyDecl::Assign;;
82 : }
83 :
84 : // Recurse to children.
132: branch 4 taken
131: branch 5 taken
85 263: for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I)
132: branch 1 taken
0: branch 2 not taken
4: branch 5 taken
128: branch 6 taken
4: branch 7 taken
128: branch 8 taken
86 132: if (*I && scan_ivar_release(*I, ID, PD, Release, SelfII, Ctx))
87 4: return true;
88 :
89 131: return false;
90 : }
91 :
92 : void clang::CheckObjCDealloc(const ObjCImplementationDecl* D,
93 6: const LangOptions& LOpts, BugReporter& BR) {
94 :
0: branch 1 not taken
6: branch 2 taken
95 6: assert (LOpts.getGCMode() != LangOptions::GCOnly);
96 :
97 6: ASTContext& Ctx = BR.getContext();
98 6: const ObjCInterfaceDecl* ID = D->getClassInterface();
99 :
100 : // Does the class contain any ivars that are pointers (or id<...>)?
101 : // If not, skip the check entirely.
102 : // NOTE: This is motivated by PR 2517:
103 : // http://llvm.org/bugs/show_bug.cgi?id=2517
104 :
105 6: bool containsPointerIvar = false;
106 :
7: branch 2 taken
2: branch 3 taken
107 9: for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end();
108 : I!=E; ++I) {
109 :
110 7: ObjCIvarDecl* ID = *I;
111 7: QualType T = ID->getType();
112 :
5: branch 2 taken
2: branch 3 taken
1: branch 5 taken
4: branch 6 taken
4: branch 7 taken
3: branch 8 taken
113 7: if (!T->isObjCObjectPointerType() ||
114 : ID->getAttr<IBOutletAttr>()) // Skip IBOutlets.
115 3: continue;
116 :
117 4: containsPointerIvar = true;
118 4: break;
119 : }
120 :
2: branch 0 taken
4: branch 1 taken
121 6: if (!containsPointerIvar)
122 2: return;
123 :
124 : // Determine if the class subclasses NSObject.
125 4: IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");
126 4: IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase");
127 :
128 :
8: branch 1 taken
0: branch 2 not taken
129 8: for ( ; ID ; ID = ID->getSuperClass()) {
130 8: IdentifierInfo *II = ID->getIdentifier();
131 :
3: branch 0 taken
5: branch 1 taken
132 8: if (II == NSObjectII)
133 3: break;
134 :
135 : // FIXME: For now, ignore classes that subclass SenTestCase, as these don't
136 : // need to implement -dealloc. They implement tear down in another way,
137 : // which we should try and catch later.
138 : // http://llvm.org/bugs/show_bug.cgi?id=3187
1: branch 0 taken
4: branch 1 taken
139 5: if (II == SenTestCaseII)
140 1: return;
141 : }
142 :
0: branch 0 not taken
3: branch 1 taken
143 3: if (!ID)
144 0: return;
145 :
146 : // Get the "dealloc" selector.
147 3: IdentifierInfo* II = &Ctx.Idents.get("dealloc");
148 3: Selector S = Ctx.Selectors.getSelector(0, &II);
149 3: ObjCMethodDecl* MD = 0;
150 :
151 : // Scan the instance methods for "dealloc".
5: branch 3 taken
0: branch 4 not taken
152 8: for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
153 3: E = D->instmeth_end(); I!=E; ++I) {
154 :
3: branch 3 taken
2: branch 4 taken
155 5: if ((*I)->getSelector() == S) {
156 3: MD = *I;
157 3: break;
158 : }
159 : }
160 :
0: branch 0 not taken
3: branch 1 taken
161 3: if (!MD) { // No dealloc found.
162 :
163 : const char* name = LOpts.getGCMode() == LangOptions::NonGC
164 : ? "missing -dealloc"
0: branch 1 not taken
0: branch 2 not taken
165 0: : "missing -dealloc (Hybrid MM, non-GC)";
166 :
167 0: std::string buf;
168 0: llvm::raw_string_ostream os(buf);
169 : os << "Objective-C class '" << D->getNameAsString()
170 0: << "' lacks a 'dealloc' instance method";
171 :
172 0: BR.EmitBasicReport(name, os.str(), D->getLocStart());
173 0: return;
174 : }
175 :
176 : // dealloc found. Scan for missing [super dealloc].
3: branch 1 taken
0: branch 2 not taken
0: branch 5 not taken
3: branch 6 taken
0: branch 7 not taken
3: branch 8 taken
177 3: if (MD->getBody() && !scan_dealloc(MD->getBody(), S)) {
178 :
179 : const char* name = LOpts.getGCMode() == LangOptions::NonGC
180 : ? "missing [super dealloc]"
0: branch 1 not taken
0: branch 2 not taken
181 0: : "missing [super dealloc] (Hybrid MM, non-GC)";
182 :
183 0: std::string buf;
184 0: llvm::raw_string_ostream os(buf);
185 : os << "The 'dealloc' instance method in Objective-C class '"
186 : << D->getNameAsString()
187 : << "' does not send a 'dealloc' message to its super class"
188 0: " (missing [super dealloc])";
189 :
190 0: BR.EmitBasicReport(name, os.str(), D->getLocStart());
191 0: return;
192 : }
193 :
194 : // Get the "release" selector.
195 3: IdentifierInfo* RII = &Ctx.Idents.get("release");
196 3: Selector RS = Ctx.Selectors.getSelector(0, &RII);
197 :
198 : // Get the "self" identifier
199 3: IdentifierInfo* SelfII = &Ctx.Idents.get("self");
200 :
201 : // Scan for missing and extra releases of ivars used by implementations
202 : // of synthesized properties
10: branch 3 taken
3: branch 4 taken
203 16: for (ObjCImplementationDecl::propimpl_iterator I = D->propimpl_begin(),
204 3: E = D->propimpl_end(); I!=E; ++I) {
205 :
206 : // We can only check the synthesized properties
0: branch 2 not taken
10: branch 3 taken
207 10: if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
208 0: continue;
209 :
210 10: ObjCIvarDecl* ID = (*I)->getPropertyIvarDecl();
0: branch 0 not taken
10: branch 1 taken
211 10: if (!ID)
212 0: continue;
213 :
214 10: QualType T = ID->getType();
0: branch 2 not taken
10: branch 3 taken
215 10: if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars
216 0: continue;
217 :
218 10: const ObjCPropertyDecl* PD = (*I)->getPropertyDecl();
0: branch 0 not taken
10: branch 1 taken
219 10: if (!PD)
220 0: continue;
221 :
222 : // ivars cannot be set via read-only properties, so we'll skip them
1: branch 1 taken
9: branch 2 taken
223 10: if (PD->isReadOnly())
224 1: continue;
225 :
226 : // ivar must be released if and only if the kind of setter was not 'assign'
227 9: bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign;
3: branch 2 taken
6: branch 3 taken
228 9: if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx)
229 : != requiresRelease) {
230 : const char *name;
231 3: const char* category = "Memory (Core Foundation/Objective-C)";
232 :
233 3: std::string buf;
234 3: llvm::raw_string_ostream os(buf);
235 :
2: branch 0 taken
1: branch 1 taken
236 3: if (requiresRelease) {
237 : name = LOpts.getGCMode() == LangOptions::NonGC
238 : ? "missing ivar release (leak)"
2: branch 1 taken
0: branch 2 not taken
239 2: : "missing ivar release (Hybrid MM, non-GC)";
240 :
241 : os << "The '" << ID->getNameAsString()
242 : << "' instance variable was retained by a synthesized property but "
243 2: "wasn't released in 'dealloc'";
244 : } else {
245 : name = LOpts.getGCMode() == LangOptions::NonGC
246 : ? "extra ivar release (use-after-release)"
1: branch 1 taken
0: branch 2 not taken
247 1: : "extra ivar release (Hybrid MM, non-GC)";
248 :
249 : os << "The '" << ID->getNameAsString()
250 : << "' instance variable was not retained by a synthesized property "
251 1: "but was released in 'dealloc'";
252 : }
253 :
254 3: BR.EmitBasicReport(name, category, os.str(), (*I)->getLocation());
255 : }
256 : }
257 : }
258 :
Generated: 2010-02-10 01:31 by zcov