1 //===-- TransEmptyStatementsAndDealloc.cpp - Transformations to ARC mode --===//
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
7 //===----------------------------------------------------------------------===//
9 // removeEmptyStatementsAndDealloc:
11 // Removes empty statements that are leftovers from previous transformations.
16 // removeRetainReleaseDealloc will leave an empty ";" that removeEmptyStatements
19 //===----------------------------------------------------------------------===//
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"
27 using namespace clang
;
28 using namespace arcmt
;
29 using namespace trans
;
31 static bool isEmptyARCMTMacroStatement(NullStmt
*S
,
32 std::vector
<SourceLocation
> &MacroLocs
,
34 if (!S
->hasLeadingEmptyMacro())
37 SourceLocation SemiLoc
= S
->getSemiLoc();
38 if (SemiLoc
.isInvalid() || SemiLoc
.isMacroID())
41 if (MacroLocs
.empty())
44 SourceManager
&SM
= Ctx
.getSourceManager();
45 std::vector
<SourceLocation
>::iterator I
= llvm::upper_bound(
46 MacroLocs
, SemiLoc
, BeforeThanCompare
<SourceLocation
>(SM
));
49 AfterMacroLoc
= I
->getLocWithOffset(getARCMTMacroName().size());
50 assert(AfterMacroLoc
.isFileID());
52 if (AfterMacroLoc
== SemiLoc
)
55 SourceLocation::IntTy RelOffs
= 0;
56 if (!SM
.isInSameSLocAddrSpace(AfterMacroLoc
, SemiLoc
, &RelOffs
))
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
65 if (RelOffs
- getARCMTMacroName().size() > 100)
68 SourceLocation AfterMacroSemiLoc
= findSemiAfterLocation(AfterMacroLoc
, Ctx
);
69 return AfterMacroSemiLoc
== SemiLoc
;
74 /// Returns true if the statement became empty due to previous
76 class EmptyChecker
: public StmtVisitor
<EmptyChecker
, bool> {
78 std::vector
<SourceLocation
> &MacroLocs
;
81 EmptyChecker(ASTContext
&ctx
, std::vector
<SourceLocation
> ¯oLocs
)
82 : Ctx(ctx
), MacroLocs(macroLocs
) { }
84 bool VisitNullStmt(NullStmt
*S
) {
85 return isEmptyARCMTMacroStatement(S
, MacroLocs
, Ctx
);
87 bool VisitCompoundStmt(CompoundStmt
*S
) {
89 return false; // was already empty, not because of transformations.
90 for (auto *I
: S
->body())
95 bool VisitIfStmt(IfStmt
*S
) {
96 if (S
->getConditionVariable())
98 Expr
*condE
= S
->getCond();
101 if (hasSideEffects(condE
, Ctx
))
103 if (!S
->getThen() || !Visit(S
->getThen()))
105 return !S
->getElse() || Visit(S
->getElse());
107 bool VisitWhileStmt(WhileStmt
*S
) {
108 if (S
->getConditionVariable())
110 Expr
*condE
= S
->getCond();
113 if (hasSideEffects(condE
, Ctx
))
117 return Visit(S
->getBody());
119 bool VisitDoStmt(DoStmt
*S
) {
120 Expr
*condE
= S
->getCond();
123 if (hasSideEffects(condE
, Ctx
))
127 return Visit(S
->getBody());
129 bool VisitObjCForCollectionStmt(ObjCForCollectionStmt
*S
) {
130 Expr
*Exp
= S
->getCollection();
133 if (hasSideEffects(Exp
, Ctx
))
137 return Visit(S
->getBody());
139 bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt
*S
) {
140 if (!S
->getSubStmt())
142 return Visit(S
->getSubStmt());
146 class EmptyStatementsRemover
:
147 public RecursiveASTVisitor
<EmptyStatementsRemover
> {
151 EmptyStatementsRemover(MigrationPass
&pass
) : Pass(pass
) { }
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
) {
164 bool VisitCompoundStmt(CompoundStmt
*S
) {
165 for (auto *I
: S
->body())
170 ASTContext
&getContext() { return Pass
.Ctx
; }
173 void check(Stmt
*S
) {
175 if (EmptyChecker(Pass
.Ctx
, Pass
.ARCMTMacroLocs
).Visit(S
)) {
176 Transaction
Trans(Pass
.TA
);
177 Pass
.TA
.removeStmt(S
);
182 } // anonymous namespace
184 static bool isBodyEmpty(CompoundStmt
*body
, ASTContext
&Ctx
,
185 std::vector
<SourceLocation
> &MacroLocs
) {
186 for (auto *I
: body
->body())
187 if (!EmptyChecker(Ctx
, MacroLocs
).Visit(I
))
193 static 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"));
200 typedef DeclContext::specific_decl_iterator
<ObjCImplementationDecl
>
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()) {
210 if (MD
->getMethodFamily() == OMF_dealloc
) {
212 } else if (MD
->isInstanceMethod() && MD
->getSelector() == FinalizeSel
) {
218 if (isBodyEmpty(DeallocM
->getCompoundBody(), Ctx
, pass
.ARCMTMacroLocs
)) {
219 Transaction
Trans(TA
);
220 TA
.remove(DeallocM
->getSourceRange());
224 Transaction
Trans(TA
);
225 TA
.remove(FinalizeM
->getSourceRange());
228 } else if (FinalizeM
) {
229 if (isBodyEmpty(FinalizeM
->getCompoundBody(), Ctx
, pass
.ARCMTMacroLocs
)) {
230 Transaction
Trans(TA
);
231 TA
.remove(FinalizeM
->getSourceRange());
233 Transaction
Trans(TA
);
234 TA
.replaceText(FinalizeM
->getSelectorStartLoc(), "finalize", "dealloc");
240 void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass
&pass
) {
241 EmptyStatementsRemover(pass
).TraverseDecl(pass
.Ctx
.getTranslationUnitDecl());
243 cleanupDeallocOrFinalize(pass
);
245 for (unsigned i
= 0, e
= pass
.ARCMTMacroLocs
.size(); i
!= e
; ++i
) {
246 Transaction
Trans(pass
.TA
);
247 pass
.TA
.remove(pass
.ARCMTMacroLocs
[i
]);