/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- TransRetainReleaseDealloc.cpp - Transformations to ARC mode ------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // removeRetainReleaseDealloc: |
10 | | // |
11 | | // Removes retain/release/autorelease/dealloc messages. |
12 | | // |
13 | | // return [[foo retain] autorelease]; |
14 | | // ----> |
15 | | // return foo; |
16 | | // |
17 | | //===----------------------------------------------------------------------===// |
18 | | |
19 | | #include "Transforms.h" |
20 | | #include "Internals.h" |
21 | | #include "clang/AST/ASTContext.h" |
22 | | #include "clang/AST/ParentMap.h" |
23 | | #include "clang/Basic/SourceManager.h" |
24 | | #include "clang/Lex/Lexer.h" |
25 | | #include "clang/Sema/SemaDiagnostic.h" |
26 | | #include "llvm/ADT/StringSwitch.h" |
27 | | |
28 | | using namespace clang; |
29 | | using namespace arcmt; |
30 | | using namespace trans; |
31 | | |
32 | | namespace { |
33 | | |
34 | | class RetainReleaseDeallocRemover : |
35 | | public RecursiveASTVisitor<RetainReleaseDeallocRemover> { |
36 | | Stmt *Body; |
37 | | MigrationPass &Pass; |
38 | | |
39 | | ExprSet Removables; |
40 | | std::unique_ptr<ParentMap> StmtMap; |
41 | | |
42 | | Selector DelegateSel, FinalizeSel; |
43 | | |
44 | | public: |
45 | | RetainReleaseDeallocRemover(MigrationPass &pass) |
46 | 403 | : Body(nullptr), Pass(pass) { |
47 | 403 | DelegateSel = |
48 | 403 | Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate")); |
49 | 403 | FinalizeSel = |
50 | 403 | Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize")); |
51 | 403 | } |
52 | | |
53 | 403 | void transformBody(Stmt *body, Decl *ParentD) { |
54 | 403 | Body = body; |
55 | 403 | collectRemovables(body, Removables); |
56 | 403 | StmtMap.reset(new ParentMap(body)); |
57 | 403 | TraverseStmt(body); |
58 | 403 | } |
59 | | |
60 | 495 | bool VisitObjCMessageExpr(ObjCMessageExpr *E) { |
61 | 495 | switch (E->getMethodFamily()) { |
62 | 270 | default: |
63 | 270 | if (E->isInstanceMessage() && E->getSelector() == FinalizeSel192 ) |
64 | 0 | break; |
65 | 270 | return true; |
66 | 25 | case OMF_autorelease: |
67 | 25 | if (isRemovable(E)) { |
68 | 14 | if (!isCommonUnusedAutorelease(E)) { |
69 | | // An unused autorelease is badness. If we remove it the receiver |
70 | | // will likely die immediately while previously it was kept alive |
71 | | // by the autorelease pool. This is bad practice in general, leave it |
72 | | // and emit an error to force the user to restructure their code. |
73 | 2 | Pass.TA.reportError( |
74 | 2 | "it is not safe to remove an unused 'autorelease' " |
75 | 2 | "message; its receiver may be destroyed immediately", |
76 | 2 | E->getBeginLoc(), E->getSourceRange()); |
77 | 2 | return true; |
78 | 2 | } |
79 | 23 | } |
80 | | // Pass through. |
81 | 23 | LLVM_FALLTHROUGH; |
82 | 98 | case OMF_retain: |
83 | 216 | case OMF_release: |
84 | 216 | if (E->getReceiverKind() == ObjCMessageExpr::Instance) |
85 | 216 | if (Expr *rec = E->getInstanceReceiver()) { |
86 | 216 | rec = rec->IgnoreParenImpCasts(); |
87 | 216 | if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone && |
88 | 4 | (E->getMethodFamily() != OMF_retain || isRemovable(E))) { |
89 | 2 | std::string err = "it is not safe to remove '"; |
90 | 2 | err += E->getSelector().getAsString() + "' message on " |
91 | 2 | "an __unsafe_unretained type"; |
92 | 2 | Pass.TA.reportError(err, rec->getBeginLoc()); |
93 | 2 | return true; |
94 | 2 | } |
95 | | |
96 | 214 | if (isGlobalVar(rec) && |
97 | 3 | (E->getMethodFamily() != OMF_retain || isRemovable(E)2 )) { |
98 | 3 | std::string err = "it is not safe to remove '"; |
99 | 3 | err += E->getSelector().getAsString() + "' message on " |
100 | 3 | "a global variable"; |
101 | 3 | Pass.TA.reportError(err, rec->getBeginLoc()); |
102 | 3 | return true; |
103 | 3 | } |
104 | | |
105 | 211 | if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)117 ) { |
106 | 2 | Pass.TA.reportError( |
107 | 2 | "it is not safe to remove 'retain' " |
108 | 2 | "message on the result of a 'delegate' message; " |
109 | 2 | "the object that was passed to 'setDelegate:' may not be " |
110 | 2 | "properly retained", |
111 | 2 | rec->getBeginLoc()); |
112 | 2 | return true; |
113 | 2 | } |
114 | 209 | } |
115 | 209 | break; |
116 | 7 | case OMF_dealloc: |
117 | 7 | break; |
118 | 216 | } |
119 | | |
120 | 216 | switch (E->getReceiverKind()) { |
121 | 0 | default: |
122 | 0 | return true; |
123 | 4 | case ObjCMessageExpr::SuperInstance: { |
124 | 4 | Transaction Trans(Pass.TA); |
125 | 4 | clearDiagnostics(E->getSelectorLoc(0)); |
126 | 4 | if (tryRemoving(E)) |
127 | 4 | return true; |
128 | 0 | Pass.TA.replace(E->getSourceRange(), "self"); |
129 | 0 | return true; |
130 | 0 | } |
131 | 212 | case ObjCMessageExpr::Instance: |
132 | 212 | break; |
133 | 212 | } |
134 | | |
135 | 212 | Expr *rec = E->getInstanceReceiver(); |
136 | 212 | if (!rec) return true0 ; |
137 | | |
138 | 212 | Transaction Trans(Pass.TA); |
139 | 212 | clearDiagnostics(E->getSelectorLoc(0)); |
140 | | |
141 | 212 | ObjCMessageExpr *Msg = E; |
142 | 212 | Expr *RecContainer = Msg; |
143 | 212 | SourceRange RecRange = rec->getSourceRange(); |
144 | 212 | checkForGCDOrXPC(Msg, RecContainer, rec, RecRange); |
145 | | |
146 | 212 | if (Msg->getMethodFamily() == OMF_release && |
147 | 115 | isRemovable(RecContainer) && isInAtFinally(RecContainer)107 ) { |
148 | | // Change the -release to "receiver = nil" in a finally to avoid a leak |
149 | | // when an exception is thrown. |
150 | 2 | Pass.TA.replace(RecContainer->getSourceRange(), RecRange); |
151 | 2 | std::string str = " = "; |
152 | 2 | str += getNilString(Pass); |
153 | 2 | Pass.TA.insertAfterToken(RecRange.getEnd(), str); |
154 | 2 | return true; |
155 | 2 | } |
156 | | |
157 | 210 | if (hasSideEffects(rec, Pass.Ctx) || !tryRemoving(RecContainer)179 ) |
158 | 75 | Pass.TA.replace(RecContainer->getSourceRange(), RecRange); |
159 | | |
160 | 210 | return true; |
161 | 210 | } |
162 | | |
163 | | private: |
164 | | /// Checks for idioms where an unused -autorelease is common. |
165 | | /// |
166 | | /// Returns true for this idiom which is common in property |
167 | | /// setters: |
168 | | /// |
169 | | /// [backingValue autorelease]; |
170 | | /// backingValue = [newValue retain]; // in general a +1 assign |
171 | | /// |
172 | | /// For these as well: |
173 | | /// |
174 | | /// [[var retain] autorelease]; |
175 | | /// return var; |
176 | | /// |
177 | 14 | bool isCommonUnusedAutorelease(ObjCMessageExpr *E) { |
178 | 14 | return isPlusOneAssignBeforeOrAfterAutorelease(E) || |
179 | 4 | isReturnedAfterAutorelease(E); |
180 | 14 | } |
181 | | |
182 | 4 | bool isReturnedAfterAutorelease(ObjCMessageExpr *E) { |
183 | 4 | Expr *Rec = E->getInstanceReceiver(); |
184 | 4 | if (!Rec) |
185 | 0 | return false; |
186 | | |
187 | 4 | Decl *RefD = getReferencedDecl(Rec); |
188 | 4 | if (!RefD) |
189 | 0 | return false; |
190 | | |
191 | 4 | Stmt *nextStmt = getNextStmt(E); |
192 | 4 | if (!nextStmt) |
193 | 0 | return false; |
194 | | |
195 | | // Check for "return <variable>;". |
196 | | |
197 | 4 | if (ReturnStmt *RetS = dyn_cast<ReturnStmt>(nextStmt)) |
198 | 2 | return RefD == getReferencedDecl(RetS->getRetValue()); |
199 | | |
200 | 2 | return false; |
201 | 2 | } |
202 | | |
203 | 14 | bool isPlusOneAssignBeforeOrAfterAutorelease(ObjCMessageExpr *E) { |
204 | 14 | Expr *Rec = E->getInstanceReceiver(); |
205 | 14 | if (!Rec) |
206 | 0 | return false; |
207 | | |
208 | 14 | Decl *RefD = getReferencedDecl(Rec); |
209 | 14 | if (!RefD) |
210 | 0 | return false; |
211 | | |
212 | 14 | Stmt *prevStmt, *nextStmt; |
213 | 14 | std::tie(prevStmt, nextStmt) = getPreviousAndNextStmt(E); |
214 | | |
215 | 14 | return isPlusOneAssignToVar(prevStmt, RefD) || |
216 | 12 | isPlusOneAssignToVar(nextStmt, RefD); |
217 | 14 | } |
218 | | |
219 | 26 | bool isPlusOneAssignToVar(Stmt *S, Decl *RefD) { |
220 | 26 | if (!S) |
221 | 10 | return false; |
222 | | |
223 | | // Check for "RefD = [+1 retained object];". |
224 | | |
225 | 16 | if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(S)) { |
226 | 9 | return (RefD == getReferencedDecl(Bop->getLHS())) && isPlusOneAssign(Bop); |
227 | 9 | } |
228 | | |
229 | 7 | if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) { |
230 | 2 | if (DS->isSingleDecl() && DS->getSingleDecl() == RefD) { |
231 | 2 | if (VarDecl *VD = dyn_cast<VarDecl>(RefD)) |
232 | 2 | return isPlusOne(VD->getInit()); |
233 | 0 | } |
234 | 0 | return false; |
235 | 0 | } |
236 | | |
237 | 5 | return false; |
238 | 5 | } |
239 | | |
240 | 4 | Stmt *getNextStmt(Expr *E) { |
241 | 4 | return getPreviousAndNextStmt(E).second; |
242 | 4 | } |
243 | | |
244 | 18 | std::pair<Stmt *, Stmt *> getPreviousAndNextStmt(Expr *E) { |
245 | 18 | Stmt *prevStmt = nullptr, *nextStmt = nullptr; |
246 | 18 | if (!E) |
247 | 0 | return std::make_pair(prevStmt, nextStmt); |
248 | | |
249 | 18 | Stmt *OuterS = E, *InnerS; |
250 | 54 | do { |
251 | 54 | InnerS = OuterS; |
252 | 54 | OuterS = StmtMap->getParent(InnerS); |
253 | 54 | } |
254 | 54 | while (OuterS && (isa<ParenExpr>(OuterS) || |
255 | 54 | isa<CastExpr>(OuterS) || |
256 | 36 | isa<FullExpr>(OuterS))); |
257 | | |
258 | 18 | if (!OuterS) |
259 | 0 | return std::make_pair(prevStmt, nextStmt); |
260 | | |
261 | 18 | Stmt::child_iterator currChildS = OuterS->child_begin(); |
262 | 18 | Stmt::child_iterator childE = OuterS->child_end(); |
263 | 18 | Stmt::child_iterator prevChildS = childE; |
264 | 62 | for (; currChildS != childE; ++currChildS44 ) { |
265 | 62 | if (*currChildS == InnerS) |
266 | 18 | break; |
267 | 44 | prevChildS = currChildS; |
268 | 44 | } |
269 | | |
270 | 18 | if (prevChildS != childE) { |
271 | 6 | prevStmt = *prevChildS; |
272 | 6 | if (auto *E = dyn_cast_or_null<Expr>(prevStmt)) |
273 | 4 | prevStmt = E->IgnoreImplicit(); |
274 | 6 | } |
275 | | |
276 | 18 | if (currChildS == childE) |
277 | 0 | return std::make_pair(prevStmt, nextStmt); |
278 | 18 | ++currChildS; |
279 | 18 | if (currChildS == childE) |
280 | 2 | return std::make_pair(prevStmt, nextStmt); |
281 | | |
282 | 16 | nextStmt = *currChildS; |
283 | 16 | if (auto *E = dyn_cast_or_null<Expr>(nextStmt)) |
284 | 12 | nextStmt = E->IgnoreImplicit(); |
285 | | |
286 | 16 | return std::make_pair(prevStmt, nextStmt); |
287 | 16 | } |
288 | | |
289 | 33 | Decl *getReferencedDecl(Expr *E) { |
290 | 33 | if (!E) |
291 | 0 | return nullptr; |
292 | | |
293 | 33 | E = E->IgnoreParenCasts(); |
294 | 33 | if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) { |
295 | 4 | switch (ME->getMethodFamily()) { |
296 | 0 | case OMF_copy: |
297 | 0 | case OMF_autorelease: |
298 | 0 | case OMF_release: |
299 | 4 | case OMF_retain: |
300 | 4 | return getReferencedDecl(ME->getInstanceReceiver()); |
301 | 0 | default: |
302 | 0 | return nullptr; |
303 | 29 | } |
304 | 29 | } |
305 | 29 | if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) |
306 | 17 | return DRE->getDecl(); |
307 | 12 | if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) |
308 | 0 | return ME->getMemberDecl(); |
309 | 12 | if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(E)) |
310 | 12 | return IRE->getDecl(); |
311 | | |
312 | 0 | return nullptr; |
313 | 0 | } |
314 | | |
315 | | /// Check if the retain/release is due to a GCD/XPC macro that are |
316 | | /// defined as: |
317 | | /// |
318 | | /// #define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; }) |
319 | | /// #define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; }) |
320 | | /// #define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; }) |
321 | | /// #define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; }) |
322 | | /// |
323 | | /// and return the top container which is the StmtExpr and the macro argument |
324 | | /// expression. |
325 | | void checkForGCDOrXPC(ObjCMessageExpr *Msg, Expr *&RecContainer, |
326 | 212 | Expr *&Rec, SourceRange &RecRange) { |
327 | 212 | SourceLocation Loc = Msg->getExprLoc(); |
328 | 212 | if (!Loc.isMacroID()) |
329 | 191 | return; |
330 | 21 | SourceManager &SM = Pass.Ctx.getSourceManager(); |
331 | 21 | StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM, |
332 | 21 | Pass.Ctx.getLangOpts()); |
333 | 21 | bool isGCDOrXPC = llvm::StringSwitch<bool>(MacroName) |
334 | 21 | .Case("dispatch_retain", true) |
335 | 21 | .Case("dispatch_release", true) |
336 | 21 | .Case("xpc_retain", true) |
337 | 21 | .Case("xpc_release", true) |
338 | 21 | .Default(false); |
339 | 21 | if (!isGCDOrXPC) |
340 | 9 | return; |
341 | | |
342 | 12 | StmtExpr *StmtE = nullptr; |
343 | 12 | Stmt *S = Msg; |
344 | 60 | while (S) { |
345 | 60 | if (StmtExpr *SE = dyn_cast<StmtExpr>(S)) { |
346 | 12 | StmtE = SE; |
347 | 12 | break; |
348 | 12 | } |
349 | 48 | S = StmtMap->getParent(S); |
350 | 48 | } |
351 | | |
352 | 12 | if (!StmtE) |
353 | 0 | return; |
354 | | |
355 | 12 | Stmt::child_range StmtExprChild = StmtE->children(); |
356 | 12 | if (StmtExprChild.begin() == StmtExprChild.end()) |
357 | 0 | return; |
358 | 12 | auto *CompS = dyn_cast_or_null<CompoundStmt>(*StmtExprChild.begin()); |
359 | 12 | if (!CompS) |
360 | 0 | return; |
361 | | |
362 | 12 | Stmt::child_range CompStmtChild = CompS->children(); |
363 | 12 | if (CompStmtChild.begin() == CompStmtChild.end()) |
364 | 0 | return; |
365 | 12 | auto *DeclS = dyn_cast_or_null<DeclStmt>(*CompStmtChild.begin()); |
366 | 12 | if (!DeclS) |
367 | 0 | return; |
368 | 12 | if (!DeclS->isSingleDecl()) |
369 | 0 | return; |
370 | 12 | VarDecl *VD = dyn_cast_or_null<VarDecl>(DeclS->getSingleDecl()); |
371 | 12 | if (!VD) |
372 | 0 | return; |
373 | 12 | Expr *Init = VD->getInit(); |
374 | 12 | if (!Init) |
375 | 0 | return; |
376 | | |
377 | 12 | RecContainer = StmtE; |
378 | 12 | Rec = Init->IgnoreParenImpCasts(); |
379 | 12 | if (FullExpr *FE = dyn_cast<FullExpr>(Rec)) |
380 | 0 | Rec = FE->getSubExpr()->IgnoreParenImpCasts(); |
381 | 12 | RecRange = Rec->getSourceRange(); |
382 | 12 | if (SM.isMacroArgExpansion(RecRange.getBegin())) |
383 | 12 | RecRange.setBegin(SM.getImmediateSpellingLoc(RecRange.getBegin())); |
384 | 12 | if (SM.isMacroArgExpansion(RecRange.getEnd())) |
385 | 12 | RecRange.setEnd(SM.getImmediateSpellingLoc(RecRange.getEnd())); |
386 | 12 | } |
387 | | |
388 | 216 | void clearDiagnostics(SourceLocation loc) const { |
389 | 216 | Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message, |
390 | 216 | diag::err_unavailable, |
391 | 216 | diag::err_unavailable_message, |
392 | 216 | loc); |
393 | 216 | } |
394 | | |
395 | 117 | bool isDelegateMessage(Expr *E) const { |
396 | 117 | if (!E) return false0 ; |
397 | | |
398 | 117 | E = E->IgnoreParenCasts(); |
399 | | |
400 | | // Also look through property-getter sugar. |
401 | 117 | if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(E)) |
402 | 1 | E = pseudoOp->getResultExpr()->IgnoreImplicit(); |
403 | | |
404 | 117 | if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) |
405 | 10 | return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel); |
406 | | |
407 | 107 | return false; |
408 | 107 | } |
409 | | |
410 | 107 | bool isInAtFinally(Expr *E) const { |
411 | 107 | assert(E); |
412 | 107 | Stmt *S = E; |
413 | 377 | while (S) { |
414 | 272 | if (isa<ObjCAtFinallyStmt>(S)) |
415 | 2 | return true; |
416 | 270 | S = StmtMap->getParent(S); |
417 | 270 | } |
418 | | |
419 | 105 | return false; |
420 | 107 | } |
421 | | |
422 | 405 | bool isRemovable(Expr *E) const { |
423 | 405 | return Removables.count(E); |
424 | 405 | } |
425 | | |
426 | 255 | bool tryRemoving(Expr *E) const { |
427 | 255 | if (isRemovable(E)) { |
428 | 135 | Pass.TA.removeStmt(E); |
429 | 135 | return true; |
430 | 135 | } |
431 | | |
432 | 120 | Stmt *parent = StmtMap->getParent(E); |
433 | | |
434 | 120 | if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent)) |
435 | 54 | return tryRemoving(castE); |
436 | | |
437 | 66 | if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent)) |
438 | 18 | return tryRemoving(parenE); |
439 | | |
440 | 48 | if (BinaryOperator * |
441 | 48 | bopE = dyn_cast_or_null<BinaryOperator>(parent)) { |
442 | 18 | if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E4 && |
443 | 4 | isRemovable(bopE)) { |
444 | 4 | Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange()); |
445 | 4 | return true; |
446 | 4 | } |
447 | 44 | } |
448 | | |
449 | 44 | return false; |
450 | 44 | } |
451 | | |
452 | | }; |
453 | | |
454 | | } // anonymous namespace |
455 | | |
456 | 85 | void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) { |
457 | 85 | BodyTransform<RetainReleaseDeallocRemover> trans(pass); |
458 | 85 | trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
459 | 85 | } |