diff options
Diffstat (limited to 'clang/lib/ARCMigrate/TransAllocCopyWithZone.cpp')
-rw-r--r-- | clang/lib/ARCMigrate/TransAllocCopyWithZone.cpp | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/clang/lib/ARCMigrate/TransAllocCopyWithZone.cpp b/clang/lib/ARCMigrate/TransAllocCopyWithZone.cpp new file mode 100644 index 00000000000..da7f25d0ea9 --- /dev/null +++ b/clang/lib/ARCMigrate/TransAllocCopyWithZone.cpp @@ -0,0 +1,223 @@ +//===--- TransAllocCopyWithZone.cpp - Tranformations to ARC mode ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// rewriteAllocCopyWithZone: +// +// Calls to +allocWithZone/-copyWithZone/-mutableCopyWithZone are changed to +// +alloc/-copy/-mutableCopy if we can safely remove the given parameter. +// +// Foo *foo1 = [[Foo allocWithZone:[self zone]] init]; +// ----> +// Foo *foo1 = [[Foo alloc] init]; +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Sema/SemaDiagnostic.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class AllocCopyWithZoneRewriter : + public RecursiveASTVisitor<AllocCopyWithZoneRewriter> { + Decl *Dcl; + Stmt *Body; + MigrationPass &Pass; + + Selector allocWithZoneSel; + Selector copyWithZoneSel; + Selector mutableCopyWithZoneSel; + Selector zoneSel; + IdentifierInfo *NSZoneII; + + std::vector<DeclStmt *> NSZoneVars; + std::vector<Expr *> Removals; + +public: + AllocCopyWithZoneRewriter(Decl *D, MigrationPass &pass) + : Dcl(D), Body(0), Pass(pass) { + SelectorTable &sels = pass.Ctx.Selectors; + IdentifierTable &ids = pass.Ctx.Idents; + allocWithZoneSel = sels.getUnarySelector(&ids.get("allocWithZone")); + copyWithZoneSel = sels.getUnarySelector(&ids.get("copyWithZone")); + mutableCopyWithZoneSel = sels.getUnarySelector( + &ids.get("mutableCopyWithZone")); + zoneSel = sels.getNullarySelector(&ids.get("zone")); + NSZoneII = &ids.get("_NSZone"); + } + + void transformBody(Stmt *body) { + Body = body; + // Don't change allocWithZone/copyWithZone messages inside + // custom implementations of such methods, it can lead to infinite loops. + if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(Dcl)) { + Selector sel = MD->getSelector(); + if (sel == allocWithZoneSel || + sel == copyWithZoneSel || + sel == mutableCopyWithZoneSel || + sel == zoneSel) + return; + } + + TraverseStmt(body); + } + + ~AllocCopyWithZoneRewriter() { + for (std::vector<DeclStmt *>::reverse_iterator + I = NSZoneVars.rbegin(), E = NSZoneVars.rend(); I != E; ++I) { + DeclStmt *DS = *I; + DeclGroupRef group = DS->getDeclGroup(); + std::vector<Expr *> varRemovals = Removals; + + bool areAllVarsUnused = true; + for (std::reverse_iterator<DeclGroupRef::iterator> + DI(group.end()), DE(group.begin()); DI != DE; ++DI) { + VarDecl *VD = cast<VarDecl>(*DI); + if (isNSZoneVarUsed(VD, varRemovals)) { + areAllVarsUnused = false; + break; + } + varRemovals.push_back(VD->getInit()); + } + + if (areAllVarsUnused) { + Transaction Trans(Pass.TA); + clearUnavailableDiags(DS); + Pass.TA.removeStmt(DS); + Removals.swap(varRemovals); + } + } + } + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + if (!isAllocCopyWithZoneCall(E)) + return true; + Expr *arg = E->getArg(0); + if (paramToAllocWithZoneHasSideEffects(arg)) + return true; + + Pass.TA.startTransaction(); + + clearUnavailableDiags(arg); + Pass.TA.clearDiagnostic(diag::err_unavailable_message, + E->getReceiverRange().getBegin()); + + Pass.TA.remove(SourceRange(E->getSelectorLoc(), arg->getLocEnd())); + StringRef rewrite; + if (E->getSelector() == allocWithZoneSel) + rewrite = "alloc"; + else if (E->getSelector() == copyWithZoneSel) + rewrite = "copy"; + else { + assert(E->getSelector() == mutableCopyWithZoneSel); + rewrite = "mutableCopy"; + } + Pass.TA.insert(E->getSelectorLoc(), rewrite); + + bool failed = Pass.TA.commitTransaction(); + if (!failed) + Removals.push_back(arg); + + return true; + } + + bool VisitDeclStmt(DeclStmt *DS) { + DeclGroupRef group = DS->getDeclGroup(); + if (group.begin() == group.end()) + return true; + for (DeclGroupRef::iterator + DI = group.begin(), DE = group.end(); DI != DE; ++DI) + if (!isRemovableNSZoneVar(*DI)) + return true; + + NSZoneVars.push_back(DS); + return true; + } + +private: + bool isRemovableNSZoneVar(Decl *D) { + if (VarDecl *VD = dyn_cast<VarDecl>(D)) { + if (isNSZone(VD->getType())) + return !paramToAllocWithZoneHasSideEffects(VD->getInit()); + } + return false; + } + + bool isNSZone(RecordDecl *RD) { + return RD && RD->getIdentifier() == NSZoneII; + } + + bool isNSZone(QualType Ty) { + QualType pointee = Ty->getPointeeType(); + if (pointee.isNull()) + return false; + if (const RecordType *recT = pointee->getAsStructureType()) + return isNSZone(recT->getDecl()); + return false; + } + + bool isNSZoneVarUsed(VarDecl *D, std::vector<Expr *> &removals) { + ExprSet refs; + collectRefs(D, Body, refs); + clearRefsIn(removals.begin(), removals.end(), refs); + + return !refs.empty(); + } + + bool isAllocCopyWithZoneCall(ObjCMessageExpr *E) { + if (E->getNumArgs() == 1 && + E->getSelector() == allocWithZoneSel && + (E->isClassMessage() || + Pass.TA.hasDiagnostic(diag::err_unavailable_message, + E->getReceiverRange().getBegin()))) + return true; + + return E->isInstanceMessage() && + E->getNumArgs() == 1 && + (E->getSelector() == copyWithZoneSel || + E->getSelector() == mutableCopyWithZoneSel); + } + + bool isZoneCall(ObjCMessageExpr *E) { + return E->isInstanceMessage() && + E->getNumArgs() == 0 && + E->getSelector() == zoneSel; + } + + bool paramToAllocWithZoneHasSideEffects(Expr *E) { + if (!hasSideEffects(E, Pass.Ctx)) + return false; + E = E->IgnoreParenCasts(); + ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E); + if (!ME) + return true; + if (!isZoneCall(ME)) + return true; + return hasSideEffects(ME->getInstanceReceiver(), Pass.Ctx); + } + + void clearUnavailableDiags(Stmt *S) { + if (S) + Pass.TA.clearDiagnostic(diag::err_unavailable, + diag::err_unavailable_message, + S->getSourceRange()); + } +}; + +} // end anonymous namespace + +void trans::rewriteAllocCopyWithZone(MigrationPass &pass) { + BodyTransform<AllocCopyWithZoneRewriter> trans(pass); + trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} |