xref: /freebsd/contrib/llvm-project/clang/lib/ARCMigrate/TransRetainReleaseDealloc.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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:
RetainReleaseDeallocRemover(MigrationPass & pass)45   RetainReleaseDeallocRemover(MigrationPass &pass)
46     : Body(nullptr), Pass(pass) {
47     DelegateSel =
48         Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate"));
49     FinalizeSel =
50         Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("finalize"));
51   }
52 
transformBody(Stmt * body,Decl * ParentD)53   void transformBody(Stmt *body, Decl *ParentD) {
54     Body = body;
55     collectRemovables(body, Removables);
56     StmtMap.reset(new ParentMap(body));
57     TraverseStmt(body);
58   }
59 
VisitObjCMessageExpr(ObjCMessageExpr * E)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               "it is not safe to remove an unused 'autorelease' "
75               "message; its receiver may be destroyed immediately",
76               E->getBeginLoc(), 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(err, rec->getBeginLoc());
93             return true;
94           }
95 
96           if (isGlobalVar(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(err, rec->getBeginLoc());
102             return true;
103           }
104 
105           if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) {
106             Pass.TA.reportError(
107                 "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                 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(E->getSelectorLoc(0));
126       if (tryRemoving(E))
127         return true;
128       Pass.TA.replace(E->getSourceRange(), "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(E->getSelectorLoc(0));
140 
141     ObjCMessageExpr *Msg = E;
142     Expr *RecContainer = Msg;
143     SourceRange RecRange = rec->getSourceRange();
144     checkForGCDOrXPC(Msg, RecContainer, rec, RecRange);
145 
146     if (Msg->getMethodFamily() == OMF_release &&
147         isRemovable(RecContainer) && isInAtFinally(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(RecContainer->getSourceRange(), RecRange);
151       std::string str = " = ";
152       str += getNilString(Pass);
153       Pass.TA.insertAfterToken(RecRange.getEnd(), str);
154       return true;
155     }
156 
157     if (hasSideEffects(rec, Pass.Ctx) || !tryRemoving(RecContainer))
158       Pass.TA.replace(RecContainer->getSourceRange(), 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   ///
isCommonUnusedAutorelease(ObjCMessageExpr * E)177   bool isCommonUnusedAutorelease(ObjCMessageExpr *E) {
178     return isPlusOneAssignBeforeOrAfterAutorelease(E) ||
179            isReturnedAfterAutorelease(E);
180   }
181 
isReturnedAfterAutorelease(ObjCMessageExpr * E)182   bool isReturnedAfterAutorelease(ObjCMessageExpr *E) {
183     Expr *Rec = E->getInstanceReceiver();
184     if (!Rec)
185       return false;
186 
187     Decl *RefD = getReferencedDecl(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>(nextStmt))
198       return RefD == getReferencedDecl(RetS->getRetValue());
199 
200     return false;
201   }
202 
isPlusOneAssignBeforeOrAfterAutorelease(ObjCMessageExpr * E)203   bool isPlusOneAssignBeforeOrAfterAutorelease(ObjCMessageExpr *E) {
204     Expr *Rec = E->getInstanceReceiver();
205     if (!Rec)
206       return false;
207 
208     Decl *RefD = getReferencedDecl(Rec);
209     if (!RefD)
210       return false;
211 
212     Stmt *prevStmt, *nextStmt;
213     std::tie(prevStmt, nextStmt) = getPreviousAndNextStmt(E);
214 
215     return isPlusOneAssignToVar(prevStmt, RefD) ||
216            isPlusOneAssignToVar(nextStmt, RefD);
217   }
218 
isPlusOneAssignToVar(Stmt * S,Decl * RefD)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>(S)) {
226       return (RefD == getReferencedDecl(Bop->getLHS())) && isPlusOneAssign(Bop);
227     }
228 
229     if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
230       if (DS->isSingleDecl() && DS->getSingleDecl() == RefD) {
231         if (VarDecl *VD = dyn_cast<VarDecl>(RefD))
232           return isPlusOne(VD->getInit());
233       }
234       return false;
235     }
236 
237     return false;
238   }
239 
getNextStmt(Expr * E)240   Stmt *getNextStmt(Expr *E) {
241     return getPreviousAndNextStmt(E).second;
242   }
243 
getPreviousAndNextStmt(Expr * E)244   std::pair<Stmt *, Stmt *> getPreviousAndNextStmt(Expr *E) {
245     Stmt *prevStmt = nullptr, *nextStmt = nullptr;
246     if (!E)
247       return std::make_pair(prevStmt, nextStmt);
248 
249     Stmt *OuterS = E, *InnerS;
250     do {
251       InnerS = OuterS;
252       OuterS = StmtMap->getParent(InnerS);
253     }
254     while (OuterS && (isa<ParenExpr>(OuterS) ||
255                       isa<CastExpr>(OuterS) ||
256                       isa<FullExpr>(OuterS)));
257 
258     if (!OuterS)
259       return std::make_pair(prevStmt, 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>(prevStmt))
273         prevStmt = E->IgnoreImplicit();
274     }
275 
276     if (currChildS == childE)
277       return std::make_pair(prevStmt, nextStmt);
278     ++currChildS;
279     if (currChildS == childE)
280       return std::make_pair(prevStmt, nextStmt);
281 
282     nextStmt = *currChildS;
283     if (auto *E = dyn_cast_or_null<Expr>(nextStmt))
284       nextStmt = E->IgnoreImplicit();
285 
286     return std::make_pair(prevStmt, nextStmt);
287   }
288 
getReferencedDecl(Expr * E)289   Decl *getReferencedDecl(Expr *E) {
290     if (!E)
291       return nullptr;
292 
293     E = E->IgnoreParenCasts();
294     if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) {
295       switch (ME->getMethodFamily()) {
296       case OMF_copy:
297       case OMF_autorelease:
298       case OMF_release:
299       case OMF_retain:
300         return getReferencedDecl(ME->getInstanceReceiver());
301       default:
302         return nullptr;
303       }
304     }
305     if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
306       return DRE->getDecl();
307     if (MemberExpr *ME = dyn_cast<MemberExpr>(E))
308       return ME->getMemberDecl();
309     if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(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.
checkForGCDOrXPC(ObjCMessageExpr * Msg,Expr * & RecContainer,Expr * & Rec,SourceRange & RecRange)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                                                      Pass.Ctx.getLangOpts());
333     bool isGCDOrXPC = llvm::StringSwitch<bool>(MacroName)
334         .Case("dispatch_retain", true)
335         .Case("dispatch_release", true)
336         .Case("xpc_retain", true)
337         .Case("xpc_release", true)
338         .Default(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>(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>(*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>(*CompStmtChild.begin());
366     if (!DeclS)
367       return;
368     if (!DeclS->isSingleDecl())
369       return;
370     VarDecl *VD = dyn_cast_or_null<VarDecl>(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>(Rec))
380       Rec = FE->getSubExpr()->IgnoreParenImpCasts();
381     RecRange = Rec->getSourceRange();
382     if (SM.isMacroArgExpansion(RecRange.getBegin()))
383       RecRange.setBegin(SM.getImmediateSpellingLoc(RecRange.getBegin()));
384     if (SM.isMacroArgExpansion(RecRange.getEnd()))
385       RecRange.setEnd(SM.getImmediateSpellingLoc(RecRange.getEnd()));
386   }
387 
clearDiagnostics(SourceLocation loc) const388   void clearDiagnostics(SourceLocation loc) const {
389     Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message,
390                             diag::err_unavailable,
391                             diag::err_unavailable_message,
392                             loc);
393   }
394 
isDelegateMessage(Expr * E) const395   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>(E))
402       E = pseudoOp->getResultExpr()->IgnoreImplicit();
403 
404     if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E))
405       return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel);
406 
407     return false;
408   }
409 
isInAtFinally(Expr * E) const410   bool isInAtFinally(Expr *E) const {
411     assert(E);
412     Stmt *S = E;
413     while (S) {
414       if (isa<ObjCAtFinallyStmt>(S))
415         return true;
416       S = StmtMap->getParent(S);
417     }
418 
419     return false;
420   }
421 
isRemovable(Expr * E) const422   bool isRemovable(Expr *E) const {
423     return Removables.count(E);
424   }
425 
tryRemoving(Expr * E) const426   bool tryRemoving(Expr *E) const {
427     if (isRemovable(E)) {
428       Pass.TA.removeStmt(E);
429       return true;
430     }
431 
432     Stmt *parent = StmtMap->getParent(E);
433 
434     if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent))
435       return tryRemoving(castE);
436 
437     if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent))
438       return tryRemoving(parenE);
439 
440     if (BinaryOperator *
441           bopE = dyn_cast_or_null<BinaryOperator>(parent)) {
442       if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E &&
443           isRemovable(bopE)) {
444         Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange());
445         return true;
446       }
447     }
448 
449     return false;
450   }
451 
452 };
453 
454 } // anonymous namespace
455 
removeRetainReleaseDeallocFinalize(MigrationPass & pass)456 void trans::removeRetainReleaseDeallocFinalize(MigrationPass &pass) {
457   BodyTransform<RetainReleaseDeallocRemover> trans(pass);
458   trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl());
459 }
460