aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>2011-06-21 20:20:39 +0000
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>2011-06-21 20:20:39 +0000
commit7196d06c2fb020a91a26e727be1871110b4a0dc9 (patch)
treef117aa3a32cc801ada74da1ac80d4105e43afc7b
parentc8505ad9182c3ddcfda42bee250b2c32dd1f3219 (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.txt11
-rw-r--r--lib/ARCMigrate/TransARCAssign.cpp75
-rw-r--r--lib/ARCMigrate/TransAllocCopyWithZone.cpp223
-rw-r--r--lib/ARCMigrate/TransAutoreleasePool.cpp437
-rw-r--r--lib/ARCMigrate/TransBlockObjCVariable.cpp143
-rw-r--r--lib/ARCMigrate/TransDeallocMethod.cpp47
-rw-r--r--lib/ARCMigrate/TransEmptyStatements.cpp161
-rw-r--r--lib/ARCMigrate/TransProperties.cpp260
-rw-r--r--lib/ARCMigrate/TransRetainReleaseDealloc.cpp142
-rw-r--r--lib/ARCMigrate/TransUnbridgedCasts.cpp214
-rw-r--r--lib/ARCMigrate/TransUnusedInitDelegate.cpp75
-rw-r--r--lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp198
-rw-r--r--lib/ARCMigrate/Transforms.cpp2030
-rw-r--r--lib/ARCMigrate/Transforms.h105
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
+/