Clang Project

clang_source_code/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp
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
46    I = std::upper_bound(MacroLocs.begin(), MacroLocs.end(), SemiLoc,
47                         BeforeThanCompare<SourceLocation>(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(AfterMacroLocSemiLoc, &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(AfterMacroLocCtx);
70  return AfterMacroSemiLoc == SemiLoc;
71}
72
73namespace {
74
75/// Returns true if the statement became empty due to previous
76/// transformations.
77class EmptyChecker : public StmtVisitor<EmptyCheckerbool> {
78  ASTContext &Ctx;
79  std::vector<SourceLocation> &MacroLocs;
80
81public:
82  EmptyChecker(ASTContext &ctxstd::vector<SourceLocation> &macroLocs)
83    : Ctx(ctx), MacroLocs(macroLocs) { }
84
85  bool VisitNullStmt(NullStmt *S) {
86    return isEmptyARCMTMacroStatement(SMacroLocsCtx);
87  }
88  bool VisitCompoundStmt(CompoundStmt *S) {
89    if (S->body_empty())
90      return false// was already empty, not because of transformations.
91    for (auto *I : S->body())
92      if (!Visit(I))
93        return false;
94    return true;
95  }
96  bool VisitIfStmt(IfStmt *S) {
97    if (S->getConditionVariable())
98      return false;
99    Expr *condE = S->getCond();
100    if (!condE)
101      return false;
102    if (hasSideEffects(condECtx))
103      return false;
104    if (!S->getThen() || !Visit(S->getThen()))
105      return false;
106    return !S->getElse() || Visit(S->getElse());
107  }
108  bool VisitWhileStmt(WhileStmt *S) {
109    if (S->getConditionVariable())
110      return false;
111    Expr *condE = S->getCond();
112    if (!condE)
113      return false;
114    if (hasSideEffects(condECtx))
115      return false;
116    if (!S->getBody())
117      return false;
118    return Visit(S->getBody());
119  }
120  bool VisitDoStmt(DoStmt *S) {
121    Expr *condE = S->getCond();
122    if (!condE)
123      return false;
124    if (hasSideEffects(condECtx))
125      return false;
126    if (!S->getBody())
127      return false;
128    return Visit(S->getBody());
129  }
130  bool VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) {
131    Expr *Exp = S->getCollection();
132    if (!Exp)
133      return false;
134    if (hasSideEffects(ExpCtx))
135      return false;
136    if (!S->getBody())
137      return false;
138    return Visit(S->getBody());
139  }
140  bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) {
141    if (!S->getSubStmt())
142      return false;
143    return Visit(S->getSubStmt());
144  }
145};
146
147class EmptyStatementsRemover :
148                            public RecursiveASTVisitor<EmptyStatementsRemover> {
149  MigrationPass &Pass;
150
151public:
152  EmptyStatementsRemover(MigrationPass &pass) : Pass(pass) { }
153
154  bool TraverseStmtExpr(StmtExpr *E) {
155    CompoundStmt *S = E->getSubStmt();
156    for (CompoundStmt::body_iterator
157           I = S->body_begin(), E = S->body_end(); I != E; ++I) {
158      if (I != E - 1)
159        check(*I);
160      TraverseStmt(*I);
161    }
162    return true;
163  }
164
165  bool VisitCompoundStmt(CompoundStmt *S) {
166    for (auto *I : S->body())
167      check(I);
168    return true;
169  }
170
171  ASTContext &getContext() { return Pass.Ctx; }
172
173private:
174  void check(Stmt *S) {
175    if (!Sreturn;
176    if (EmptyChecker(Pass.CtxPass.ARCMTMacroLocs).Visit(S)) {
177      Transaction Trans(Pass.TA);
178      Pass.TA.removeStmt(S);
179    }
180  }
181};
182
183// anonymous namespace
184
185static bool isBodyEmpty(CompoundStmt *bodyASTContext &Ctx,
186                        std::vector<SourceLocation> &MacroLocs) {
187  for (auto *I : body->body())
188    if (!EmptyChecker(Ctx, MacroLocs).Visit(I))
189      return false;
190
191  return true;
192}
193
194static void cleanupDeallocOrFinalize(MigrationPass &pass) {
195  ASTContext &Ctx = pass.Ctx;
196  TransformActions &TA = pass.TA;
197  DeclContext *DC = Ctx.getTranslationUnitDecl();
198  Selector FinalizeSel =
199      Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize"));
200
201  typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl>
202    impl_iterator;
203  for (impl_iterator I = impl_iterator(DC->decls_begin()),
204                     E = impl_iterator(DC->decls_end()); I != E; ++I) {
205    ObjCMethodDecl *DeallocM = nullptr;
206    ObjCMethodDecl *FinalizeM = nullptr;
207    for (auto *MD : I->instance_methods()) {
208      if (!MD->hasBody())
209        continue;
210
211      if (MD->getMethodFamily() == OMF_dealloc) {
212        DeallocM = MD;
213      } else if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) {
214        FinalizeM = MD;
215      }
216    }
217
218    if (DeallocM) {
219      if (isBodyEmpty(DeallocM->getCompoundBody(), Ctxpass.ARCMTMacroLocs)) {
220        Transaction Trans(TA);
221        TA.remove(DeallocM->getSourceRange());
222      }
223
224      if (FinalizeM) {
225        Transaction Trans(TA);
226        TA.remove(FinalizeM->getSourceRange());
227      }
228
229    } else if (FinalizeM) {
230      if (isBodyEmpty(FinalizeM->getCompoundBody(), Ctxpass.ARCMTMacroLocs)) {
231        Transaction Trans(TA);
232        TA.remove(FinalizeM->getSourceRange());
233      } else {
234        Transaction Trans(TA);
235        TA.replaceText(FinalizeM->getSelectorStartLoc(), "finalize""dealloc");
236      }
237    }
238  }
239}
240
241void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) {
242  EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl());
243
244  cleanupDeallocOrFinalize(pass);
245
246  for (unsigned i = 0e = pass.ARCMTMacroLocs.size(); i != e; ++i) {
247    Transaction Trans(pass.TA);
248    pass.TA.remove(pass.ARCMTMacroLocs[i]);
249  }
250}
251