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 | : Body(nullptr), Pass(pass) { |
47 | DelegateSel = |
48 | Pass.Ctx.Selectors.getNullarySelector(ID: &Pass.Ctx.Idents.get(Name: "delegate" )); |
49 | FinalizeSel = |
50 | Pass.Ctx.Selectors.getNullarySelector(ID: &Pass.Ctx.Idents.get(Name: "finalize" )); |
51 | } |
52 | |
53 | void transformBody(Stmt *body, Decl *ParentD) { |
54 | Body = body; |
55 | collectRemovables(S: body, exprs&: Removables); |
56 | StmtMap.reset(p: new ParentMap(body)); |
57 | TraverseStmt(S: body); |
58 | } |
59 | |
60 | bool VisitObjCMessageExpr(ObjCMessageExpr *E) { |
61 | switch (E->getMethodFamily()) { |
62 | default: |
63 | if (E->isInstanceMessage() && E->getSelector() == FinalizeSel) |
64 | break; |
65 | return true; |
66 | case OMF_autorelease: |
67 | if (isRemovable(E)) { |
68 | 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 | Pass.TA.reportError( |
74 | error: "it is not safe to remove an unused 'autorelease' " |
75 | "message; its receiver may be destroyed immediately" , |
76 | loc: E->getBeginLoc(), range: E->getSourceRange()); |
77 | return true; |
78 | } |
79 | } |
80 | // Pass through. |
81 | [[fallthrough]]; |
82 | case OMF_retain: |
83 | case OMF_release: |
84 | if (E->getReceiverKind() == ObjCMessageExpr::Instance) |
85 | if (Expr *rec = E->getInstanceReceiver()) { |
86 | rec = rec->IgnoreParenImpCasts(); |
87 | if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone && |
88 | (E->getMethodFamily() != OMF_retain || isRemovable(E))) { |
89 | std::string err = "it is not safe to remove '" ; |
90 | err += E->getSelector().getAsString() + "' message on " |
91 | "an __unsafe_unretained type" ; |
92 | Pass.TA.reportError(error: err, loc: rec->getBeginLoc()); |
93 | return true; |
94 | } |
95 | |
96 | if (isGlobalVar(E: rec) && |
97 | (E->getMethodFamily() != OMF_retain || isRemovable(E))) { |
98 | std::string err = "it is not safe to remove '" ; |
99 | err += E->getSelector().getAsString() + "' message on " |
100 | "a global variable" ; |
101 | Pass.TA.reportError(error: err, loc: rec->getBeginLoc()); |
102 | return true; |
103 | } |
104 | |
105 | if (E->getMethodFamily() == OMF_release && isDelegateMessage(E: rec)) { |
106 | Pass.TA.reportError( |
107 | error: "it is not safe to remove 'retain' " |
108 | "message on the result of a 'delegate' message; " |
109 | "the object that was passed to 'setDelegate:' may not be " |
110 | "properly retained" , |
111 | loc: rec->getBeginLoc()); |
112 | return true; |
113 | } |
114 | } |
115 | break; |
116 | case OMF_dealloc: |
117 | break; |
118 | } |
119 | |
120 | switch (E->getReceiverKind()) { |
121 | default: |
122 | return true; |
123 | case ObjCMessageExpr::SuperInstance: { |
124 | Transaction Trans(Pass.TA); |
125 | clearDiagnostics(loc: E->getSelectorLoc(Index: 0)); |
126 | if (tryRemoving(E)) |
127 | return true; |
128 | Pass.TA.replace(range: E->getSourceRange(), text: "self" ); |
129 | return true; |
130 | } |
131 | case ObjCMessageExpr::Instance: |
132 | break; |
133 | } |
134 | |
135 | Expr *rec = E->getInstanceReceiver(); |
136 | if (!rec) return true; |
137 | |
138 | Transaction Trans(Pass.TA); |
139 | clearDiagnostics(loc: E->getSelectorLoc(Index: 0)); |
140 | |
141 | ObjCMessageExpr *Msg = E; |
142 | Expr *RecContainer = Msg; |
143 | SourceRange RecRange = rec->getSourceRange(); |
144 | checkForGCDOrXPC(Msg, RecContainer, Rec&: rec, RecRange); |
145 | |
146 | if (Msg->getMethodFamily() == OMF_release && |
147 | isRemovable(E: RecContainer) && isInAtFinally(E: RecContainer)) { |
148 | // Change the -release to "receiver = nil" in a finally to avoid a leak |
149 | // when an exception is thrown. |
150 | Pass.TA.replace(range: RecContainer->getSourceRange(), replacementRange: RecRange); |
151 | std::string str = " = " ; |
152 | str += getNilString(Pass); |
153 | Pass.TA.insertAfterToken(loc: RecRange.getEnd(), text: str); |
154 | return true; |
155 | } |
156 | |
157 | if (hasSideEffects(E: rec, Ctx&: Pass.Ctx) || !tryRemoving(E: RecContainer)) |
158 | Pass.TA.replace(range: RecContainer->getSourceRange(), replacementRange: RecRange); |
159 | |
160 | return true; |
161 | } |
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 | bool isCommonUnusedAutorelease(ObjCMessageExpr *E) { |
178 | return isPlusOneAssignBeforeOrAfterAutorelease(E) || |
179 | isReturnedAfterAutorelease(E); |
180 | } |
181 | |
182 | bool isReturnedAfterAutorelease(ObjCMessageExpr *E) { |
183 | Expr *Rec = E->getInstanceReceiver(); |
184 | if (!Rec) |
185 | return false; |
186 | |
187 | Decl *RefD = getReferencedDecl(E: Rec); |
188 | if (!RefD) |
189 | return false; |
190 | |
191 | Stmt *nextStmt = getNextStmt(E); |
192 | if (!nextStmt) |
193 | return false; |
194 | |
195 | // Check for "return <variable>;". |
196 | |
197 | if (ReturnStmt *RetS = dyn_cast<ReturnStmt>(Val: nextStmt)) |
198 | return RefD == getReferencedDecl(E: RetS->getRetValue()); |
199 | |
200 | return false; |
201 | } |
202 | |
203 | bool isPlusOneAssignBeforeOrAfterAutorelease(ObjCMessageExpr *E) { |
204 | Expr *Rec = E->getInstanceReceiver(); |
205 | if (!Rec) |
206 | return false; |
207 | |
208 | Decl *RefD = getReferencedDecl(E: Rec); |
209 | if (!RefD) |
210 | return false; |
211 | |
212 | Stmt *prevStmt, *nextStmt; |
213 | std::tie(args&: prevStmt, args&: nextStmt) = getPreviousAndNextStmt(E); |
214 | |
215 | return isPlusOneAssignToVar(S: prevStmt, RefD) || |
216 | isPlusOneAssignToVar(S: nextStmt, RefD); |
217 | } |
218 | |
219 | bool isPlusOneAssignToVar(Stmt *S, Decl *RefD) { |
220 | if (!S) |
221 | return false; |
222 | |
223 | // Check for "RefD = [+1 retained object];". |
224 | |
225 | if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(Val: S)) { |
226 | return (RefD == getReferencedDecl(E: Bop->getLHS())) && isPlusOneAssign(E: Bop); |
227 | } |
228 | |
229 | if (DeclStmt *DS = dyn_cast<DeclStmt>(Val: S)) { |
230 | if (DS->isSingleDecl() && DS->getSingleDecl() == RefD) { |
231 | if (VarDecl *VD = dyn_cast<VarDecl>(Val: RefD)) |
232 | return isPlusOne(E: VD->getInit()); |
233 | } |
234 | return false; |
235 | } |
236 | |
237 | return false; |
238 | } |
239 | |
240 | Stmt *getNextStmt(Expr *E) { |
241 | return getPreviousAndNextStmt(E).second; |
242 | } |
243 | |
244 | std::pair<Stmt *, Stmt *> getPreviousAndNextStmt(Expr *E) { |
245 | Stmt *prevStmt = nullptr, *nextStmt = nullptr; |
246 | if (!E) |
247 | return std::make_pair(x&: prevStmt, y&: nextStmt); |
248 | |
249 | Stmt *OuterS = E, *InnerS; |
250 | do { |
251 | InnerS = OuterS; |
252 | OuterS = StmtMap->getParent(InnerS); |
253 | } |
254 | while (OuterS && (isa<ParenExpr>(Val: OuterS) || |
255 | isa<CastExpr>(Val: OuterS) || |
256 | isa<FullExpr>(Val: OuterS))); |
257 | |
258 | if (!OuterS) |
259 | return std::make_pair(x&: prevStmt, y&: nextStmt); |
260 | |
261 | Stmt::child_iterator currChildS = OuterS->child_begin(); |
262 | Stmt::child_iterator childE = OuterS->child_end(); |
263 | Stmt::child_iterator prevChildS = childE; |
264 | for (; currChildS != childE; ++currChildS) { |
265 | if (*currChildS == InnerS) |
266 | break; |
267 | prevChildS = currChildS; |
268 | } |
269 | |
270 | if (prevChildS != childE) { |
271 | prevStmt = *prevChildS; |
272 | if (auto *E = dyn_cast_or_null<Expr>(Val: prevStmt)) |
273 | prevStmt = E->IgnoreImplicit(); |
274 | } |
275 | |
276 | if (currChildS == childE) |
277 | return std::make_pair(x&: prevStmt, y&: nextStmt); |
278 | ++currChildS; |
279 | if (currChildS == childE) |
280 | return std::make_pair(x&: prevStmt, y&: nextStmt); |
281 | |
282 | nextStmt = *currChildS; |
283 | if (auto *E = dyn_cast_or_null<Expr>(Val: nextStmt)) |
284 | nextStmt = E->IgnoreImplicit(); |
285 | |
286 | return std::make_pair(x&: prevStmt, y&: nextStmt); |
287 | } |
288 | |
289 | Decl *getReferencedDecl(Expr *E) { |
290 | if (!E) |
291 | return nullptr; |
292 | |
293 | E = E->IgnoreParenCasts(); |
294 | if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(Val: E)) { |
295 | switch (ME->getMethodFamily()) { |
296 | case OMF_copy: |
297 | case OMF_autorelease: |
298 | case OMF_release: |
299 | case OMF_retain: |
300 | return getReferencedDecl(E: ME->getInstanceReceiver()); |
301 | default: |
302 | return nullptr; |
303 | } |
304 | } |
305 | if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Val: E)) |
306 | return DRE->getDecl(); |
307 | if (MemberExpr *ME = dyn_cast<MemberExpr>(Val: E)) |
308 | return ME->getMemberDecl(); |
309 | if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(Val: E)) |
310 | return IRE->getDecl(); |
311 | |
312 | return nullptr; |
313 | } |
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 | Expr *&Rec, SourceRange &RecRange) { |
327 | SourceLocation Loc = Msg->getExprLoc(); |
328 | if (!Loc.isMacroID()) |
329 | return; |
330 | SourceManager &SM = Pass.Ctx.getSourceManager(); |
331 | StringRef MacroName = Lexer::getImmediateMacroName(Loc, SM, |
332 | LangOpts: Pass.Ctx.getLangOpts()); |
333 | bool isGCDOrXPC = llvm::StringSwitch<bool>(MacroName) |
334 | .Case(S: "dispatch_retain" , Value: true) |
335 | .Case(S: "dispatch_release" , Value: true) |
336 | .Case(S: "xpc_retain" , Value: true) |
337 | .Case(S: "xpc_release" , Value: true) |
338 | .Default(Value: false); |
339 | if (!isGCDOrXPC) |
340 | return; |
341 | |
342 | StmtExpr *StmtE = nullptr; |
343 | Stmt *S = Msg; |
344 | while (S) { |
345 | if (StmtExpr *SE = dyn_cast<StmtExpr>(Val: S)) { |
346 | StmtE = SE; |
347 | break; |
348 | } |
349 | S = StmtMap->getParent(S); |
350 | } |
351 | |
352 | if (!StmtE) |
353 | return; |
354 | |
355 | Stmt::child_range StmtExprChild = StmtE->children(); |
356 | if (StmtExprChild.begin() == StmtExprChild.end()) |
357 | return; |
358 | auto *CompS = dyn_cast_or_null<CompoundStmt>(Val: *StmtExprChild.begin()); |
359 | if (!CompS) |
360 | return; |
361 | |
362 | Stmt::child_range CompStmtChild = CompS->children(); |
363 | if (CompStmtChild.begin() == CompStmtChild.end()) |
364 | return; |
365 | auto *DeclS = dyn_cast_or_null<DeclStmt>(Val: *CompStmtChild.begin()); |
366 | if (!DeclS) |
367 | return; |
368 | if (!DeclS->isSingleDecl()) |
369 | return; |
370 | VarDecl *VD = dyn_cast_or_null<VarDecl>(Val: DeclS->getSingleDecl()); |
371 | if (!VD) |
372 | return; |
373 | Expr *Init = VD->getInit(); |
374 | if (!Init) |
375 | return; |
376 | |
377 | RecContainer = StmtE; |
378 | Rec = Init->IgnoreParenImpCasts(); |
379 | if (FullExpr *FE = dyn_cast<FullExpr>(Val: Rec)) |
380 | Rec = FE->getSubExpr()->IgnoreParenImpCasts(); |
381 | RecRange = Rec->getSourceRange(); |
382 | if (SM.isMacroArgExpansion(Loc: RecRange.getBegin())) |
383 | RecRange.setBegin(SM.getImmediateSpellingLoc(Loc: RecRange.getBegin())); |
384 | if (SM.isMacroArgExpansion(Loc: RecRange.getEnd())) |
385 | RecRange.setEnd(SM.getImmediateSpellingLoc(Loc: RecRange.getEnd())); |
386 | } |
387 | |
388 | void clearDiagnostics(SourceLocation loc) const { |
389 | Pass.TA.clearDiagnostic(ID1: diag::err_arc_illegal_explicit_message, |
390 | ID2: diag::err_unavailable, |
391 | ID3: diag::err_unavailable_message, |
392 | range: loc); |
393 | } |
394 | |
395 | bool isDelegateMessage(Expr *E) const { |
396 | if (!E) return false; |
397 | |
398 | E = E->IgnoreParenCasts(); |
399 | |
400 | // Also look through property-getter sugar. |
401 | if (PseudoObjectExpr *pseudoOp = dyn_cast<PseudoObjectExpr>(Val: E)) |
402 | E = pseudoOp->getResultExpr()->IgnoreImplicit(); |
403 | |
404 | if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(Val: E)) |
405 | return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel); |
406 | |
407 | return false; |
408 | } |
409 | |
410 | bool isInAtFinally(Expr *E) const { |
411 | assert(E); |
412 | Stmt *S = E; |
413 | while (S) { |
414 | if (isa<ObjCAtFinallyStmt>(Val: S)) |
415 | return true; |
416 | S = StmtMap->getParent(S); |
417 | } |
418 | |
419 | return false; |
420 | } |
421 | |
422 | bool isRemovable(Expr *E) const { |
423 | return Removables.count(V: E); |
424 | } |
425 | |
426 | bool tryRemoving(Expr *E) const { |
427 | if (isRemovable(E)) { |
428 | Pass.TA.removeStmt(S: E); |
429 | return true; |
430 | } |
431 | |
432 | Stmt *parent = StmtMap->getParent(E); |
433 | |
434 | if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(Val: parent)) |
435 | return tryRemoving(E: castE); |
436 | |
437 | if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(Val: parent)) |
438 | return tryRemoving(E: parenE); |
439 | |
440 | if (BinaryOperator * |
441 | bopE = dyn_cast_or_null<BinaryOperator>(Val: parent)) { |
442 | if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E && |
443 | isRemovable(E: bopE)) { |
444 | Pass.TA.replace(range: bopE->getSourceRange(), replacementRange: bopE->getRHS()->getSourceRange()); |
445 | return true; |
446 | } |
447 | } |
448 | |
449 | return false; |
450 | } |
451 | |
452 | }; |
453 | |
454 | } // anonymous namespace |
455 | |
456 | void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) { |
457 | BodyTransform<RetainReleaseDeallocRemover> trans(pass); |
458 | trans.TraverseDecl(D: pass.Ctx.getTranslationUnitDecl()); |
459 | } |
460 | |