1*0b57cec5SDimitry Andric //===-- TransEmptyStatementsAndDealloc.cpp - Transformations to ARC mode --===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // removeEmptyStatementsAndDealloc: 10*0b57cec5SDimitry Andric // 11*0b57cec5SDimitry Andric // Removes empty statements that are leftovers from previous transformations. 12*0b57cec5SDimitry Andric // e.g for 13*0b57cec5SDimitry Andric // 14*0b57cec5SDimitry Andric // [x retain]; 15*0b57cec5SDimitry Andric // 16*0b57cec5SDimitry Andric // removeRetainReleaseDealloc will leave an empty ";" that removeEmptyStatements 17*0b57cec5SDimitry Andric // will remove. 18*0b57cec5SDimitry Andric // 19*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 20*0b57cec5SDimitry Andric 21*0b57cec5SDimitry Andric #include "Transforms.h" 22*0b57cec5SDimitry Andric #include "Internals.h" 23*0b57cec5SDimitry Andric #include "clang/AST/ASTContext.h" 24*0b57cec5SDimitry Andric #include "clang/AST/StmtVisitor.h" 25*0b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h" 26*0b57cec5SDimitry Andric 27*0b57cec5SDimitry Andric using namespace clang; 28*0b57cec5SDimitry Andric using namespace arcmt; 29*0b57cec5SDimitry Andric using namespace trans; 30*0b57cec5SDimitry Andric 31*0b57cec5SDimitry Andric static bool isEmptyARCMTMacroStatement(NullStmt *S, 32*0b57cec5SDimitry Andric std::vector<SourceLocation> &MacroLocs, 33*0b57cec5SDimitry Andric ASTContext &Ctx) { 34*0b57cec5SDimitry Andric if (!S->hasLeadingEmptyMacro()) 35*0b57cec5SDimitry Andric return false; 36*0b57cec5SDimitry Andric 37*0b57cec5SDimitry Andric SourceLocation SemiLoc = S->getSemiLoc(); 38*0b57cec5SDimitry Andric if (SemiLoc.isInvalid() || SemiLoc.isMacroID()) 39*0b57cec5SDimitry Andric return false; 40*0b57cec5SDimitry Andric 41*0b57cec5SDimitry Andric if (MacroLocs.empty()) 42*0b57cec5SDimitry Andric return false; 43*0b57cec5SDimitry Andric 44*0b57cec5SDimitry Andric SourceManager &SM = Ctx.getSourceManager(); 45*0b57cec5SDimitry Andric std::vector<SourceLocation>::iterator I = llvm::upper_bound( 46*0b57cec5SDimitry Andric MacroLocs, SemiLoc, BeforeThanCompare<SourceLocation>(SM)); 47*0b57cec5SDimitry Andric --I; 48*0b57cec5SDimitry Andric SourceLocation 49*0b57cec5SDimitry Andric AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size()); 50*0b57cec5SDimitry Andric assert(AfterMacroLoc.isFileID()); 51*0b57cec5SDimitry Andric 52*0b57cec5SDimitry Andric if (AfterMacroLoc == SemiLoc) 53*0b57cec5SDimitry Andric return true; 54*0b57cec5SDimitry Andric 55*0b57cec5SDimitry Andric int RelOffs = 0; 56*0b57cec5SDimitry Andric if (!SM.isInSameSLocAddrSpace(AfterMacroLoc, SemiLoc, &RelOffs)) 57*0b57cec5SDimitry Andric return false; 58*0b57cec5SDimitry Andric if (RelOffs < 0) 59*0b57cec5SDimitry Andric return false; 60*0b57cec5SDimitry Andric 61*0b57cec5SDimitry Andric // We make the reasonable assumption that a semicolon after 100 characters 62*0b57cec5SDimitry Andric // means that it is not the next token after our macro. If this assumption 63*0b57cec5SDimitry Andric // fails it is not critical, we will just fail to clear out, e.g., an empty 64*0b57cec5SDimitry Andric // 'if'. 65*0b57cec5SDimitry Andric if (RelOffs - getARCMTMacroName().size() > 100) 66*0b57cec5SDimitry Andric return false; 67*0b57cec5SDimitry Andric 68*0b57cec5SDimitry Andric SourceLocation AfterMacroSemiLoc = findSemiAfterLocation(AfterMacroLoc, Ctx); 69*0b57cec5SDimitry Andric return AfterMacroSemiLoc == SemiLoc; 70*0b57cec5SDimitry Andric } 71*0b57cec5SDimitry Andric 72*0b57cec5SDimitry Andric namespace { 73*0b57cec5SDimitry Andric 74*0b57cec5SDimitry Andric /// Returns true if the statement became empty due to previous 75*0b57cec5SDimitry Andric /// transformations. 76*0b57cec5SDimitry Andric class EmptyChecker : public StmtVisitor<EmptyChecker, bool> { 77*0b57cec5SDimitry Andric ASTContext &Ctx; 78*0b57cec5SDimitry Andric std::vector<SourceLocation> &MacroLocs; 79*0b57cec5SDimitry Andric 80*0b57cec5SDimitry Andric public: 81*0b57cec5SDimitry Andric EmptyChecker(ASTContext &ctx, std::vector<SourceLocation> ¯oLocs) 82*0b57cec5SDimitry Andric : Ctx(ctx), MacroLocs(macroLocs) { } 83*0b57cec5SDimitry Andric 84*0b57cec5SDimitry Andric bool VisitNullStmt(NullStmt *S) { 85*0b57cec5SDimitry Andric return isEmptyARCMTMacroStatement(S, MacroLocs, Ctx); 86*0b57cec5SDimitry Andric } 87*0b57cec5SDimitry Andric bool VisitCompoundStmt(CompoundStmt *S) { 88*0b57cec5SDimitry Andric if (S->body_empty()) 89*0b57cec5SDimitry Andric return false; // was already empty, not because of transformations. 90*0b57cec5SDimitry Andric for (auto *I : S->body()) 91*0b57cec5SDimitry Andric if (!Visit(I)) 92*0b57cec5SDimitry Andric return false; 93*0b57cec5SDimitry Andric return true; 94*0b57cec5SDimitry Andric } 95*0b57cec5SDimitry Andric bool VisitIfStmt(IfStmt *S) { 96*0b57cec5SDimitry Andric if (S->getConditionVariable()) 97*0b57cec5SDimitry Andric return false; 98*0b57cec5SDimitry Andric Expr *condE = S->getCond(); 99*0b57cec5SDimitry Andric if (!condE) 100*0b57cec5SDimitry Andric return false; 101*0b57cec5SDimitry Andric if (hasSideEffects(condE, Ctx)) 102*0b57cec5SDimitry Andric return false; 103*0b57cec5SDimitry Andric if (!S->getThen() || !Visit(S->getThen())) 104*0b57cec5SDimitry Andric return false; 105*0b57cec5SDimitry Andric return !S->getElse() || Visit(S->getElse()); 106*0b57cec5SDimitry Andric } 107*0b57cec5SDimitry Andric bool VisitWhileStmt(WhileStmt *S) { 108*0b57cec5SDimitry Andric if (S->getConditionVariable()) 109*0b57cec5SDimitry Andric return false; 110*0b57cec5SDimitry Andric Expr *condE = S->getCond(); 111*0b57cec5SDimitry Andric if (!condE) 112*0b57cec5SDimitry Andric return false; 113*0b57cec5SDimitry Andric if (hasSideEffects(condE, Ctx)) 114*0b57cec5SDimitry Andric return false; 115*0b57cec5SDimitry Andric if (!S->getBody()) 116*0b57cec5SDimitry Andric return false; 117*0b57cec5SDimitry Andric return Visit(S->getBody()); 118*0b57cec5SDimitry Andric } 119*0b57cec5SDimitry Andric bool VisitDoStmt(DoStmt *S) { 120*0b57cec5SDimitry Andric Expr *condE = S->getCond(); 121*0b57cec5SDimitry Andric if (!condE) 122*0b57cec5SDimitry Andric return false; 123*0b57cec5SDimitry Andric if (hasSideEffects(condE, Ctx)) 124*0b57cec5SDimitry Andric return false; 125*0b57cec5SDimitry Andric if (!S->getBody()) 126*0b57cec5SDimitry Andric return false; 127*0b57cec5SDimitry Andric return Visit(S->getBody()); 128*0b57cec5SDimitry Andric } 129*0b57cec5SDimitry Andric bool VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { 130*0b57cec5SDimitry Andric Expr *Exp = S->getCollection(); 131*0b57cec5SDimitry Andric if (!Exp) 132*0b57cec5SDimitry Andric return false; 133*0b57cec5SDimitry Andric if (hasSideEffects(Exp, Ctx)) 134*0b57cec5SDimitry Andric return false; 135*0b57cec5SDimitry Andric if (!S->getBody()) 136*0b57cec5SDimitry Andric return false; 137*0b57cec5SDimitry Andric return Visit(S->getBody()); 138*0b57cec5SDimitry Andric } 139*0b57cec5SDimitry Andric bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { 140*0b57cec5SDimitry Andric if (!S->getSubStmt()) 141*0b57cec5SDimitry Andric return false; 142*0b57cec5SDimitry Andric return Visit(S->getSubStmt()); 143*0b57cec5SDimitry Andric } 144*0b57cec5SDimitry Andric }; 145*0b57cec5SDimitry Andric 146*0b57cec5SDimitry Andric class EmptyStatementsRemover : 147*0b57cec5SDimitry Andric public RecursiveASTVisitor<EmptyStatementsRemover> { 148*0b57cec5SDimitry Andric MigrationPass &Pass; 149*0b57cec5SDimitry Andric 150*0b57cec5SDimitry Andric public: 151*0b57cec5SDimitry Andric EmptyStatementsRemover(MigrationPass &pass) : Pass(pass) { } 152*0b57cec5SDimitry Andric 153*0b57cec5SDimitry Andric bool TraverseStmtExpr(StmtExpr *E) { 154*0b57cec5SDimitry Andric CompoundStmt *S = E->getSubStmt(); 155*0b57cec5SDimitry Andric for (CompoundStmt::body_iterator 156*0b57cec5SDimitry Andric I = S->body_begin(), E = S->body_end(); I != E; ++I) { 157*0b57cec5SDimitry Andric if (I != E - 1) 158*0b57cec5SDimitry Andric check(*I); 159*0b57cec5SDimitry Andric TraverseStmt(*I); 160*0b57cec5SDimitry Andric } 161*0b57cec5SDimitry Andric return true; 162*0b57cec5SDimitry Andric } 163*0b57cec5SDimitry Andric 164*0b57cec5SDimitry Andric bool VisitCompoundStmt(CompoundStmt *S) { 165*0b57cec5SDimitry Andric for (auto *I : S->body()) 166*0b57cec5SDimitry Andric check(I); 167*0b57cec5SDimitry Andric return true; 168*0b57cec5SDimitry Andric } 169*0b57cec5SDimitry Andric 170*0b57cec5SDimitry Andric ASTContext &getContext() { return Pass.Ctx; } 171*0b57cec5SDimitry Andric 172*0b57cec5SDimitry Andric private: 173*0b57cec5SDimitry Andric void check(Stmt *S) { 174*0b57cec5SDimitry Andric if (!S) return; 175*0b57cec5SDimitry Andric if (EmptyChecker(Pass.Ctx, Pass.ARCMTMacroLocs).Visit(S)) { 176*0b57cec5SDimitry Andric Transaction Trans(Pass.TA); 177*0b57cec5SDimitry Andric Pass.TA.removeStmt(S); 178*0b57cec5SDimitry Andric } 179*0b57cec5SDimitry Andric } 180*0b57cec5SDimitry Andric }; 181*0b57cec5SDimitry Andric 182*0b57cec5SDimitry Andric } // anonymous namespace 183*0b57cec5SDimitry Andric 184*0b57cec5SDimitry Andric static bool isBodyEmpty(CompoundStmt *body, ASTContext &Ctx, 185*0b57cec5SDimitry Andric std::vector<SourceLocation> &MacroLocs) { 186*0b57cec5SDimitry Andric for (auto *I : body->body()) 187*0b57cec5SDimitry Andric if (!EmptyChecker(Ctx, MacroLocs).Visit(I)) 188*0b57cec5SDimitry Andric return false; 189*0b57cec5SDimitry Andric 190*0b57cec5SDimitry Andric return true; 191*0b57cec5SDimitry Andric } 192*0b57cec5SDimitry Andric 193*0b57cec5SDimitry Andric static void cleanupDeallocOrFinalize(MigrationPass &pass) { 194*0b57cec5SDimitry Andric ASTContext &Ctx = pass.Ctx; 195*0b57cec5SDimitry Andric TransformActions &TA = pass.TA; 196*0b57cec5SDimitry Andric DeclContext *DC = Ctx.getTranslationUnitDecl(); 197*0b57cec5SDimitry Andric Selector FinalizeSel = 198*0b57cec5SDimitry Andric Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize")); 199*0b57cec5SDimitry Andric 200*0b57cec5SDimitry Andric typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl> 201*0b57cec5SDimitry Andric impl_iterator; 202*0b57cec5SDimitry Andric for (impl_iterator I = impl_iterator(DC->decls_begin()), 203*0b57cec5SDimitry Andric E = impl_iterator(DC->decls_end()); I != E; ++I) { 204*0b57cec5SDimitry Andric ObjCMethodDecl *DeallocM = nullptr; 205*0b57cec5SDimitry Andric ObjCMethodDecl *FinalizeM = nullptr; 206*0b57cec5SDimitry Andric for (auto *MD : I->instance_methods()) { 207*0b57cec5SDimitry Andric if (!MD->hasBody()) 208*0b57cec5SDimitry Andric continue; 209*0b57cec5SDimitry Andric 210*0b57cec5SDimitry Andric if (MD->getMethodFamily() == OMF_dealloc) { 211*0b57cec5SDimitry Andric DeallocM = MD; 212*0b57cec5SDimitry Andric } else if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) { 213*0b57cec5SDimitry Andric FinalizeM = MD; 214*0b57cec5SDimitry Andric } 215*0b57cec5SDimitry Andric } 216*0b57cec5SDimitry Andric 217*0b57cec5SDimitry Andric if (DeallocM) { 218*0b57cec5SDimitry Andric if (isBodyEmpty(DeallocM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { 219*0b57cec5SDimitry Andric Transaction Trans(TA); 220*0b57cec5SDimitry Andric TA.remove(DeallocM->getSourceRange()); 221*0b57cec5SDimitry Andric } 222*0b57cec5SDimitry Andric 223*0b57cec5SDimitry Andric if (FinalizeM) { 224*0b57cec5SDimitry Andric Transaction Trans(TA); 225*0b57cec5SDimitry Andric TA.remove(FinalizeM->getSourceRange()); 226*0b57cec5SDimitry Andric } 227*0b57cec5SDimitry Andric 228*0b57cec5SDimitry Andric } else if (FinalizeM) { 229*0b57cec5SDimitry Andric if (isBodyEmpty(FinalizeM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { 230*0b57cec5SDimitry Andric Transaction Trans(TA); 231*0b57cec5SDimitry Andric TA.remove(FinalizeM->getSourceRange()); 232*0b57cec5SDimitry Andric } else { 233*0b57cec5SDimitry Andric Transaction Trans(TA); 234*0b57cec5SDimitry Andric TA.replaceText(FinalizeM->getSelectorStartLoc(), "finalize", "dealloc"); 235*0b57cec5SDimitry Andric } 236*0b57cec5SDimitry Andric } 237*0b57cec5SDimitry Andric } 238*0b57cec5SDimitry Andric } 239*0b57cec5SDimitry Andric 240*0b57cec5SDimitry Andric void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) { 241*0b57cec5SDimitry Andric EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); 242*0b57cec5SDimitry Andric 243*0b57cec5SDimitry Andric cleanupDeallocOrFinalize(pass); 244*0b57cec5SDimitry Andric 245*0b57cec5SDimitry Andric for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) { 246*0b57cec5SDimitry Andric Transaction Trans(pass.TA); 247*0b57cec5SDimitry Andric pass.TA.remove(pass.ARCMTMacroLocs[i]); 248*0b57cec5SDimitry Andric } 249*0b57cec5SDimitry Andric } 250