f785676f2a
all of the features in the current working draft of the upcoming C++ standard, provisionally named C++1y. The code generator's performance is greatly increased, and the loop auto-vectorizer is now enabled at -Os and -O2 in addition to -O3. The PowerPC backend has made several major improvements to code generation quality and compile time, and the X86, SPARC, ARM32, Aarch64 and SystemZ backends have all seen major feature work. Release notes for llvm and clang can be found here: <http://llvm.org/releases/3.4/docs/ReleaseNotes.html> <http://llvm.org/releases/3.4/tools/clang/docs/ReleaseNotes.html> MFC after: 1 month
611 lines
18 KiB
C++
611 lines
18 KiB
C++
//===--- Transforms.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.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Transforms.h"
|
|
#include "Internals.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
#include "clang/AST/StmtVisitor.h"
|
|
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "clang/Basic/TargetInfo.h"
|
|
#include "clang/Lex/Lexer.h"
|
|
#include "clang/Sema/Sema.h"
|
|
#include "clang/Sema/SemaDiagnostic.h"
|
|
#include "llvm/ADT/DenseSet.h"
|
|
#include "llvm/ADT/StringSwitch.h"
|
|
#include <map>
|
|
|
|
using namespace clang;
|
|
using namespace arcmt;
|
|
using namespace trans;
|
|
|
|
ASTTraverser::~ASTTraverser() { }
|
|
|
|
bool MigrationPass::CFBridgingFunctionsDefined() {
|
|
if (!EnableCFBridgeFns.hasValue())
|
|
EnableCFBridgeFns = SemaRef.isKnownName("CFBridgingRetain") &&
|
|
SemaRef.isKnownName("CFBridgingRelease");
|
|
return *EnableCFBridgeFns;
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Helpers.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
bool trans::canApplyWeak(ASTContext &Ctx, QualType type,
|
|
bool AllowOnUnknownClass) {
|
|
if (!Ctx.getLangOpts().ObjCARCWeak)
|
|
return false;
|
|
|
|
QualType T = type;
|
|
if (T.isNull())
|
|
return false;
|
|
|
|
// iOS is always safe to use 'weak'.
|
|
if (Ctx.getTargetInfo().getTriple().isiOS())
|
|
AllowOnUnknownClass = true;
|
|
|
|
while (const PointerType *ptr = T->getAs<PointerType>())
|
|
T = ptr->getPointeeType();
|
|
if (const ObjCObjectPointerType *ObjT = T->getAs<ObjCObjectPointerType>()) {
|
|
ObjCInterfaceDecl *Class = ObjT->getInterfaceDecl();
|
|
if (!AllowOnUnknownClass && (!Class || Class->getName() == "NSObject"))
|
|
return false; // id/NSObject is not safe for weak.
|
|
if (!AllowOnUnknownClass && !Class->hasDefinition())
|
|
return false; // forward classes are not verifiable, therefore not safe.
|
|
if (Class && Class->isArcWeakrefUnavailable())
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool trans::isPlusOneAssign(const BinaryOperator *E) {
|
|
if (E->getOpcode() != BO_Assign)
|
|
return false;
|
|
|
|
return isPlusOne(E->getRHS());
|
|
}
|
|
|
|
bool trans::isPlusOne(const Expr *E) {
|
|
if (!E)
|
|
return false;
|
|
if (const ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(E))
|
|
E = EWC->getSubExpr();
|
|
|
|
if (const ObjCMessageExpr *
|
|
ME = dyn_cast<ObjCMessageExpr>(E->IgnoreParenCasts()))
|
|
if (ME->getMethodFamily() == OMF_retain)
|
|
return true;
|
|
|
|
if (const CallExpr *
|
|
callE = dyn_cast<CallExpr>(E->IgnoreParenCasts())) {
|
|
if (const FunctionDecl *FD = callE->getDirectCallee()) {
|
|
if (FD->getAttr<CFReturnsRetainedAttr>())
|
|
return true;
|
|
|
|
if (FD->isGlobal() &&
|
|
FD->getIdentifier() &&
|
|
FD->getParent()->isTranslationUnit() &&
|
|
FD->isExternallyVisible() &&
|
|
ento::cocoa::isRefType(callE->getType(), "CF",
|
|
FD->getIdentifier()->getName())) {
|
|
StringRef fname = FD->getIdentifier()->getName();
|
|
if (fname.endswith("Retain") ||
|
|
fname.find("Create") != StringRef::npos ||
|
|
fname.find("Copy") != StringRef::npos) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E);
|
|
while (implCE && implCE->getCastKind() == CK_BitCast)
|
|
implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
|
|
|
|
if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/// \brief 'Loc' is the end of a statement range. This returns the location
|
|
/// immediately after the semicolon following the statement.
|
|
/// If no semicolon is found or the location is inside a macro, the returned
|
|
/// source location will be invalid.
|
|
SourceLocation trans::findLocationAfterSemi(SourceLocation loc,
|
|
ASTContext &Ctx, bool IsDecl) {
|
|
SourceLocation SemiLoc = findSemiAfterLocation(loc, Ctx, IsDecl);
|
|
if (SemiLoc.isInvalid())
|
|
return SourceLocation();
|
|
return SemiLoc.getLocWithOffset(1);
|
|
}
|
|
|
|
/// \brief \arg Loc is the end of a statement range. This returns the location
|
|
/// of the semicolon following the statement.
|
|
/// If no semicolon is found or the location is inside a macro, the returned
|
|
/// source location will be invalid.
|
|
SourceLocation trans::findSemiAfterLocation(SourceLocation loc,
|
|
ASTContext &Ctx,
|
|
bool IsDecl) {
|
|
SourceManager &SM = Ctx.getSourceManager();
|
|
if (loc.isMacroID()) {
|
|
if (!Lexer::isAtEndOfMacroExpansion(loc, SM, Ctx.getLangOpts(), &loc))
|
|
return SourceLocation();
|
|
}
|
|
loc = Lexer::getLocForEndOfToken(loc, /*Offset=*/0, SM, Ctx.getLangOpts());
|
|
|
|
// Break down the source location.
|
|
std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
|
|
|
|
// Try to load the file buffer.
|
|
bool invalidTemp = false;
|
|
StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
|
|
if (invalidTemp)
|
|
return SourceLocation();
|
|
|
|
const char *tokenBegin = file.data() + locInfo.second;
|
|
|
|
// Lex from the start of the given location.
|
|
Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
|
|
Ctx.getLangOpts(),
|
|
file.begin(), tokenBegin, file.end());
|
|
Token tok;
|
|
lexer.LexFromRawLexer(tok);
|
|
if (tok.isNot(tok::semi)) {
|
|
if (!IsDecl)
|
|
return SourceLocation();
|
|
// Declaration may be followed with other tokens; such as an __attribute,
|
|
// before ending with a semicolon.
|
|
return findSemiAfterLocation(tok.getLocation(), Ctx, /*IsDecl*/true);
|
|
}
|
|
|
|
return tok.getLocation();
|
|
}
|
|
|
|
bool trans::hasSideEffects(Expr *E, ASTContext &Ctx) {
|
|
if (!E || !E->HasSideEffects(Ctx))
|
|
return false;
|
|
|
|
E = E->IgnoreParenCasts();
|
|
ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E);
|
|
if (!ME)
|
|
return true;
|
|
switch (ME->getMethodFamily()) {
|
|
case OMF_autorelease:
|
|
case OMF_dealloc:
|
|
case OMF_release:
|
|
case OMF_retain:
|
|
switch (ME->getReceiverKind()) {
|
|
case ObjCMessageExpr::SuperInstance:
|
|
return false;
|
|
case ObjCMessageExpr::Instance:
|
|
return hasSideEffects(ME->getInstanceReceiver(), Ctx);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool trans::isGlobalVar(Expr *E) {
|
|
E = E->IgnoreParenCasts();
|
|
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
|
|
return DRE->getDecl()->getDeclContext()->isFileContext() &&
|
|
DRE->getDecl()->isExternallyVisible();
|
|
if (ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(E))
|
|
return isGlobalVar(condOp->getTrueExpr()) &&
|
|
isGlobalVar(condOp->getFalseExpr());
|
|
|
|
return false;
|
|
}
|
|
|
|
StringRef trans::getNilString(ASTContext &Ctx) {
|
|
if (Ctx.Idents.get("nil").hasMacroDefinition())
|
|
return "nil";
|
|
else
|
|
return "0";
|
|
}
|
|
|
|
namespace {
|
|
|
|
class ReferenceClear : public RecursiveASTVisitor<ReferenceClear> {
|
|
ExprSet &Refs;
|
|
public:
|
|
ReferenceClear(ExprSet &refs) : Refs(refs) { }
|
|
bool VisitDeclRefExpr(DeclRefExpr *E) { Refs.erase(E); return true; }
|
|
};
|
|
|
|
class ReferenceCollector : public RecursiveASTVisitor<ReferenceCollector> {
|
|
ValueDecl *Dcl;
|
|
ExprSet &Refs;
|
|
|
|
public:
|
|
ReferenceCollector(ValueDecl *D, ExprSet &refs)
|
|
: Dcl(D), Refs(refs) { }
|
|
|
|
bool VisitDeclRefExpr(DeclRefExpr *E) {
|
|
if (E->getDecl() == Dcl)
|
|
Refs.insert(E);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class RemovablesCollector : public RecursiveASTVisitor<RemovablesCollector> {
|
|
ExprSet &Removables;
|
|
|
|
public:
|
|
RemovablesCollector(ExprSet &removables)
|
|
: Removables(removables) { }
|
|
|
|
bool shouldWalkTypesOfTypeLocs() const { return false; }
|
|
|
|
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)
|
|
mark(*I);
|
|
TraverseStmt(*I);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool VisitCompoundStmt(CompoundStmt *S) {
|
|
for (CompoundStmt::body_iterator
|
|
I = S->body_begin(), E = S->body_end(); I != E; ++I)
|
|
mark(*I);
|
|
return true;
|
|
}
|
|
|
|
bool VisitIfStmt(IfStmt *S) {
|
|
mark(S->getThen());
|
|
mark(S->getElse());
|
|
return true;
|
|
}
|
|
|
|
bool VisitWhileStmt(WhileStmt *S) {
|
|
mark(S->getBody());
|
|
return true;
|
|
}
|
|
|
|
bool VisitDoStmt(DoStmt *S) {
|
|
mark(S->getBody());
|
|
return true;
|
|
}
|
|
|
|
bool VisitForStmt(ForStmt *S) {
|
|
mark(S->getInit());
|
|
mark(S->getInc());
|
|
mark(S->getBody());
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
void mark(Stmt *S) {
|
|
if (!S) return;
|
|
|
|
while (LabelStmt *Label = dyn_cast<LabelStmt>(S))
|
|
S = Label->getSubStmt();
|
|
S = S->IgnoreImplicit();
|
|
if (Expr *E = dyn_cast<Expr>(S))
|
|
Removables.insert(E);
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
void trans::clearRefsIn(Stmt *S, ExprSet &refs) {
|
|
ReferenceClear(refs).TraverseStmt(S);
|
|
}
|
|
|
|
void trans::collectRefs(ValueDecl *D, Stmt *S, ExprSet &refs) {
|
|
ReferenceCollector(D, refs).TraverseStmt(S);
|
|
}
|
|
|
|
void trans::collectRemovables(Stmt *S, ExprSet &exprs) {
|
|
RemovablesCollector(exprs).TraverseStmt(S);
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// MigrationContext
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
class ASTTransform : public RecursiveASTVisitor<ASTTransform> {
|
|
MigrationContext &MigrateCtx;
|
|
typedef RecursiveASTVisitor<ASTTransform> base;
|
|
|
|
public:
|
|
ASTTransform(MigrationContext &MigrateCtx) : MigrateCtx(MigrateCtx) { }
|
|
|
|
bool shouldWalkTypesOfTypeLocs() const { return false; }
|
|
|
|
bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) {
|
|
ObjCImplementationContext ImplCtx(MigrateCtx, D);
|
|
for (MigrationContext::traverser_iterator
|
|
I = MigrateCtx.traversers_begin(),
|
|
E = MigrateCtx.traversers_end(); I != E; ++I)
|
|
(*I)->traverseObjCImplementation(ImplCtx);
|
|
|
|
return base::TraverseObjCImplementationDecl(D);
|
|
}
|
|
|
|
bool TraverseStmt(Stmt *rootS) {
|
|
if (!rootS)
|
|
return true;
|
|
|
|
BodyContext BodyCtx(MigrateCtx, rootS);
|
|
for (MigrationContext::traverser_iterator
|
|
I = MigrateCtx.traversers_begin(),
|
|
E = MigrateCtx.traversers_end(); I != E; ++I)
|
|
(*I)->traverseBody(BodyCtx);
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
MigrationContext::~MigrationContext() {
|
|
for (traverser_iterator
|
|
I = traversers_begin(), E = traversers_end(); I != E; ++I)
|
|
delete *I;
|
|
}
|
|
|
|
bool MigrationContext::isGCOwnedNonObjC(QualType T) {
|
|
while (!T.isNull()) {
|
|
if (const AttributedType *AttrT = T->getAs<AttributedType>()) {
|
|
if (AttrT->getAttrKind() == AttributedType::attr_objc_ownership)
|
|
return !AttrT->getModifiedType()->isObjCRetainableType();
|
|
}
|
|
|
|
if (T->isArrayType())
|
|
T = Pass.Ctx.getBaseElementType(T);
|
|
else if (const PointerType *PT = T->getAs<PointerType>())
|
|
T = PT->getPointeeType();
|
|
else if (const ReferenceType *RT = T->getAs<ReferenceType>())
|
|
T = RT->getPointeeType();
|
|
else
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MigrationContext::rewritePropertyAttribute(StringRef fromAttr,
|
|
StringRef toAttr,
|
|
SourceLocation atLoc) {
|
|
if (atLoc.isMacroID())
|
|
return false;
|
|
|
|
SourceManager &SM = Pass.Ctx.getSourceManager();
|
|
|
|
// Break down the source location.
|
|
std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
|
|
|
|
// Try to load the file buffer.
|
|
bool invalidTemp = false;
|
|
StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
|
|
if (invalidTemp)
|
|
return false;
|
|
|
|
const char *tokenBegin = file.data() + locInfo.second;
|
|
|
|
// Lex from the start of the given location.
|
|
Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
|
|
Pass.Ctx.getLangOpts(),
|
|
file.begin(), tokenBegin, file.end());
|
|
Token tok;
|
|
lexer.LexFromRawLexer(tok);
|
|
if (tok.isNot(tok::at)) return false;
|
|
lexer.LexFromRawLexer(tok);
|
|
if (tok.isNot(tok::raw_identifier)) return false;
|
|
if (StringRef(tok.getRawIdentifierData(), tok.getLength())
|
|
!= "property")
|
|
return false;
|
|
lexer.LexFromRawLexer(tok);
|
|
if (tok.isNot(tok::l_paren)) return false;
|
|
|
|
Token BeforeTok = tok;
|
|
Token AfterTok;
|
|
AfterTok.startToken();
|
|
SourceLocation AttrLoc;
|
|
|
|
lexer.LexFromRawLexer(tok);
|
|
if (tok.is(tok::r_paren))
|
|
return false;
|
|
|
|
while (1) {
|
|
if (tok.isNot(tok::raw_identifier)) return false;
|
|
StringRef ident(tok.getRawIdentifierData(), tok.getLength());
|
|
if (ident == fromAttr) {
|
|
if (!toAttr.empty()) {
|
|
Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr);
|
|
return true;
|
|
}
|
|
// We want to remove the attribute.
|
|
AttrLoc = tok.getLocation();
|
|
}
|
|
|
|
do {
|
|
lexer.LexFromRawLexer(tok);
|
|
if (AttrLoc.isValid() && AfterTok.is(tok::unknown))
|
|
AfterTok = tok;
|
|
} while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren));
|
|
if (tok.is(tok::r_paren))
|
|
break;
|
|
if (AttrLoc.isInvalid())
|
|
BeforeTok = tok;
|
|
lexer.LexFromRawLexer(tok);
|
|
}
|
|
|
|
if (toAttr.empty() && AttrLoc.isValid() && AfterTok.isNot(tok::unknown)) {
|
|
// We want to remove the attribute.
|
|
if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::r_paren)) {
|
|
Pass.TA.remove(SourceRange(BeforeTok.getLocation(),
|
|
AfterTok.getLocation()));
|
|
} else if (BeforeTok.is(tok::l_paren) && AfterTok.is(tok::comma)) {
|
|
Pass.TA.remove(SourceRange(AttrLoc, AfterTok.getLocation()));
|
|
} else {
|
|
Pass.TA.remove(SourceRange(BeforeTok.getLocation(), AttrLoc));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool MigrationContext::addPropertyAttribute(StringRef attr,
|
|
SourceLocation atLoc) {
|
|
if (atLoc.isMacroID())
|
|
return false;
|
|
|
|
SourceManager &SM = Pass.Ctx.getSourceManager();
|
|
|
|
// Break down the source location.
|
|
std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc);
|
|
|
|
// Try to load the file buffer.
|
|
bool invalidTemp = false;
|
|
StringRef file = SM.getBufferData(locInfo.first, &invalidTemp);
|
|
if (invalidTemp)
|
|
return false;
|
|
|
|
const char *tokenBegin = file.data() + locInfo.second;
|
|
|
|
// Lex from the start of the given location.
|
|
Lexer lexer(SM.getLocForStartOfFile(locInfo.first),
|
|
Pass.Ctx.getLangOpts(),
|
|
file.begin(), tokenBegin, file.end());
|
|
Token tok;
|
|
lexer.LexFromRawLexer(tok);
|
|
if (tok.isNot(tok::at)) return false;
|
|
lexer.LexFromRawLexer(tok);
|
|
if (tok.isNot(tok::raw_identifier)) return false;
|
|
if (StringRef(tok.getRawIdentifierData(), tok.getLength())
|
|
!= "property")
|
|
return false;
|
|
lexer.LexFromRawLexer(tok);
|
|
|
|
if (tok.isNot(tok::l_paren)) {
|
|
Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") ");
|
|
return true;
|
|
}
|
|
|
|
lexer.LexFromRawLexer(tok);
|
|
if (tok.is(tok::r_paren)) {
|
|
Pass.TA.insert(tok.getLocation(), attr);
|
|
return true;
|
|
}
|
|
|
|
if (tok.isNot(tok::raw_identifier)) return false;
|
|
|
|
Pass.TA.insert(tok.getLocation(), std::string(attr) + ", ");
|
|
return true;
|
|
}
|
|
|
|
void MigrationContext::traverse(TranslationUnitDecl *TU) {
|
|
for (traverser_iterator
|
|
I = traversers_begin(), E = traversers_end(); I != E; ++I)
|
|
(*I)->traverseTU(*this);
|
|
|
|
ASTTransform(*this).TraverseDecl(TU);
|
|
}
|
|
|
|
static void GCRewriteFinalize(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) {
|
|
for (ObjCImplementationDecl::instmeth_iterator
|
|
MI = I->instmeth_begin(),
|
|
ME = I->instmeth_end(); MI != ME; ++MI) {
|
|
ObjCMethodDecl *MD = *MI;
|
|
if (!MD->hasBody())
|
|
continue;
|
|
|
|
if (MD->isInstanceMethod() && MD->getSelector() == FinalizeSel) {
|
|
ObjCMethodDecl *FinalizeM = MD;
|
|
Transaction Trans(TA);
|
|
TA.insert(FinalizeM->getSourceRange().getBegin(),
|
|
"#if !__has_feature(objc_arc)\n");
|
|
CharSourceRange::getTokenRange(FinalizeM->getSourceRange());
|
|
const SourceManager &SM = pass.Ctx.getSourceManager();
|
|
const LangOptions &LangOpts = pass.Ctx.getLangOpts();
|
|
bool Invalid;
|
|
std::string str = "\n#endif\n";
|
|
str += Lexer::getSourceText(
|
|
CharSourceRange::getTokenRange(FinalizeM->getSourceRange()),
|
|
SM, LangOpts, &Invalid);
|
|
TA.insertAfterToken(FinalizeM->getSourceRange().getEnd(), str);
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// getAllTransformations.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static void traverseAST(MigrationPass &pass) {
|
|
MigrationContext MigrateCtx(pass);
|
|
|
|
if (pass.isGCMigration()) {
|
|
MigrateCtx.addTraverser(new GCCollectableCallsTraverser);
|
|
MigrateCtx.addTraverser(new GCAttrsTraverser());
|
|
}
|
|
MigrateCtx.addTraverser(new PropertyRewriteTraverser());
|
|
MigrateCtx.addTraverser(new BlockObjCVariableTraverser());
|
|
MigrateCtx.addTraverser(new ProtectedScopeTraverser());
|
|
|
|
MigrateCtx.traverse(pass.Ctx.getTranslationUnitDecl());
|
|
}
|
|
|
|
static void independentTransforms(MigrationPass &pass) {
|
|
rewriteAutoreleasePool(pass);
|
|
removeRetainReleaseDeallocFinalize(pass);
|
|
rewriteUnusedInitDelegate(pass);
|
|
removeZeroOutPropsInDeallocFinalize(pass);
|
|
makeAssignARCSafe(pass);
|
|
rewriteUnbridgedCasts(pass);
|
|
checkAPIUses(pass);
|
|
traverseAST(pass);
|
|
}
|
|
|
|
std::vector<TransformFn> arcmt::getAllTransformations(
|
|
LangOptions::GCMode OrigGCMode,
|
|
bool NoFinalizeRemoval) {
|
|
std::vector<TransformFn> transforms;
|
|
|
|
if (OrigGCMode == LangOptions::GCOnly && NoFinalizeRemoval)
|
|
transforms.push_back(GCRewriteFinalize);
|
|
transforms.push_back(independentTransforms);
|
|
// This depends on previous transformations removing various expressions.
|
|
transforms.push_back(removeEmptyStatementsAndDeallocFinalize);
|
|
|
|
return transforms;
|
|
}
|