/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 133 | ASTContext &Ctx) { |
34 | 133 | if (!S->hasLeadingEmptyMacro()) |
35 | 5 | return false; |
36 | | |
37 | 128 | SourceLocation SemiLoc = S->getSemiLoc(); |
38 | 128 | if (SemiLoc.isInvalid() || SemiLoc.isMacroID()) |
39 | 0 | return false; |
40 | | |
41 | 128 | if (MacroLocs.empty()) |
42 | 1 | return false; |
43 | | |
44 | 127 | SourceManager &SM = Ctx.getSourceManager(); |
45 | 127 | std::vector<SourceLocation>::iterator I = llvm::upper_bound( |
46 | 127 | MacroLocs, SemiLoc, BeforeThanCompare<SourceLocation>(SM)); |
47 | 127 | --I; |
48 | 127 | SourceLocation |
49 | 127 | AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size()); |
50 | 127 | assert(AfterMacroLoc.isFileID()); |
51 | | |
52 | 127 | if (AfterMacroLoc == SemiLoc) |
53 | 126 | return true; |
54 | | |
55 | 1 | SourceLocation::IntTy RelOffs = 0; |
56 | 1 | if (!SM.isInSameSLocAddrSpace(AfterMacroLoc, SemiLoc, &RelOffs)) |
57 | 0 | return false; |
58 | 1 | if (RelOffs < 0) |
59 | 0 | 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 | 1 | if (RelOffs - getARCMTMacroName().size() > 100) |
66 | 0 | return false; |
67 | | |
68 | 1 | SourceLocation AfterMacroSemiLoc = findSemiAfterLocation(AfterMacroLoc, Ctx); |
69 | 1 | return AfterMacroSemiLoc == SemiLoc; |
70 | 1 | } |
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 | 924 | : Ctx(ctx), MacroLocs(macroLocs) { } |
83 | | |
84 | 133 | bool VisitNullStmt(NullStmt *S) { |
85 | 133 | return isEmptyARCMTMacroStatement(S, MacroLocs, Ctx); |
86 | 133 | } |
87 | 68 | bool VisitCompoundStmt(CompoundStmt *S) { |
88 | 68 | if (S->body_empty()) |
89 | 9 | return false; // was already empty, not because of transformations. |
90 | 59 | for (auto *I : S->body()) |
91 | 72 | if (!Visit(I)) |
92 | 48 | return false; |
93 | 11 | return true; |
94 | 59 | } |
95 | 30 | bool VisitIfStmt(IfStmt *S) { |
96 | 30 | if (S->getConditionVariable()) |
97 | 0 | return false; |
98 | 30 | Expr *condE = S->getCond(); |
99 | 30 | if (!condE) |
100 | 0 | return false; |
101 | 30 | if (hasSideEffects(condE, Ctx)) |
102 | 2 | return false; |
103 | 28 | if (!S->getThen() || !Visit(S->getThen())) |
104 | 24 | return false; |
105 | 4 | return !S->getElse() || Visit(S->getElse())0 ; |
106 | 28 | } |
107 | 13 | bool VisitWhileStmt(WhileStmt *S) { |
108 | 13 | if (S->getConditionVariable()) |
109 | 0 | return false; |
110 | 13 | Expr *condE = S->getCond(); |
111 | 13 | if (!condE) |
112 | 0 | return false; |
113 | 13 | if (hasSideEffects(condE, Ctx)) |
114 | 0 | return false; |
115 | 13 | if (!S->getBody()) |
116 | 0 | return false; |
117 | 13 | return Visit(S->getBody()); |
118 | 13 | } |
119 | 1 | bool VisitDoStmt(DoStmt *S) { |
120 | 1 | Expr *condE = S->getCond(); |
121 | 1 | if (!condE) |
122 | 0 | return false; |
123 | 1 | if (hasSideEffects(condE, Ctx)) |
124 | 0 | return false; |
125 | 1 | if (!S->getBody()) |
126 | 0 | return false; |
127 | 1 | return Visit(S->getBody()); |
128 | 1 | } |
129 | 12 | bool VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { |
130 | 12 | Expr *Exp = S->getCollection(); |
131 | 12 | if (!Exp) |
132 | 0 | return false; |
133 | 12 | if (hasSideEffects(Exp, Ctx)) |
134 | 0 | return false; |
135 | 12 | if (!S->getBody()) |
136 | 0 | return false; |
137 | 12 | return Visit(S->getBody()); |
138 | 12 | } |
139 | 29 | bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { |
140 | 29 | if (!S->getSubStmt()) |
141 | 0 | return false; |
142 | 29 | return Visit(S->getSubStmt()); |
143 | 29 | } |
144 | | }; |
145 | | |
146 | | class EmptyStatementsRemover : |
147 | | public RecursiveASTVisitor<EmptyStatementsRemover> { |
148 | | MigrationPass &Pass; |
149 | | |
150 | | public: |
151 | 85 | EmptyStatementsRemover(MigrationPass &pass) : Pass(pass) { } |
152 | | |
153 | 6 | bool TraverseStmtExpr(StmtExpr *E) { |
154 | 6 | CompoundStmt *S = E->getSubStmt(); |
155 | 6 | for (CompoundStmt::body_iterator |
156 | 24 | I = S->body_begin(), E = S->body_end(); I != E; ++I18 ) { |
157 | 18 | if (I != E - 1) |
158 | 12 | check(*I); |
159 | 18 | TraverseStmt(*I); |
160 | 18 | } |
161 | 6 | return true; |
162 | 6 | } |
163 | | |
164 | 462 | bool VisitCompoundStmt(CompoundStmt *S) { |
165 | 462 | for (auto *I : S->body()) |
166 | 867 | check(I); |
167 | 462 | return true; |
168 | 462 | } |
169 | | |
170 | 0 | ASTContext &getContext() { return Pass.Ctx; } |
171 | | |
172 | | private: |
173 | 879 | void check(Stmt *S) { |
174 | 879 | if (!S) return0 ; |
175 | 879 | if (EmptyChecker(Pass.Ctx, Pass.ARCMTMacroLocs).Visit(S)) { |
176 | 97 | Transaction Trans(Pass.TA); |
177 | 97 | Pass.TA.removeStmt(S); |
178 | 97 | } |
179 | 879 | } |
180 | | }; |
181 | | |
182 | | } // anonymous namespace |
183 | | |
184 | | static bool isBodyEmpty(CompoundStmt *body, ASTContext &Ctx, |
185 | 33 | std::vector<SourceLocation> &MacroLocs) { |
186 | 33 | for (auto *I : body->body()) |
187 | 45 | if (!EmptyChecker(Ctx, MacroLocs).Visit(I)) |
188 | 29 | return false; |
189 | | |
190 | 4 | return true; |
191 | 33 | } |
192 | | |
193 | 85 | static void cleanupDeallocOrFinalize(MigrationPass &pass) { |
194 | 85 | ASTContext &Ctx = pass.Ctx; |
195 | 85 | TransformActions &TA = pass.TA; |
196 | 85 | DeclContext *DC = Ctx.getTranslationUnitDecl(); |
197 | 85 | Selector FinalizeSel = |
198 | 85 | Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize")); |
199 | | |
200 | 85 | typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl> |
201 | 85 | impl_iterator; |
202 | 85 | for (impl_iterator I = impl_iterator(DC->decls_begin()), |
203 | 173 | E = impl_iterator(DC->decls_end()); I != E; ++I88 ) { |
204 | 88 | ObjCMethodDecl *DeallocM = nullptr; |
205 | 88 | ObjCMethodDecl *FinalizeM = nullptr; |
206 | 478 | for (auto *MD : I->instance_methods()) { |
207 | 478 | if (!MD->hasBody()) |
208 | 329 | continue; |
209 | | |
210 | 149 | if (MD->getMethodFamily() == OMF_dealloc) { |
211 | 24 | DeallocM = MD; |
212 | 125 | } else if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) { |
213 | 18 | FinalizeM = MD; |
214 | 18 | } |
215 | 149 | } |
216 | | |
217 | 88 | if (DeallocM) { |
218 | 24 | if (isBodyEmpty(DeallocM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { |
219 | 4 | Transaction Trans(TA); |
220 | 4 | TA.remove(DeallocM->getSourceRange()); |
221 | 4 | } |
222 | | |
223 | 24 | if (FinalizeM) { |
224 | 9 | Transaction Trans(TA); |
225 | 9 | TA.remove(FinalizeM->getSourceRange()); |
226 | 9 | } |
227 | | |
228 | 64 | } else if (FinalizeM) { |
229 | 9 | if (isBodyEmpty(FinalizeM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { |
230 | 0 | Transaction Trans(TA); |
231 | 0 | TA.remove(FinalizeM->getSourceRange()); |
232 | 9 | } else { |
233 | 9 | Transaction Trans(TA); |
234 | 9 | TA.replaceText(FinalizeM->getSelectorStartLoc(), "finalize", "dealloc"); |
235 | 9 | } |
236 | 9 | } |
237 | 88 | } |
238 | 85 | } |
239 | | |
240 | 85 | void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) { |
241 | 85 | EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
242 | | |
243 | 85 | cleanupDeallocOrFinalize(pass); |
244 | | |
245 | 177 | for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i92 ) { |
246 | 92 | Transaction Trans(pass.TA); |
247 | 92 | pass.TA.remove(pass.ARCMTMacroLocs[i]); |
248 | 92 | } |
249 | 85 | } |