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