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 MacroLocs, SemiLoc, BeforeThanCompare<SourceLocation>(SM)); 47 --I; 48 SourceLocation 49 AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size()); 50 assert(AfterMacroLoc.isFileID()); 51 52 if (AfterMacroLoc == SemiLoc) 53 return true; 54 55 int RelOffs = 0; 56 if (!SM.isInSameSLocAddrSpace(AfterMacroLoc, SemiLoc, &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(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(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(condE, Ctx)) 102 return false; 103 if (!S->getThen() || !Visit(S->getThen())) 104 return false; 105 return !S->getElse() || Visit(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(condE, Ctx)) 114 return false; 115 if (!S->getBody()) 116 return false; 117 return Visit(S->getBody()); 118 } 119 bool VisitDoStmt(DoStmt *S) { 120 Expr *condE = S->getCond(); 121 if (!condE) 122 return false; 123 if (hasSideEffects(condE, Ctx)) 124 return false; 125 if (!S->getBody()) 126 return false; 127 return Visit(S->getBody()); 128 } 129 bool VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { 130 Expr *Exp = S->getCollection(); 131 if (!Exp) 132 return false; 133 if (hasSideEffects(Exp, Ctx)) 134 return false; 135 if (!S->getBody()) 136 return false; 137 return Visit(S->getBody()); 138 } 139 bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { 140 if (!S->getSubStmt()) 141 return false; 142 return Visit(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(*I); 159 TraverseStmt(*I); 160 } 161 return true; 162 } 163 164 bool VisitCompoundStmt(CompoundStmt *S) { 165 for (auto *I : S->body()) 166 check(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(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(&pass.Ctx.Idents.get("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(DeallocM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { 219 Transaction Trans(TA); 220 TA.remove(DeallocM->getSourceRange()); 221 } 222 223 if (FinalizeM) { 224 Transaction Trans(TA); 225 TA.remove(FinalizeM->getSourceRange()); 226 } 227 228 } else if (FinalizeM) { 229 if (isBodyEmpty(FinalizeM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { 230 Transaction Trans(TA); 231 TA.remove(FinalizeM->getSourceRange()); 232 } else { 233 Transaction Trans(TA); 234 TA.replaceText(FinalizeM->getSelectorStartLoc(), "finalize", "dealloc"); 235 } 236 } 237 } 238 } 239 240 void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) { 241 EmptyStatementsRemover(pass).TraverseDecl(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(pass.ARCMTMacroLocs[i]); 248 } 249 } 250