//===--- TransEmptyStatements.cpp - Transformations to ARC mode -----------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // removeEmptyStatementsAndDealloc: // // Removes empty statements that are leftovers from previous transformations. // e.g for // // [x retain]; // // removeRetainReleaseDealloc will leave an empty ";" that removeEmptyStatements // will remove. // //===----------------------------------------------------------------------===// #include "Transforms.h" #include "Internals.h" #include "clang/AST/ASTContext.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/SourceManager.h" using namespace clang; using namespace arcmt; using namespace trans; static bool isEmptyARCMTMacroStatement(NullStmt *S, std::vector<SourceLocation> &MacroLocs, ASTContext &Ctx) { if (!S->hasLeadingEmptyMacro()) return false; SourceLocation SemiLoc = S->getSemiLoc(); if (SemiLoc.isInvalid() || SemiLoc.isMacroID()) return false; if (MacroLocs.empty()) return false; SourceManager &SM = Ctx.getSourceManager(); std::vector<SourceLocation>::iterator I = std::upper_bound(MacroLocs.begin(), MacroLocs.end(), SemiLoc, BeforeThanCompare<SourceLocation>(SM)); --I; SourceLocation AfterMacroLoc = I->getLocWithOffset(getARCMTMacroName().size()); assert(AfterMacroLoc.isFileID()); if (AfterMacroLoc == SemiLoc) return true; int RelOffs = 0; if (!SM.isInSameSLocAddrSpace(AfterMacroLoc, SemiLoc, &RelOffs)) return false; if (RelOffs < 0) return false; // We make the reasonable assumption that a semicolon after 100 characters // means that it is not the next token after our macro. If this assumption // fails it is not critical, we will just fail to clear out, e.g., an empty // 'if'. if (RelOffs - getARCMTMacroName().size() > 100) return false; SourceLocation AfterMacroSemiLoc = findSemiAfterLocation(AfterMacroLoc, Ctx); return AfterMacroSemiLoc == SemiLoc; } namespace { /// \brief Returns true if the statement became empty due to previous /// transformations. class EmptyChecker : public StmtVisitor<EmptyChecker, bool> { ASTContext &Ctx; std::vector<SourceLocation> &MacroLocs; public: EmptyChecker(ASTContext &ctx, std::vector<SourceLocation> ¯oLocs) : Ctx(ctx), MacroLocs(macroLocs) { } bool VisitNullStmt(NullStmt *S) { return isEmptyARCMTMacroStatement(S, MacroLocs, Ctx); } bool VisitCompoundStmt(CompoundStmt *S) { if (S->body_empty()) return false; // was already empty, not because of transformations. for (auto *I : S->body()) if (!Visit(I)) return false; return true; } bool VisitIfStmt(IfStmt *S) { if (S->getConditionVariable()) return false; Expr *condE = S->getCond(); if (!condE) return false; if (hasSideEffects(condE, Ctx)) return false; if (!S->getThen() || !Visit(S->getThen())) return false; if (S->getElse() && !Visit(S->getElse())) return false; return true; } bool VisitWhileStmt(WhileStmt *S) { if (S->getConditionVariable()) return false; Expr *condE = S->getCond(); if (!condE) return false; if (hasSideEffects(condE, Ctx)) return false; if (!S->getBody()) return false; return Visit(S->getBody()); } bool VisitDoStmt(DoStmt *S) { Expr *condE = S->getCond(); if (!condE) return false; if (hasSideEffects(condE, Ctx)) return false; if (!S->getBody()) return false; return Visit(S->getBody()); } bool VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { Expr *Exp = S->getCollection(); if (!Exp) return false; if (hasSideEffects(Exp, Ctx)) return false; if (!S->getBody()) return false; return Visit(S->getBody()); } bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { if (!S->getSubStmt()) return false; return Visit(S->getSubStmt()); } }; class EmptyStatementsRemover : public RecursiveASTVisitor<EmptyStatementsRemover> { MigrationPass &Pass; public: EmptyStatementsRemover(MigrationPass &pass) : Pass(pass) { } bool TraverseStmtExpr(StmtExpr *E) { CompoundStmt *S = E->getSubStmt(); for (CompoundStmt::body_iterator I = S->body_begin(), E = S->body_end(); I != E; ++I) { if (I != E - 1) check(*I); TraverseStmt(*I); } return true; } bool VisitCompoundStmt(CompoundStmt *S) { for (auto *I : S->body()) check(I); return true; } ASTContext &getContext() { return Pass.Ctx; } private: void check(Stmt *S) { if (!S) return; if (EmptyChecker(Pass.Ctx, Pass.ARCMTMacroLocs).Visit(S)) { Transaction Trans(Pass.TA); Pass.TA.removeStmt(S); } } }; } // anonymous namespace static bool isBodyEmpty(CompoundStmt *body, ASTContext &Ctx, std::vector<SourceLocation> &MacroLocs) { for (auto *I : body->body()) if (!EmptyChecker(Ctx, MacroLocs).Visit(I)) return false; return true; } static void cleanupDeallocOrFinalize(MigrationPass &pass) { ASTContext &Ctx = pass.Ctx; TransformActions &TA = pass.TA; DeclContext *DC = Ctx.getTranslationUnitDecl(); Selector FinalizeSel = Ctx.Selectors.getNullarySelector(&pass.Ctx.Idents.get("finalize")); typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl> impl_iterator; for (impl_iterator I = impl_iterator(DC->decls_begin()), E = impl_iterator(DC->decls_end()); I != E; ++I) { ObjCMethodDecl *DeallocM = nullptr; ObjCMethodDecl *FinalizeM = nullptr; for (auto *MD : I->instance_methods()) { if (!MD->hasBody()) continue; if (MD->getMethodFamily() == OMF_dealloc) { DeallocM = MD; } else if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) { FinalizeM = MD; } } if (DeallocM) { if (isBodyEmpty(DeallocM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { Transaction Trans(TA); TA.remove(DeallocM->getSourceRange()); } if (FinalizeM) { Transaction Trans(TA); TA.remove(FinalizeM->getSourceRange()); } } else if (FinalizeM) { if (isBodyEmpty(FinalizeM->getCompoundBody(), Ctx, pass.ARCMTMacroLocs)) { Transaction Trans(TA); TA.remove(FinalizeM->getSourceRange()); } else { Transaction Trans(TA); TA.replaceText(FinalizeM->getSelectorStartLoc(), "finalize", "dealloc"); } } } } void trans::removeEmptyStatementsAndDeallocFinalize(MigrationPass &pass) { EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); cleanupDeallocOrFinalize(pass); for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) { Transaction Trans(pass.TA); pass.TA.remove(pass.ARCMTMacroLocs[i]); } }