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    return !S->getElse() || Visit(S->getElse());
108  }
109  bool VisitWhileStmt(WhileStmt *S) {
110    if (S->getConditionVariable())
111      return false;
112    Expr *condE = S->getCond();
113    if (!condE)
114      return false;
115    if (hasSideEffects(condE, Ctx))
116      return false;
117    if (!S->getBody())
118      return false;
119    return Visit(S->getBody());
120  }
121  bool VisitDoStmt(DoStmt *S) {
122    Expr *condE = S->getCond();
123    if (!condE)
124      return false;
125    if (hasSideEffects(condE, Ctx))
126      return false;
127    if (!S->getBody())
128      return false;
129    return Visit(S->getBody());
130  }
131  bool VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {
132    Expr *Exp = S->getCollection();
133    if (!Exp)
134      return false;
135    if (hasSideEffects(Exp, Ctx))
136      return false;
137    if (!S->getBody())
138      return false;
139    return Visit(S->getBody());
140  }
141  bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) {
142    if (!S->getSubStmt())
143      return false;
144    return Visit(S->getSubStmt());
145  }
146};
147
148class EmptyStatementsRemover :
149                            public RecursiveASTVisitor<EmptyStatementsRemover> {
150  MigrationPass &Pass;
151
152public:
153  EmptyStatementsRemover(MigrationPass &pass) : Pass(pass) { }
154
155  bool TraverseStmtExpr(StmtExpr *E) {
156    CompoundStmt *S = E->getSubStmt();
157    for (CompoundStmt::body_iterator
158           I = S->body_begin(), E = S->body_end(); I != E; ++I) {
159      if (I != E - 1)
160        check(*I);
161      TraverseStmt(*I);
162    }
163    return true;
164  }
165
166  bool VisitCompoundStmt(CompoundStmt *S) {
167    for (auto *I : S->body())
168      check(I);
169    return true;
170  }
171
172  ASTContext &getContext() { return Pass.Ctx; }
173
174private:
175  void check(Stmt *S) {
176    if (!S) return;
177    if (EmptyChecker(Pass.Ctx, Pass.ARCMTMacroLocs).Visit(S)) {
178      Transaction Trans(Pass.TA);
179      Pass.TA.removeStmt(S);
180    }
181  }
182};
183
184} // anonymous namespace
185
186static bool isBodyEmpty(CompoundStmt *body, ASTContext &Ctx,
187                        std::vector<SourceLocation> &MacroLocs) {
188  for (auto *I : body->body())
189    if (!EmptyChecker(Ctx, MacroLocs).Visit(I))
190      return false;
191
192  return true;
193}
194
195static void cleanupDeallocOrFinalize(MigrationPass &pass) {
196  ASTContext &Ctx = pass.Ctx;
197  TransformActions &TA = pass.TA;
198  DeclContext *DC = Ctx.getTranslationUnitDecl();
199  Selector FinalizeSel =
200      Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize"));
201
202  typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl>
203    impl_iterator;
204  for (impl_iterator I = impl_iterator(DC->decls_begin()),
205                     E = impl_iterator(DC->decls_end()); I != E; ++I) {
206    ObjCMethodDecl *DeallocM = nullptr;
207    ObjCMethodDecl *FinalizeM = nullptr;
208    for (auto *MD : I->instance_methods()) {
209      if (!MD->hasBody())
210        continue;
211
212      if (MD->getMethodFamily() == OMF_dealloc) {
213        DeallocM = MD;
214      } else if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) {
215        FinalizeM = MD;
216      }
217    }
218
219    if (DeallocM) {
220      if (isBodyEmpty(DeallocM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) {
221        Transaction Trans(TA);
222        TA.remove(DeallocM->getSourceRange());
223      }
224
225      if (FinalizeM) {
226        Transaction Trans(TA);
227        TA.remove(FinalizeM->getSourceRange());
228      }
229
230    } else if (FinalizeM) {
231      if (isBodyEmpty(FinalizeM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) {
232        Transaction Trans(TA);
233        TA.remove(FinalizeM->getSourceRange());
234      } else {
235        Transaction Trans(TA);
236        TA.replaceText(FinalizeM->getSelectorStartLoc(), "finalize", "dealloc");
237      }
238    }
239  }
240}
241
242void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) {
243  EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
244
245  cleanupDeallocOrFinalize(pass);
246
247  for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) {
248    Transaction Trans(pass.TA);
249    pass.TA.remove(pass.ARCMTMacroLocs[i]);
250  }
251}
252