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