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