1e5dd7070Spatrick //===-- TransEmptyStatementsAndDealloc.cpp - Transformations to ARC mode --===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // removeEmptyStatementsAndDealloc:
10e5dd7070Spatrick //
11e5dd7070Spatrick // Removes empty statements that are leftovers from previous transformations.
12e5dd7070Spatrick // e.g for
13e5dd7070Spatrick //
14e5dd7070Spatrick // [x retain];
15e5dd7070Spatrick //
16e5dd7070Spatrick // removeRetainReleaseDealloc will leave an empty ";" that removeEmptyStatements
17e5dd7070Spatrick // will remove.
18e5dd7070Spatrick //
19e5dd7070Spatrick //===----------------------------------------------------------------------===//
20e5dd7070Spatrick
21e5dd7070Spatrick #include "Transforms.h"
22e5dd7070Spatrick #include "Internals.h"
23e5dd7070Spatrick #include "clang/AST/ASTContext.h"
24e5dd7070Spatrick #include "clang/AST/StmtVisitor.h"
25e5dd7070Spatrick #include "clang/Basic/SourceManager.h"
26e5dd7070Spatrick
27e5dd7070Spatrick using namespace clang;
28e5dd7070Spatrick using namespace arcmt;
29e5dd7070Spatrick using namespace trans;
30e5dd7070Spatrick
isEmptyARCMTMacroStatement(NullStmt * S,std::vector<SourceLocation> & MacroLocs,ASTContext & Ctx)31e5dd7070Spatrick static bool isEmptyARCMTMacroStatement(NullStmt *S,
32e5dd7070Spatrick std::vector<SourceLocation> &MacroLocs,
33e5dd7070Spatrick ASTContext &Ctx) {
34e5dd7070Spatrick if (!S->hasLeadingEmptyMacro())
35e5dd7070Spatrick return false;
36e5dd7070Spatrick
37e5dd7070Spatrick SourceLocation SemiLoc = S->getSemiLoc();
38e5dd7070Spatrick if (SemiLoc.isInvalid() || SemiLoc.isMacroID())
39e5dd7070Spatrick return false;
40e5dd7070Spatrick
41e5dd7070Spatrick if (MacroLocs.empty())
42e5dd7070Spatrick return false;
43e5dd7070Spatrick
44e5dd7070Spatrick SourceManager &SM = Ctx.getSourceManager();
45e5dd7070Spatrick std::vector<SourceLocation>::iterator I = llvm::upper_bound(
46e5dd7070Spatrick MacroLocs, SemiLoc, BeforeThanCompare<SourceLocation>(SM));
47e5dd7070Spatrick --I;
48e5dd7070Spatrick SourceLocation
49e5dd7070Spatrick AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size());
50e5dd7070Spatrick assert(AfterMacroLoc.isFileID());
51e5dd7070Spatrick
52e5dd7070Spatrick if (AfterMacroLoc == SemiLoc)
53e5dd7070Spatrick return true;
54e5dd7070Spatrick
55*a9ac8606Spatrick SourceLocation::IntTy RelOffs = 0;
56e5dd7070Spatrick if (!SM.isInSameSLocAddrSpace(AfterMacroLoc, SemiLoc, &RelOffs))
57e5dd7070Spatrick return false;
58e5dd7070Spatrick if (RelOffs < 0)
59e5dd7070Spatrick return false;
60e5dd7070Spatrick
61e5dd7070Spatrick // We make the reasonable assumption that a semicolon after 100 characters
62e5dd7070Spatrick // means that it is not the next token after our macro. If this assumption
63e5dd7070Spatrick // fails it is not critical, we will just fail to clear out, e.g., an empty
64e5dd7070Spatrick // 'if'.
65e5dd7070Spatrick if (RelOffs - getARCMTMacroName().size() > 100)
66e5dd7070Spatrick return false;
67e5dd7070Spatrick
68e5dd7070Spatrick SourceLocation AfterMacroSemiLoc = findSemiAfterLocation(AfterMacroLoc, Ctx);
69e5dd7070Spatrick return AfterMacroSemiLoc == SemiLoc;
70e5dd7070Spatrick }
71e5dd7070Spatrick
72e5dd7070Spatrick namespace {
73e5dd7070Spatrick
74e5dd7070Spatrick /// Returns true if the statement became empty due to previous
75e5dd7070Spatrick /// transformations.
76e5dd7070Spatrick class EmptyChecker : public StmtVisitor<EmptyChecker, bool> {
77e5dd7070Spatrick ASTContext &Ctx;
78e5dd7070Spatrick std::vector<SourceLocation> &MacroLocs;
79e5dd7070Spatrick
80e5dd7070Spatrick public:
EmptyChecker(ASTContext & ctx,std::vector<SourceLocation> & macroLocs)81e5dd7070Spatrick EmptyChecker(ASTContext &ctx, std::vector<SourceLocation> ¯oLocs)
82e5dd7070Spatrick : Ctx(ctx), MacroLocs(macroLocs) { }
83e5dd7070Spatrick
VisitNullStmt(NullStmt * S)84e5dd7070Spatrick bool VisitNullStmt(NullStmt *S) {
85e5dd7070Spatrick return isEmptyARCMTMacroStatement(S, MacroLocs, Ctx);
86e5dd7070Spatrick }
VisitCompoundStmt(CompoundStmt * S)87e5dd7070Spatrick bool VisitCompoundStmt(CompoundStmt *S) {
88e5dd7070Spatrick if (S->body_empty())
89e5dd7070Spatrick return false; // was already empty, not because of transformations.
90e5dd7070Spatrick for (auto *I : S->body())
91e5dd7070Spatrick if (!Visit(I))
92e5dd7070Spatrick return false;
93e5dd7070Spatrick return true;
94e5dd7070Spatrick }
VisitIfStmt(IfStmt * S)95e5dd7070Spatrick bool VisitIfStmt(IfStmt *S) {
96e5dd7070Spatrick if (S->getConditionVariable())
97e5dd7070Spatrick return false;
98e5dd7070Spatrick Expr *condE = S->getCond();
99e5dd7070Spatrick if (!condE)
100e5dd7070Spatrick return false;
101e5dd7070Spatrick if (hasSideEffects(condE, Ctx))
102e5dd7070Spatrick return false;
103e5dd7070Spatrick if (!S->getThen() || !Visit(S->getThen()))
104e5dd7070Spatrick return false;
105e5dd7070Spatrick return !S->getElse() || Visit(S->getElse());
106e5dd7070Spatrick }
VisitWhileStmt(WhileStmt * S)107e5dd7070Spatrick bool VisitWhileStmt(WhileStmt *S) {
108e5dd7070Spatrick if (S->getConditionVariable())
109e5dd7070Spatrick return false;
110e5dd7070Spatrick Expr *condE = S->getCond();
111e5dd7070Spatrick if (!condE)
112e5dd7070Spatrick return false;
113e5dd7070Spatrick if (hasSideEffects(condE, Ctx))
114e5dd7070Spatrick return false;
115e5dd7070Spatrick if (!S->getBody())
116e5dd7070Spatrick return false;
117e5dd7070Spatrick return Visit(S->getBody());
118e5dd7070Spatrick }
VisitDoStmt(DoStmt * S)119e5dd7070Spatrick bool VisitDoStmt(DoStmt *S) {
120e5dd7070Spatrick Expr *condE = S->getCond();
121e5dd7070Spatrick if (!condE)
122e5dd7070Spatrick return false;
123e5dd7070Spatrick if (hasSideEffects(condE, Ctx))
124e5dd7070Spatrick return false;
125e5dd7070Spatrick if (!S->getBody())
126e5dd7070Spatrick return false;
127e5dd7070Spatrick return Visit(S->getBody());
128e5dd7070Spatrick }
VisitObjCForCollectionStmt(ObjCForCollectionStmt * S)129e5dd7070Spatrick bool VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {
130e5dd7070Spatrick Expr *Exp = S->getCollection();
131e5dd7070Spatrick if (!Exp)
132e5dd7070Spatrick return false;
133e5dd7070Spatrick if (hasSideEffects(Exp, Ctx))
134e5dd7070Spatrick return false;
135e5dd7070Spatrick if (!S->getBody())
136e5dd7070Spatrick return false;
137e5dd7070Spatrick return Visit(S->getBody());
138e5dd7070Spatrick }
VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt * S)139e5dd7070Spatrick bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) {
140e5dd7070Spatrick if (!S->getSubStmt())
141e5dd7070Spatrick return false;
142e5dd7070Spatrick return Visit(S->getSubStmt());
143e5dd7070Spatrick }
144e5dd7070Spatrick };
145e5dd7070Spatrick
146e5dd7070Spatrick class EmptyStatementsRemover :
147e5dd7070Spatrick public RecursiveASTVisitor<EmptyStatementsRemover> {
148e5dd7070Spatrick MigrationPass &Pass;
149e5dd7070Spatrick
150e5dd7070Spatrick public:
EmptyStatementsRemover(MigrationPass & pass)151e5dd7070Spatrick EmptyStatementsRemover(MigrationPass &pass) : Pass(pass) { }
152e5dd7070Spatrick
TraverseStmtExpr(StmtExpr * E)153e5dd7070Spatrick bool TraverseStmtExpr(StmtExpr *E) {
154e5dd7070Spatrick CompoundStmt *S = E->getSubStmt();
155e5dd7070Spatrick for (CompoundStmt::body_iterator
156e5dd7070Spatrick I = S->body_begin(), E = S->body_end(); I != E; ++I) {
157e5dd7070Spatrick if (I != E - 1)
158e5dd7070Spatrick check(*I);
159e5dd7070Spatrick TraverseStmt(*I);
160e5dd7070Spatrick }
161e5dd7070Spatrick return true;
162e5dd7070Spatrick }
163e5dd7070Spatrick
VisitCompoundStmt(CompoundStmt * S)164e5dd7070Spatrick bool VisitCompoundStmt(CompoundStmt *S) {
165e5dd7070Spatrick for (auto *I : S->body())
166e5dd7070Spatrick check(I);
167e5dd7070Spatrick return true;
168e5dd7070Spatrick }
169e5dd7070Spatrick
getContext()170e5dd7070Spatrick ASTContext &getContext() { return Pass.Ctx; }
171e5dd7070Spatrick
172e5dd7070Spatrick private:
check(Stmt * S)173e5dd7070Spatrick void check(Stmt *S) {
174e5dd7070Spatrick if (!S) return;
175e5dd7070Spatrick if (EmptyChecker(Pass.Ctx, Pass.ARCMTMacroLocs).Visit(S)) {
176e5dd7070Spatrick Transaction Trans(Pass.TA);
177e5dd7070Spatrick Pass.TA.removeStmt(S);
178e5dd7070Spatrick }
179e5dd7070Spatrick }
180e5dd7070Spatrick };
181e5dd7070Spatrick
182e5dd7070Spatrick } // anonymous namespace
183e5dd7070Spatrick
isBodyEmpty(CompoundStmt * body,ASTContext & Ctx,std::vector<SourceLocation> & MacroLocs)184e5dd7070Spatrick static bool isBodyEmpty(CompoundStmt *body, ASTContext &Ctx,
185e5dd7070Spatrick std::vector<SourceLocation> &MacroLocs) {
186e5dd7070Spatrick for (auto *I : body->body())
187e5dd7070Spatrick if (!EmptyChecker(Ctx, MacroLocs).Visit(I))
188e5dd7070Spatrick return false;
189e5dd7070Spatrick
190e5dd7070Spatrick return true;
191e5dd7070Spatrick }
192e5dd7070Spatrick
cleanupDeallocOrFinalize(MigrationPass & pass)193e5dd7070Spatrick static void cleanupDeallocOrFinalize(MigrationPass &pass) {
194e5dd7070Spatrick ASTContext &Ctx = pass.Ctx;
195e5dd7070Spatrick TransformActions &TA = pass.TA;
196e5dd7070Spatrick DeclContext *DC = Ctx.getTranslationUnitDecl();
197e5dd7070Spatrick Selector FinalizeSel =
198e5dd7070Spatrick Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize"));
199e5dd7070Spatrick
200e5dd7070Spatrick typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl>
201e5dd7070Spatrick impl_iterator;
202e5dd7070Spatrick for (impl_iterator I = impl_iterator(DC->decls_begin()),
203e5dd7070Spatrick E = impl_iterator(DC->decls_end()); I != E; ++I) {
204e5dd7070Spatrick ObjCMethodDecl *DeallocM = nullptr;
205e5dd7070Spatrick ObjCMethodDecl *FinalizeM = nullptr;
206e5dd7070Spatrick for (auto *MD : I->instance_methods()) {
207e5dd7070Spatrick if (!MD->hasBody())
208e5dd7070Spatrick continue;
209e5dd7070Spatrick
210e5dd7070Spatrick if (MD->getMethodFamily() == OMF_dealloc) {
211e5dd7070Spatrick DeallocM = MD;
212e5dd7070Spatrick } else if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) {
213e5dd7070Spatrick FinalizeM = MD;
214e5dd7070Spatrick }
215e5dd7070Spatrick }
216e5dd7070Spatrick
217e5dd7070Spatrick if (DeallocM) {
218e5dd7070Spatrick if (isBodyEmpty(DeallocM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) {
219e5dd7070Spatrick Transaction Trans(TA);
220e5dd7070Spatrick TA.remove(DeallocM->getSourceRange());
221e5dd7070Spatrick }
222e5dd7070Spatrick
223e5dd7070Spatrick if (FinalizeM) {
224e5dd7070Spatrick Transaction Trans(TA);
225e5dd7070Spatrick TA.remove(FinalizeM->getSourceRange());
226e5dd7070Spatrick }
227e5dd7070Spatrick
228e5dd7070Spatrick } else if (FinalizeM) {
229e5dd7070Spatrick if (isBodyEmpty(FinalizeM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) {
230e5dd7070Spatrick Transaction Trans(TA);
231e5dd7070Spatrick TA.remove(FinalizeM->getSourceRange());
232e5dd7070Spatrick } else {
233e5dd7070Spatrick Transaction Trans(TA);
234e5dd7070Spatrick TA.replaceText(FinalizeM->getSelectorStartLoc(), "finalize", "dealloc");
235e5dd7070Spatrick }
236e5dd7070Spatrick }
237e5dd7070Spatrick }
238e5dd7070Spatrick }
239e5dd7070Spatrick
removeEmptyStatementsAndDeallocFinalize(MigrationPass & pass)240e5dd7070Spatrick void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) {
241e5dd7070Spatrick EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
242e5dd7070Spatrick
243e5dd7070Spatrick cleanupDeallocOrFinalize(pass);
244e5dd7070Spatrick
245e5dd7070Spatrick for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) {
246e5dd7070Spatrick Transaction Trans(pass.TA);
247e5dd7070Spatrick pass.TA.remove(pass.ARCMTMacroLocs[i]);
248e5dd7070Spatrick }
249e5dd7070Spatrick }
250