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 | |
27 | using namespace clang; |
28 | using namespace arcmt; |
29 | using namespace trans; |
30 | |
31 | static 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 | Range&: MacroLocs, Value&: SemiLoc, C: BeforeThanCompare<SourceLocation>(SM)); |
47 | --I; |
48 | SourceLocation |
49 | AfterMacroLoc = I->getLocWithOffset(Offset: getARCMTMacroName().size()); |
50 | assert(AfterMacroLoc.isFileID()); |
51 | |
52 | if (AfterMacroLoc == SemiLoc) |
53 | return true; |
54 | |
55 | SourceLocation::IntTy RelOffs = 0; |
56 | if (!SM.isInSameSLocAddrSpace(LHS: AfterMacroLoc, RHS: SemiLoc, RelativeOffset: &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(loc: AfterMacroLoc, Ctx); |
69 | return AfterMacroSemiLoc == SemiLoc; |
70 | } |
71 | |
72 | namespace { |
73 | |
74 | /// Returns true if the statement became empty due to previous |
75 | /// transformations. |
76 | class EmptyChecker : public StmtVisitor<EmptyChecker, bool> { |
77 | ASTContext &Ctx; |
78 | std::vector<SourceLocation> &MacroLocs; |
79 | |
80 | public: |
81 | EmptyChecker(ASTContext &ctx, std::vector<SourceLocation> ¯oLocs) |
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(S: 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(E: condE, Ctx)) |
102 | return false; |
103 | if (!S->getThen() || !Visit(S: S->getThen())) |
104 | return false; |
105 | return !S->getElse() || Visit(S: 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(E: condE, Ctx)) |
114 | return false; |
115 | if (!S->getBody()) |
116 | return false; |
117 | return Visit(S: S->getBody()); |
118 | } |
119 | bool VisitDoStmt(DoStmt *S) { |
120 | Expr *condE = S->getCond(); |
121 | if (!condE) |
122 | return false; |
123 | if (hasSideEffects(E: condE, Ctx)) |
124 | return false; |
125 | if (!S->getBody()) |
126 | return false; |
127 | return Visit(S: S->getBody()); |
128 | } |
129 | bool VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { |
130 | Expr *Exp = S->getCollection(); |
131 | if (!Exp) |
132 | return false; |
133 | if (hasSideEffects(E: Exp, Ctx)) |
134 | return false; |
135 | if (!S->getBody()) |
136 | return false; |
137 | return Visit(S: S->getBody()); |
138 | } |
139 | bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { |
140 | if (!S->getSubStmt()) |
141 | return false; |
142 | return Visit(S: S->getSubStmt()); |
143 | } |
144 | }; |
145 | |
146 | class EmptyStatementsRemover : |
147 | public RecursiveASTVisitor<EmptyStatementsRemover> { |
148 | MigrationPass &Pass; |
149 | |
150 | public: |
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(S: *I); |
159 | TraverseStmt(S: *I); |
160 | } |
161 | return true; |
162 | } |
163 | |
164 | bool VisitCompoundStmt(CompoundStmt *S) { |
165 | for (auto *I : S->body()) |
166 | check(S: I); |
167 | return true; |
168 | } |
169 | |
170 | ASTContext &getContext() { return Pass.Ctx; } |
171 | |
172 | private: |
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 | |
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(S: I)) |
188 | return false; |
189 | |
190 | return true; |
191 | } |
192 | |
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(ID: &pass.Ctx.Idents.get(Name: "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(body: DeallocM->getCompoundBody(), Ctx, MacroLocs&: pass.ARCMTMacroLocs)) { |
219 | Transaction Trans(TA); |
220 | TA.remove(range: DeallocM->getSourceRange()); |
221 | } |
222 | |
223 | if (FinalizeM) { |
224 | Transaction Trans(TA); |
225 | TA.remove(range: FinalizeM->getSourceRange()); |
226 | } |
227 | |
228 | } else if (FinalizeM) { |
229 | if (isBodyEmpty(body: FinalizeM->getCompoundBody(), Ctx, MacroLocs&: pass.ARCMTMacroLocs)) { |
230 | Transaction Trans(TA); |
231 | TA.remove(range: FinalizeM->getSourceRange()); |
232 | } else { |
233 | Transaction Trans(TA); |
234 | TA.replaceText(loc: FinalizeM->getSelectorStartLoc(), text: "finalize" , replacementText: "dealloc" ); |
235 | } |
236 | } |
237 | } |
238 | } |
239 | |
240 | void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) { |
241 | EmptyStatementsRemover(pass).TraverseDecl(D: 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(range: pass.ARCMTMacroLocs[i]); |
248 | } |
249 | } |
250 | |