diff options
author | Argyrios Kyrtzidis <akyrtzi@gmail.com> | 2011-06-21 20:20:39 +0000 |
---|---|---|
committer | Argyrios Kyrtzidis <akyrtzi@gmail.com> | 2011-06-21 20:20:39 +0000 |
commit | 7196d06c2fb020a91a26e727be1871110b4a0dc9 (patch) | |
tree | f117aa3a32cc801ada74da1ac80d4105e43afc7b | |
parent | c8505ad9182c3ddcfda42bee250b2c32dd1f3219 (diff) |
[arcmt] Break apart Transforms.cpp.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@133539 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/ARCMigrate/CMakeLists.txt | 11 | ||||
-rw-r--r-- | lib/ARCMigrate/TransARCAssign.cpp | 75 | ||||
-rw-r--r-- | lib/ARCMigrate/TransAllocCopyWithZone.cpp | 223 | ||||
-rw-r--r-- | lib/ARCMigrate/TransAutoreleasePool.cpp | 437 | ||||
-rw-r--r-- | lib/ARCMigrate/TransBlockObjCVariable.cpp | 143 | ||||
-rw-r--r-- | lib/ARCMigrate/TransDeallocMethod.cpp | 47 | ||||
-rw-r--r-- | lib/ARCMigrate/TransEmptyStatements.cpp | 161 | ||||
-rw-r--r-- | lib/ARCMigrate/TransProperties.cpp | 260 | ||||
-rw-r--r-- | lib/ARCMigrate/TransRetainReleaseDealloc.cpp | 142 | ||||
-rw-r--r-- | lib/ARCMigrate/TransUnbridgedCasts.cpp | 214 | ||||
-rw-r--r-- | lib/ARCMigrate/TransUnusedInitDelegate.cpp | 75 | ||||
-rw-r--r-- | lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp | 198 | ||||
-rw-r--r-- | lib/ARCMigrate/Transforms.cpp | 2030 | ||||
-rw-r--r-- | lib/ARCMigrate/Transforms.h | 105 |
14 files changed, 2180 insertions, 1941 deletions
diff --git a/lib/ARCMigrate/CMakeLists.txt b/lib/ARCMigrate/CMakeLists.txt index 0049d2ff37..56db384bef 100644 --- a/lib/ARCMigrate/CMakeLists.txt +++ b/lib/ARCMigrate/CMakeLists.txt @@ -4,8 +4,19 @@ add_clang_library(clangARCMigrate ARCMT.cpp ARCMTActions.cpp FileRemapper.cpp + TransAllocCopyWithZone.cpp + TransARCAssign.cpp + TransAutoreleasePool.cpp + TransBlockObjCVariable.cpp + TransDeallocMethod.cpp + TransEmptyStatements.cpp TransformActions.cpp Transforms.cpp + TransProperties.cpp + TransRetainReleaseDealloc.cpp + TransUnbridgedCasts.cpp + TransUnusedInitDelegate.cpp + TransZeroOutPropsInDealloc.cpp ) add_dependencies(clangARCMigrate diff --git a/lib/ARCMigrate/TransARCAssign.cpp b/lib/ARCMigrate/TransARCAssign.cpp new file mode 100644 index 0000000000..8c00df5daa --- /dev/null +++ b/lib/ARCMigrate/TransARCAssign.cpp @@ -0,0 +1,75 @@ +//===--- TransARCAssign.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. +// +//===----------------------------------------------------------------------===// +// +// makeAssignARCSafe: +// +// Add '__strong' where appropriate. +// +// for (id x in collection) { +// x = 0; +// } +// ----> +// for (__strong id x in collection) { +// x = 0; +// } +// +//===----------------------------------------------------------------------===// + +#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 ARCAssignChecker : public RecursiveASTVisitor<ARCAssignChecker> { + MigrationPass &Pass; + llvm::DenseSet<VarDecl *> ModifiedVars; + +public: + ARCAssignChecker(MigrationPass &pass) : Pass(pass) { } + + bool VisitBinaryOperator(BinaryOperator *Exp) { + Expr *E = Exp->getLHS(); + SourceLocation OrigLoc = E->getExprLoc(); + SourceLocation Loc = OrigLoc; + DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()); + if (declRef && isa<VarDecl>(declRef->getDecl())) { + ASTContext &Ctx = Pass.Ctx; + Expr::isModifiableLvalueResult IsLV = E->isModifiableLvalue(Ctx, &Loc); + if (IsLV != Expr::MLV_ConstQualified) + return true; + VarDecl *var = cast<VarDecl>(declRef->getDecl()); + if (var->isARCPseudoStrong()) { + Transaction Trans(Pass.TA); + if (Pass.TA.clearDiagnostic(diag::err_typecheck_arr_assign_enumeration, + Exp->getOperatorLoc())) { + if (!ModifiedVars.count(var)) { + TypeLoc TLoc = var->getTypeSourceInfo()->getTypeLoc(); + Pass.TA.insert(TLoc.getBeginLoc(), "__strong "); + ModifiedVars.insert(var); + } + } + } + } + + return true; + } +}; + +} // anonymous namespace + +void trans::makeAssignARCSafe(MigrationPass &pass) { + ARCAssignChecker assignCheck(pass); + assignCheck.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} diff --git a/lib/ARCMigrate/TransAllocCopyWithZone.cpp b/lib/ARCMigrate/TransAllocCopyWithZone.cpp new file mode 100644 index 0000000000..da7f25d0ea --- /dev/null +++ b/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()); +} diff --git a/lib/ARCMigrate/TransAutoreleasePool.cpp b/lib/ARCMigrate/TransAutoreleasePool.cpp new file mode 100644 index 0000000000..602ac0cd02 --- /dev/null +++ b/lib/ARCMigrate/TransAutoreleasePool.cpp @@ -0,0 +1,437 @@ +//===--- TransAutoreleasePool.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. +// +//===----------------------------------------------------------------------===// +// +// rewriteAutoreleasePool: +// +// Calls to NSAutoreleasePools will be rewritten as an @autorelease scope. +// +// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +// ... +// [pool release]; +// ----> +// @autorelease { +// ... +// } +// +// An NSAutoreleasePool will not be touched if: +// - There is not a corresponding -release/-drain in the same scope +// - Not all references of the NSAutoreleasePool variable can be removed +// - There is a variable that is declared inside the intended @autorelease scope +// which is also used outside it. +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Basic/SourceManager.h" +#include <map> + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class ReleaseCollector : public RecursiveASTVisitor<ReleaseCollector> { + Decl *Dcl; + llvm::SmallVectorImpl<ObjCMessageExpr *> &Releases; + +public: + ReleaseCollector(Decl *D, llvm::SmallVectorImpl<ObjCMessageExpr *> &releases) + : Dcl(D), Releases(releases) { } + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + if (!E->isInstanceMessage()) + return true; + if (E->getMethodFamily() != OMF_release) + return true; + Expr *instance = E->getInstanceReceiver()->IgnoreParenCasts(); + if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(instance)) { + if (DE->getDecl() == Dcl) + Releases.push_back(E); + } + return true; + } +}; + +} + +namespace { + +class AutoreleasePoolRewriter + : public RecursiveASTVisitor<AutoreleasePoolRewriter> { +public: + AutoreleasePoolRewriter(Decl *D, MigrationPass &pass) + : Dcl(D), Body(0), Pass(pass) { + PoolII = &pass.Ctx.Idents.get("NSAutoreleasePool"); + DrainSel = pass.Ctx.Selectors.getNullarySelector( + &pass.Ctx.Idents.get("drain")); + } + + void transformBody(Stmt *body) { + Body = body; + TraverseStmt(body); + } + + ~AutoreleasePoolRewriter() { + llvm::SmallVector<VarDecl *, 8> VarsToHandle; + + for (std::map<VarDecl *, PoolVarInfo>::iterator + I = PoolVars.begin(), E = PoolVars.end(); I != E; ++I) { + VarDecl *var = I->first; + PoolVarInfo &info = I->second; + + // Check that we can handle/rewrite all references of the pool. + + clearRefsIn(info.Dcl, info.Refs); + for (llvm::SmallVectorImpl<PoolScope>::iterator + scpI = info.Scopes.begin(), + scpE = info.Scopes.end(); scpI != scpE; ++scpI) { + PoolScope &scope = *scpI; + clearRefsIn(*scope.Begin, info.Refs); + clearRefsIn(*scope.End, info.Refs); + clearRefsIn(scope.Releases.begin(), scope.Releases.end(), info.Refs); + } + + // Even if one reference is not handled we will not do anything about that + // pool variable. + if (info.Refs.empty()) + VarsToHandle.push_back(var); + } + + for (unsigned i = 0, e = VarsToHandle.size(); i != e; ++i) { + PoolVarInfo &info = PoolVars[VarsToHandle[i]]; + + Transaction Trans(Pass.TA); + + clearUnavailableDiags(info.Dcl); + Pass.TA.removeStmt(info.Dcl); + + // Add "@autoreleasepool { }" + for (llvm::SmallVectorImpl<PoolScope>::iterator + scpI = info.Scopes.begin(), + scpE = info.Scopes.end(); scpI != scpE; ++scpI) { + PoolScope &scope = *scpI; + clearUnavailableDiags(*scope.Begin); + clearUnavailableDiags(*scope.End); + if (scope.IsFollowedBySimpleReturnStmt) { + // Include the return in the scope. + Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {"); + Pass.TA.removeStmt(*scope.End); + Stmt::child_iterator retI = scope.End; + ++retI; + SourceLocation afterSemi = findLocationAfterSemi((*retI)->getLocEnd(), + Pass.Ctx); + assert(afterSemi.isValid() && + "Didn't we check before setting IsFollowedBySimpleReturnStmt " + "to true?"); + Pass.TA.insertAfterToken(afterSemi, "\n}"); + Pass.TA.increaseIndentation( + SourceRange(scope.getIndentedRange().getBegin(), + (*retI)->getLocEnd()), + scope.CompoundParent->getLocStart()); + } else { + Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {"); + Pass.TA.replaceStmt(*scope.End, "}"); + Pass.TA.increaseIndentation(scope.getIndentedRange(), + scope.CompoundParent->getLocStart()); + } + } + + // Remove rest of pool var references. + for (llvm::SmallVectorImpl<PoolScope>::iterator + scpI = info.Scopes.begin(), + scpE = info.Scopes.end(); scpI != scpE; ++scpI) { + PoolScope &scope = *scpI; + for (llvm::SmallVectorImpl<ObjCMessageExpr *>::iterator + relI = scope.Releases.begin(), + relE = scope.Releases.end(); relI != relE; ++relI) { + clearUnavailableDiags(*relI); + Pass.TA.removeStmt(*relI); + } + } + } + } + + bool VisitCompoundStmt(CompoundStmt *S) { + llvm::SmallVector<PoolScope, 4> Scopes; + + for (Stmt::child_iterator + I = S->body_begin(), E = S->body_end(); I != E; ++I) { + Stmt *child = getEssential(*I); + if (DeclStmt *DclS = dyn_cast<DeclStmt>(child)) { + if (DclS->isSingleDecl()) { + if (VarDecl *VD = dyn_cast<VarDecl>(DclS->getSingleDecl())) { + if (isNSAutoreleasePool(VD->getType())) { + PoolVarInfo &info = PoolVars[VD]; + info.Dcl = DclS; + collectRefs(VD, S, info.Refs); + // Does this statement follow the pattern: + // NSAutoreleasePool * pool = [NSAutoreleasePool new]; + if (isPoolCreation(VD->getInit())) { + Scopes.push_back(PoolScope()); + Scopes.back().PoolVar = VD; + Scopes.back().CompoundParent = S; + Scopes.back().Begin = I; + } + } + } + } + } else if (BinaryOperator *bop = dyn_cast<BinaryOperator>(child)) { + if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(bop->getLHS())) { + if (VarDecl *VD = dyn_cast<VarDecl>(dref->getDecl())) { + // Does this statement follow the pattern: + // pool = [NSAutoreleasePool new]; + if (isNSAutoreleasePool(VD->getType()) && + isPoolCreation(bop->getRHS())) { + Scopes.push_back(PoolScope()); + Scopes.back().PoolVar = VD; + Scopes.back().CompoundParent = S; + Scopes.back().Begin = I; + } + } + } + } + + if (Scopes.empty()) + continue; + + if (isPoolDrain(Scopes.back().PoolVar, child)) { + PoolScope &scope = Scopes.back(); + scope.End = I; + handlePoolScope(scope, S); + Scopes.pop_back(); + } + } + return true; + } + +private: + void clearUnavailableDiags(Stmt *S) { + if (S) + Pass.TA.clearDiagnostic(diag::err_unavailable, + diag::err_unavailable_message, + S->getSourceRange()); + } + + struct PoolScope { + VarDecl *PoolVar; + CompoundStmt *CompoundParent; + Stmt::child_iterator Begin; + Stmt::child_iterator End; + bool IsFollowedBySimpleReturnStmt; + llvm::SmallVector<ObjCMessageExpr *, 4> Releases; + + PoolScope() : PoolVar(0), CompoundParent(0), Begin(), End(), + IsFollowedBySimpleReturnStmt(false) { } + + SourceRange getIndentedRange() const { + Stmt::child_iterator rangeS = Begin; + ++rangeS; + if (rangeS == End) + return SourceRange(); + Stmt::child_iterator rangeE = Begin; + for (Stmt::child_iterator I = rangeS; I != End; ++I) + ++rangeE; + return SourceRange((*rangeS)->getLocStart(), (*rangeE)->getLocEnd()); + } + }; + + class NameReferenceChecker : public RecursiveASTVisitor<NameReferenceChecker>{ + ASTContext &Ctx; + SourceRange ScopeRange; + SourceLocation &referenceLoc, &declarationLoc; + + public: + NameReferenceChecker(ASTContext &ctx, PoolScope &scope, + SourceLocation &referenceLoc, + SourceLocation &declarationLoc) + : Ctx(ctx), referenceLoc(referenceLoc), + declarationLoc(declarationLoc) { + ScopeRange = SourceRange((*scope.Begin)->getLocStart(), + (*scope.End)->getLocStart()); + } + + bool VisitDeclRefExpr(DeclRefExpr *E) { + return checkRef(E->getLocation(), E->getDecl()->getLocation()); + } + + bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { + return checkRef(E->getLocation(), E->getDecl()->getLocation()); + } + + bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { + return checkRef(TL.getBeginLoc(), TL.getTypedefNameDecl()->getLocation()); + } + + bool VisitTagTypeLoc(TagTypeLoc TL) { + return checkRef(TL.getBeginLoc(), TL.getDecl()->getLocation()); + } + + private: + bool checkRef(SourceLocation refLoc, SourceLocation declLoc) { + if (isInScope(declLoc)) { + referenceLoc = refLoc; + declarationLoc = declLoc; + return false; + } + return true; + } + + bool isInScope(SourceLocation loc) { + SourceManager &SM = Ctx.getSourceManager(); + if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin())) + return false; + return SM.isBeforeInTranslationUnit(loc, ScopeRange.getEnd()); + } + }; + + void handlePoolScope(PoolScope &scope, CompoundStmt *compoundS) { + // Check that all names declared inside the scope are not used + // outside the scope. + { + bool nameUsedOutsideScope = false; + SourceLocation referenceLoc, declarationLoc; + Stmt::child_iterator SI = scope.End, SE = compoundS->body_end(); + ++SI; + // Check if the autoreleasepool scope is followed by a simple return + // statement, in which case we will include the return in the scope. + if (SI != SE) + if (ReturnStmt *retS = dyn_cast<ReturnStmt>(*SI)) + if ((retS->getRetValue() == 0 || + isa<DeclRefExpr>(retS->getRetValue()->IgnoreParenCasts())) && + findLocationAfterSemi(retS->getLocEnd(), Pass.Ctx).isValid()) { + scope.IsFollowedBySimpleReturnStmt = true; + ++SI; // the return will be included in scope, don't check it. + } + + for (; SI != SE; ++SI) { + nameUsedOutsideScope = !NameReferenceChecker(Pass.Ctx, scope, + referenceLoc, + declarationLoc).TraverseStmt(*SI); + if (nameUsedOutsideScope) + break; + } + + // If not all references were cleared it means some variables/typenames/etc + // declared inside the pool scope are used outside of it. + // We won't try to rewrite the pool. + if (nameUsedOutsideScope) { + Pass.TA.reportError("a name is referenced outside the " + "NSAutoreleasePool scope that it was declared in", referenceLoc); + Pass.TA.reportNote("name declared here", declarationLoc); + Pass.TA.reportNote("intended @autoreleasepool scope begins here", + (*scope.Begin)->getLocStart()); + Pass.TA.reportNote("intended @autoreleasepool scope ends here", + (*scope.End)->getLocStart()); + return; + } + } + + // Collect all releases of the pool; they will be removed. + { + ReleaseCollector releaseColl(scope.PoolVar, scope.Releases); + Stmt::child_iterator I = scope.Begin; + ++I; + for (; I != scope.End; ++I) + releaseColl.TraverseStmt(*I); + } + + PoolVars[scope.PoolVar].Scopes.push_back(scope); + } + + bool isPoolCreation(Expr *E) { + if (!E) return false; + E = getEssential(E); + ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E); + if (!ME) return false; + if (ME->getMethodFamily() == OMF_new && + ME->getReceiverKind() == ObjCMessageExpr::Class && + isNSAutoreleasePool(ME->getReceiverInterface())) + return true; + if (ME->getReceiverKind() == ObjCMessageExpr::Instance && + ME->getMethodFamily() == OMF_init) { + Expr *rec = getEssential(ME->getInstanceReceiver()); + if (ObjCMessageExpr *recME = dyn_cast_or_null<ObjCMessageExpr>(rec)) { + if (recME->getMethodFamily() == OMF_alloc && + recME->getReceiverKind() == ObjCMessageExpr::Class && + isNSAutoreleasePool(recME->getReceiverInterface())) + return true; + } + } + + return false; + } + + bool isPoolDrain(VarDecl *poolVar, Stmt *S) { + if (!S) return false; + S = getEssential(S); + ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S); + if (!ME) return false; + if (ME->getReceiverKind() == ObjCMessageExpr::Instance) { + Expr *rec = getEssential(ME->getInstanceReceiver()); + if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(rec)) + if (dref->getDecl() == poolVar) + return ME->getMethodFamily() == OMF_release || + ME->getSelector() == DrainSel; + } + + return false; + } + + bool isNSAutoreleasePool(ObjCInterfaceDecl *IDecl) { + return IDecl && IDecl->getIdentifier() == PoolII; + } + + bool isNSAutoreleasePool(QualType Ty) { + QualType pointee = Ty->getPointeeType(); + if (pointee.isNull()) + return false; + if (const ObjCInterfaceType *interT = pointee->getAs<ObjCInterfaceType>()) + return isNSAutoreleasePool(interT->getDecl()); + return false; + } + + static Expr *getEssential(Expr *E) { + return cast<Expr>(getEssential((Stmt*)E)); + } + static Stmt *getEssential(Stmt *S) { + if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(S)) + S = EWC->getSubExpr(); + if (Expr *E = dyn_cast<Expr>(S)) + S = E->IgnoreParenCasts(); + return S; + } + + Decl *Dcl; + Stmt *Body; + MigrationPass &Pass; + + IdentifierInfo *PoolII; + Selector DrainSel; + + struct PoolVarInfo { + DeclStmt *Dcl; + ExprSet Refs; + llvm::SmallVector<PoolScope, 2> Scopes; + + PoolVarInfo() : Dcl(0) { } + }; + + std::map<VarDecl *, PoolVarInfo> PoolVars; +}; + +} // anonymous namespace + +void trans::rewriteAutoreleasePool(MigrationPass &pass) { + BodyTransform<AutoreleasePoolRewriter> trans(pass); + trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} diff --git a/lib/ARCMigrate/TransBlockObjCVariable.cpp b/lib/ARCMigrate/TransBlockObjCVariable.cpp new file mode 100644 index 0000000000..ba556277cc --- /dev/null +++ b/lib/ARCMigrate/TransBlockObjCVariable.cpp @@ -0,0 +1,143 @@ +//===--- TransBlockObjCVariable.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. +// +//===----------------------------------------------------------------------===// +// +// rewriteBlockObjCVariable: +// +// Adding __block to an obj-c variable could be either because the the variable +// is used for output storage or the user wanted to break a retain cycle. +// This transformation checks whether a reference of the variable for the block +// is actually needed (it is assigned to or its address is taken) or not. +// If the reference is not needed it will assume __block was added to break a +// cycle so it will remove '__block' and add __weak/__unsafe_unretained. +// e.g +// +// __block Foo *x; +// bar(^ { [x cake]; }); +// ----> +// __weak Foo *x; +// bar(^ { [x cake]; }); +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Basic/SourceManager.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class RootBlockObjCVarRewriter : + public RecursiveASTVisitor<RootBlockObjCVarRewriter> { + MigrationPass &Pass; + llvm::DenseSet<VarDecl *> CheckedVars; + + class BlockVarChecker : public RecursiveASTVisitor<BlockVarChecker> { + VarDecl *Var; + + typedef RecursiveASTVisitor<BlockVarChecker> base; + public: + BlockVarChecker(VarDecl *var) : Var(var) { } + + bool TraverseImplicitCastExpr(ImplicitCastExpr *castE) { + if (BlockDeclRefExpr * + ref = dyn_cast<BlockDeclRefExpr>(castE->getSubExpr())) { + if (ref->getDecl() == Var) { + if (castE->getCastKind() == CK_LValueToRValue) + return true; // Using the value of the variable. + if (castE->getCastKind() == CK_NoOp && castE->isLValue() && + Var->getASTContext().getLangOptions().CPlusPlus) + return true; // Binding to const C++ reference. + } + } + + return base::TraverseImplicitCastExpr(castE); + } + + bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { + if (E->getDecl() == Var) + return false; // The reference of the variable, and not just its value, + // is needed. + return true; + } + }; + +public: + RootBlockObjCVarRewriter(MigrationPass &pass) : Pass(pass) { } + + bool VisitBlockDecl(BlockDecl *block) { + llvm::SmallVector<VarDecl *, 4> BlockVars; + + for (BlockDecl::capture_iterator + I = block->capture_begin(), E = block->capture_end(); I != E; ++I) { + VarDecl *var = I->getVariable(); + if (I->isByRef() && + !isAlreadyChecked(var) && + var->getType()->isObjCObjectPointerType() && + isImplicitStrong(var->getType())) { + BlockVars.push_back(var); + } + } + + for (unsigned i = 0, e = BlockVars.size(); i != e; ++i) { + VarDecl *var = BlockVars[i]; + CheckedVars.insert(var); + + BlockVarChecker checker(var); + bool onlyValueOfVarIsNeeded = checker.TraverseStmt(block->getBody()); + if (onlyValueOfVarIsNeeded) { + BlocksAttr *attr = var->getAttr<BlocksAttr>(); + if(!attr) + continue; + bool hasARCRuntime = !Pass.Ctx.getLangOptions().ObjCNoAutoRefCountRuntime; + SourceManager &SM = Pass.Ctx.getSourceManager(); + Transaction Trans(Pass.TA); + Pass.TA.replaceText(SM.getInstantiationLoc(attr->getLocation()), + "__block", + hasARCRuntime ? "__weak" : "__unsafe_unretained"); + } + + } + + return true; + } + +private: + bool isAlreadyChecked(VarDecl *VD) { + return CheckedVars.count(VD); + } + + bool isImplicitStrong(QualType ty) { + if (isa<AttributedType>(ty.getTypePtr())) + return false; + return ty.getLocalQualifiers().getObjCLifetime() == Qualifiers::OCL_Strong; + } +}; + +class BlockObjCVarRewriter : public RecursiveASTVisitor<BlockObjCVarRewriter> { + MigrationPass &Pass; + +public: + BlockObjCVarRewriter(MigrationPass &pass) : Pass(pass) { } + + bool TraverseBlockDecl(BlockDecl *block) { + RootBlockObjCVarRewriter(Pass).TraverseDecl(block); + return true; + } +}; + +} // anonymous namespace + +void trans::rewriteBlockObjCVariable(MigrationPass &pass) { + BlockObjCVarRewriter trans(pass); + trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} diff --git a/lib/ARCMigrate/TransDeallocMethod.cpp b/lib/ARCMigrate/TransDeallocMethod.cpp new file mode 100644 index 0000000000..a7c3c1e9d6 --- /dev/null +++ b/lib/ARCMigrate/TransDeallocMethod.cpp @@ -0,0 +1,47 @@ +//===--- TransDeallocMethod.cpp - Tranformations to ARC mode --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +/ |