1 //===--- TransEmptyStatements.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 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(AfterMacroLoc, SemiLoc, &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(AfterMacroLoc, Ctx); 70 return AfterMacroSemiLoc == SemiLoc; 71 } 72 73 namespace { 74 75 /// Returns true if the statement became empty due to previous 76 /// transformations. 77 class EmptyChecker : public StmtVisitor<EmptyChecker, bool> { 78 ASTContext &Ctx; 79 std::vector<SourceLocation> &MacroLocs; 80 81 public: 82 EmptyChecker(ASTContext &ctx, std::vector<SourceLocation> ¯oLocs) 83 : Ctx(ctx), MacroLocs(macroLocs) { } 84 85 bool VisitNullStmt(NullStmt *S) { 86 return isEmptyARCMTMacroStatement(S, MacroLocs, Ctx); 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(condE, Ctx)) 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(condE, Ctx)) 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(condE, Ctx)) 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(Exp, Ctx)) 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 147 class EmptyStatementsRemover : 148 public RecursiveASTVisitor<EmptyStatementsRemover> { 149 MigrationPass &Pass; 150 151 public: 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 173 private: 174 void check(Stmt *S) { 175 if (!S) return; 176 if (EmptyChecker(Pass.Ctx, Pass.ARCMTMacroLocs).Visit(S)) { 177 Transaction Trans(Pass.TA); 178 Pass.TA.removeStmt(S); 179 } 180 } 181 }; 182 183 } // anonymous namespace 184 185 static bool isBodyEmpty(CompoundStmt *body, ASTContext &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 194 static 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(), Ctx, pass.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(), Ctx, pass.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 241 void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) { 242 EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 243 244 cleanupDeallocOrFinalize(pass); 245 246 for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) { 247 Transaction Trans(pass.TA); 248 pass.TA.remove(pass.ARCMTMacroLocs[i]); 249 } 250 } 251