TransEmptyStatementsAndDealloc.cpp revision 234353
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/StmtVisitor.h"
25#include "clang/Basic/SourceManager.h"
26
27using namespace clang;
28using namespace arcmt;
29using namespace trans;
30
31static bool isEmptyARCMTMacroStatement(NullStmt *S,
32                                       std::vector<SourceLocation> &MacroLocs,
33                                       ASTContext &Ctx) {
34  if (!S->hasLeadingEmptyMacro())
35    return false;
36
37  SourceLocation SemiLoc = S->getSemiLoc();
38  if (SemiLoc.isInvalid() || SemiLoc.isMacroID())
39    return false;
40
41  if (MacroLocs.empty())
42    return false;
43
44  SourceManager &SM = Ctx.getSourceManager();
45  std::vector<SourceLocation>::iterator
46    I = std::upper_bound(MacroLocs.begin(), MacroLocs.end(), SemiLoc,
47                         SourceManager::LocBeforeThanCompare(SM));
48  --I;
49  SourceLocation
50      AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size());
51  assert(AfterMacroLoc.isFileID());
52
53  if (AfterMacroLoc == SemiLoc)
54    return true;
55
56  int RelOffs = 0;
57  if (!SM.isInSameSLocAddrSpace(AfterMacroLoc, SemiLoc, &RelOffs))
58    return false;
59  if (RelOffs < 0)
60    return false;
61
62  // We make the reasonable assumption that a semicolon after 100 characters
63  // means that it is not the next token after our macro. If this assumption
64  // fails it is not critical, we will just fail to clear out, e.g., an empty
65  // 'if'.
66  if (RelOffs - getARCMTMacroName().size() > 100)
67    return false;
68
69  SourceLocation AfterMacroSemiLoc = findSemiAfterLocation(AfterMacroLoc, Ctx);
70  return AfterMacroSemiLoc == SemiLoc;
71}
72
73namespace {
74
75/// \brief Returns true if the statement became empty due to previous
76/// transformations.
77class EmptyChecker : public StmtVisitor<EmptyChecker, bool> {
78  ASTContext &Ctx;
79  std::vector<SourceLocation> &MacroLocs;
80
81public:
82  EmptyChecker(ASTContext &ctx, std::vector<SourceLocation> &macroLocs)
83    : Ctx(ctx), MacroLocs(macroLocs) { }
84
85  bool VisitNullStmt(NullStmt *S) {
86    return isEmptyARCMTMacroStatement(S, MacroLocs, Ctx);
87  }
88  bool VisitCompoundStmt(CompoundStmt *S) {
89    if (S->body_empty())
90      return false; // was already empty, not because of transformations.
91    for (CompoundStmt::body_iterator
92           I = S->body_begin(), E = S->body_end(); I != E; ++I)
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 (CompoundStmt::body_iterator
170           I = S->body_begin(), E = S->body_end(); I != E; ++I)
171      check(*I);
172    return true;
173  }
174
175  ASTContext &getContext() { return Pass.Ctx; }
176
177private:
178  void check(Stmt *S) {
179    if (!S) return;
180    if (EmptyChecker(Pass.Ctx, Pass.ARCMTMacroLocs).Visit(S)) {
181      Transaction Trans(Pass.TA);
182      Pass.TA.removeStmt(S);
183    }
184  }
185};
186
187} // anonymous namespace
188
189static bool isBodyEmpty(CompoundStmt *body, ASTContext &Ctx,
190                        std::vector<SourceLocation> &MacroLocs) {
191  for (CompoundStmt::body_iterator
192         I = body->body_begin(), E = body->body_end(); I != E; ++I)
193    if (!EmptyChecker(Ctx, MacroLocs).Visit(*I))
194      return false;
195
196  return true;
197}
198
199static void cleanupDeallocOrFinalize(MigrationPass &pass) {
200  ASTContext &Ctx = pass.Ctx;
201  TransformActions &TA = pass.TA;
202  DeclContext *DC = Ctx.getTranslationUnitDecl();
203  Selector FinalizeSel =
204      Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize"));
205
206  typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl>
207    impl_iterator;
208  for (impl_iterator I = impl_iterator(DC->decls_begin()),
209                     E = impl_iterator(DC->decls_end()); I != E; ++I) {
210    ObjCMethodDecl *DeallocM = 0;
211    ObjCMethodDecl *FinalizeM = 0;
212    for (ObjCImplementationDecl::instmeth_iterator
213           MI = (*I)->instmeth_begin(),
214           ME = (*I)->instmeth_end(); MI != ME; ++MI) {
215      ObjCMethodDecl *MD = *MI;
216      if (!MD->hasBody())
217        continue;
218
219      if (MD->getMethodFamily() == OMF_dealloc) {
220        DeallocM = MD;
221      } else if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) {
222        FinalizeM = MD;
223      }
224    }
225
226    if (DeallocM) {
227      if (isBodyEmpty(DeallocM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) {
228        Transaction Trans(TA);
229        TA.remove(DeallocM->getSourceRange());
230      }
231
232      if (FinalizeM) {
233        Transaction Trans(TA);
234        TA.remove(FinalizeM->getSourceRange());
235      }
236
237    } else if (FinalizeM) {
238      if (isBodyEmpty(FinalizeM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) {
239        Transaction Trans(TA);
240        TA.remove(FinalizeM->getSourceRange());
241      } else {
242        Transaction Trans(TA);
243        TA.replaceText(FinalizeM->getSelectorStartLoc(), "finalize", "dealloc");
244      }
245    }
246  }
247}
248
249void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) {
250  EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
251
252  cleanupDeallocOrFinalize(pass);
253
254  for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) {
255    Transaction Trans(pass.TA);
256    pass.TA.remove(pass.ARCMTMacroLocs[i]);
257  }
258}
259