| //===--- Tranforms.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. |
| // |
| //===----------------------------------------------------------------------===// |
| // Transformations: |
| //===----------------------------------------------------------------------===// |
| // |
| // castNonObjCToObjC: |
| // |
| // A cast of non-objc pointer to an objc one is checked. If the non-objc pointer |
| // is from a file-level variable, objc_unretainedObject function is used to |
| // convert it. |
| // |
| // NSString *str = (NSString *)kUTTypePlainText; |
| // str = b ? kUTTypeRTF : kUTTypePlainText; |
| // ----> |
| // NSString *str = objc_unretainedObject(kUTTypePlainText); |
| // str = objc_unretainedObject(b ? kUTTypeRTF : kUTTypePlainText); |
| // |
| // For a C pointer to ObjC, objc_unretainedPointer is used. |
| // |
| // void *vp = str; // NSString* |
| // ----> |
| // void *vp = (void*)objc_unretainedPointer(str); |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // 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]; |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // 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. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // makeAssignARCSafe: |
| // |
| // Add '__strong' where appropriate. |
| // |
| // for (id x in collection) { |
| // x = 0; |
| // } |
| // ----> |
| // for (__strong id x in collection) { |
| // x = 0; |
| // } |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // removeRetainReleaseDealloc: |
| // |
| // Removes retain/release/autorelease/dealloc messages. |
| // |
| // return [[foo retain] autorelease]; |
| // ----> |
| // return foo; |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // removeEmptyStatements: |
| // |
| // Removes empty statements that are leftovers from previous transformations. |
| // e.g for |
| // |
| // [x retain]; |
| // |
| // removeRetainReleaseDealloc will leave an empty ";" that removeEmptyStatements |
| // will remove. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // changeIvarsOfAssignProperties: |
| // |
| // If a property is synthesized with 'assign' attribute and the user didn't |
| // set a lifetime attribute, change the property to 'weak' or add |
| // __unsafe_unretained if the ARC runtime is not available. |
| // |
| // @interface Foo : NSObject { |
| // NSObject *x; |
| // } |
| // @property (assign) id x; |
| // @end |
| // ----> |
| // @interface Foo : NSObject { |
| // NSObject *__weak x; |
| // } |
| // @property (weak) id x; |
| // @end |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // rewriteUnusedDelegateInit: |
| // |
| // Rewrites an unused result of calling a delegate initialization, to assigning |
| // the result to self. |
| // e.g |
| // [self init]; |
| // ----> |
| // self = [self init]; |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // 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]; }); |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // removeZeroOutIvarsInDealloc: |
| // |
| // Removes zero'ing out "strong" @synthesized properties in a -dealloc method. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "Internals.h" |
| #include "clang/Sema/SemaDiagnostic.h" |
| #include "clang/AST/RecursiveASTVisitor.h" |
| #include "clang/AST/StmtVisitor.h" |
| #include "clang/AST/ParentMap.h" |
| #include "clang/Analysis/DomainSpecific/CocoaConventions.h" |
| #include "clang/Lex/Lexer.h" |
| #include "clang/Basic/SourceManager.h" |
| #include "llvm/ADT/StringSwitch.h" |
| #include "llvm/ADT/DenseSet.h" |
| #include <map> |
| |
| using namespace clang; |
| using namespace arcmt; |
| using llvm::StringRef; |
| |
| //===----------------------------------------------------------------------===// |
| // Transformations. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| class RemovablesCollector : public RecursiveASTVisitor<RemovablesCollector> { |
| llvm::DenseSet<Expr *> &Removables; |
| |
| public: |
| RemovablesCollector(llvm::DenseSet<Expr *> &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; |
| |
| if (LabelStmt *Label = dyn_cast<LabelStmt>(S)) |
| return mark(Label->getSubStmt()); |
| if (ImplicitCastExpr *CE = dyn_cast<ImplicitCastExpr>(S)) |
| return mark(CE->getSubExpr()); |
| if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(S)) |
| return mark(EWC->getSubExpr()); |
| if (Expr *E = dyn_cast<Expr>(S)) |
| Removables.insert(E); |
| } |
| }; |
| |
| } // end anonymous namespace. |
| |
| static bool 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; |
| } |
| |
| static void removeDeallocMethod(MigrationPass &pass) { |
| ASTContext &Ctx = pass.Ctx; |
| TransformActions &TA = pass.TA; |
| DeclContext *DC = Ctx.getTranslationUnitDecl(); |
| ObjCMethodDecl *DeallocMethodDecl = 0; |
| IdentifierInfo *II = &Ctx.Idents.get("dealloc"); |
| |
| for (DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); |
| I != E; ++I) { |
| Decl *D = *I; |
| if (ObjCImplementationDecl *IMD = |
| dyn_cast<ObjCImplementationDecl>(D)) { |
| DeallocMethodDecl = 0; |
| for (ObjCImplementationDecl::instmeth_iterator I = |
| IMD->instmeth_begin(), E = IMD->instmeth_end(); |
| I != E; ++I) { |
| ObjCMethodDecl *OMD = *I; |
| if (OMD->isInstanceMethod() && |
| OMD->getSelector() == Ctx.Selectors.getSelector(0, &II)) { |
| DeallocMethodDecl = OMD; |
| break; |
| } |
| } |
| if (DeallocMethodDecl && |
| DeallocMethodDecl->getCompoundBody()->body_empty()) { |
| Transaction Trans(TA); |
| TA.remove(DeallocMethodDecl->getSourceRange()); |
| } |
| } |
| } |
| } |
| |
| namespace { |
| |
| class ReferenceClear : public RecursiveASTVisitor<ReferenceClear> { |
| llvm::DenseSet<Expr *> &Refs; |
| public: |
| ReferenceClear(llvm::DenseSet<Expr *> &refs) : Refs(refs) { } |
| bool VisitDeclRefExpr(DeclRefExpr *E) { Refs.erase(E); return true; } |
| bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { Refs.erase(E); return true; } |
| void clearRefsIn(Stmt *S) { TraverseStmt(S); } |
| template <typename iterator> |
| void clearRefsIn(iterator begin, iterator end) { |
| for (; begin != end; ++begin) |
| TraverseStmt(*begin); |
| } |
| }; |
| |
| class ReferenceCollector : public RecursiveASTVisitor<ReferenceCollector> { |
| ValueDecl *Dcl; |
| llvm::DenseSet<Expr *> &Refs; |
| |
| public: |
| ReferenceCollector(llvm::DenseSet<Expr *> &refs) |
| : Dcl(0), Refs(refs) { } |
| |
| void lookFor(ValueDecl *D, Stmt *S) { |
| Dcl = D; |
| TraverseStmt(S); |
| } |
| |
| bool VisitDeclRefExpr(DeclRefExpr *E) { |
| if (E->getDecl() == Dcl) |
| Refs.insert(E); |
| return true; |
| } |
| |
| bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { |
| if (E->getDecl() == Dcl) |
| Refs.insert(E); |
| return true; |
| } |
| }; |
| |
| 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; |
| } |
| }; |
| |
| template <typename BODY_TRANS> |
| class BodyTransform : public RecursiveASTVisitor<BodyTransform<BODY_TRANS> > { |
| MigrationPass &Pass; |
| |
| public: |
| BodyTransform(MigrationPass &pass) : Pass(pass) { } |
| |
| void handleBody(Decl *D) { |
| Stmt *body = D->getBody(); |
| if (body) { |
| BODY_TRANS(D, Pass).transformBody(body); |
| } |
| } |
| |
| bool TraverseBlockDecl(BlockDecl *D) { |
| handleBody(D); |
| return true; |
| } |
| bool TraverseObjCMethodDecl(ObjCMethodDecl *D) { |
| if (D->isThisDeclarationADefinition()) |
| handleBody(D); |
| return true; |
| } |
| bool TraverseFunctionDecl(FunctionDecl *D) { |
| if (D->isThisDeclarationADefinition()) |
| handleBody(D); |
| return true; |
| } |
| }; |
| |
| } // anonymous namespace |
| |
| //===----------------------------------------------------------------------===// |
| // makeAssignARCSafe |
| //===----------------------------------------------------------------------===// |
| |
| 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 |
| |
| static void makeAssignARCSafe(MigrationPass &pass) { |
| ARCAssignChecker assignCheck(pass); |
| assignCheck.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // castNonObjCToObjC |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| class NonObjCToObjCCaster : public RecursiveASTVisitor<NonObjCToObjCCaster> { |
| MigrationPass &Pass; |
| IdentifierInfo *SelfII; |
| public: |
| NonObjCToObjCCaster(MigrationPass &pass) : Pass(pass) { |
| SelfII = &Pass.Ctx.Idents.get("self"); |
| } |
| |
| bool VisitCastExpr(CastExpr *E) { |
| if (E->getCastKind() != CK_AnyPointerToObjCPointerCast |
| && E->getCastKind() != CK_BitCast) |
| return true; |
| |
| QualType castType = E->getType(); |
| Expr *castExpr = E->getSubExpr(); |
| QualType castExprType = castExpr->getType(); |
| |
| if (castType->isObjCObjectPointerType() && |
| castExprType->isObjCObjectPointerType()) |
| return true; |
| if (!castType->isObjCObjectPointerType() && |
| !castExprType->isObjCObjectPointerType()) |
| return true; |
| |
| bool exprRetainable = castExprType->isObjCIndirectLifetimeType(); |
| bool castRetainable = castType->isObjCIndirectLifetimeType(); |
| if (exprRetainable == castRetainable) return true; |
| |
| if (castExpr->isNullPointerConstant(Pass.Ctx, |
| Expr::NPC_ValueDependentIsNull)) |
| return true; |
| |
| SourceLocation loc = castExpr->getExprLoc(); |
| if (loc.isValid() && Pass.Ctx.getSourceManager().isInSystemHeader(loc)) |
| return true; |
| |
| if (castType->isObjCObjectPointerType()) |
| transformNonObjCToObjCCast(E); |
| else |
| transformObjCToNonObjCCast(E); |
| |
| return true; |
| } |
| |
| private: |
| void transformNonObjCToObjCCast(CastExpr *E) { |
| if (!E) return; |
| |
| // Global vars are assumed that are cast as unretained. |
| if (isGlobalVar(E)) |
| if (E->getSubExpr()->getType()->isPointerType()) { |
| castToObjCObject(E, /*retained=*/false); |
| return; |
| } |
| |
| // If the cast is directly over the result of a Core Foundation function |
| // try to figure out whether it should be cast as retained or unretained. |
| Expr *inner = E->IgnoreParenCasts(); |
| if (CallExpr *callE = dyn_cast<CallExpr>(inner)) { |
| if (FunctionDecl *FD = callE->getDirectCallee()) { |
| if (FD->getAttr<CFReturnsRetainedAttr>()) { |
| castToObjCObject(E, /*retained=*/true); |
| return; |
| } |
| if (FD->getAttr<CFReturnsNotRetainedAttr>()) { |
| castToObjCObject(E, /*retained=*/false); |
| return; |
| } |
| if (FD->isGlobal() && |
| FD->getIdentifier() && |
| ento::cocoa::isRefType(E->getSubExpr()->getType(), "CF", |
| FD->getIdentifier()->getName())) { |
| StringRef fname = FD->getIdentifier()->getName(); |
| if (fname.endswith("Retain") || |
| fname.find("Create") != StringRef::npos || |
| fname.find("Copy") != StringRef::npos) { |
| castToObjCObject(E, /*retained=*/true); |
| return; |
| } |
| |
| if (fname.find("Get") != StringRef::npos) { |
| castToObjCObject(E, /*retained=*/false); |
| return; |
| } |
| } |
| } |
| } |
| } |
| |
| void castToObjCObject(CastExpr *E, bool retained) { |
| rewriteToBridgedCast(E, retained ? OBC_BridgeTransfer : OBC_Bridge); |
| } |
| |
| void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind) { |
| TransformActions &TA = Pass.TA; |
| |
| // We will remove the compiler diagnostic. |
| if (!TA.hasDiagnostic(diag::err_arc_mismatched_cast, |
| diag::err_arc_cast_requires_bridge, |
| E->getLocStart())) |
| return; |
| |
| StringRef bridge; |
| switch(Kind) { |
| case OBC_Bridge: |
| bridge = "__bridge "; break; |
| case OBC_BridgeTransfer: |
| bridge = "__bridge_transfer "; break; |
| case OBC_BridgeRetained: |
| bridge = "__bridge_retained "; break; |
| } |
| |
| Transaction Trans(TA); |
| TA.clearDiagnostic(diag::err_arc_mismatched_cast, |
| diag::err_arc_cast_requires_bridge, |
| E->getLocStart()); |
| if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(E)) { |
| TA.insertAfterToken(CCE->getLParenLoc(), bridge); |
| } else { |
| SourceLocation insertLoc = E->getSubExpr()->getLocStart(); |
| llvm::SmallString<128> newCast; |
| newCast += '('; |
| newCast += bridge; |
| newCast += E->getType().getAsString(Pass.Ctx.PrintingPolicy); |
| newCast += ')'; |
| |
| if (isa<ParenExpr>(E->getSubExpr())) { |
| TA.insert(insertLoc, newCast.str()); |
| } else { |
| newCast += '('; |
| TA.insert(insertLoc, newCast.str()); |
| TA.insertAfterToken(E->getLocEnd(), ")"); |
| } |
| } |
| } |
| |
| void transformObjCToNonObjCCast(CastExpr *E) { |
| if (isSelf(E->getSubExpr())) |
| return rewriteToBridgedCast(E, OBC_Bridge); |
| } |
| |
| bool isSelf(Expr *E) { |
| E = E->IgnoreParenLValueCasts(); |
| if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) |
| if (DRE->getDecl()->getIdentifier() == SelfII) |
| return true; |
| return false; |
| } |
| |
| static bool isGlobalVar(Expr *E) { |
| E = E->IgnoreParenCasts(); |
| if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) |
| return DRE->getDecl()->getDeclContext()->isFileContext(); |
| if (ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(E)) |
| return isGlobalVar(condOp->getTrueExpr()) && |
| isGlobalVar(condOp->getFalseExpr()); |
| |
| return false; |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| static void castNonObjCToObjC(MigrationPass &pass) { |
| NonObjCToObjCCaster trans(pass); |
| trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // rewriteAllocCopyWithZone |
| //===----------------------------------------------------------------------===// |
| |
| 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) { |
| llvm::DenseSet<Expr *> refs; |
| |
| ReferenceCollector refColl(refs); |
| refColl.lookFor(D, Body); |
| |
| ReferenceClear refClear(refs); |
| refClear.clearRefsIn(removals.begin(), removals.end()); |
| |
| 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 |
| |
| static void rewriteAllocCopyWithZone(MigrationPass &pass) { |
| BodyTransform<AllocCopyWithZoneRewriter> trans(pass); |
| trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // rewriteAutoreleasePool |
| //===----------------------------------------------------------------------===// |
| |
| /// \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. |
| static SourceLocation findLocationAfterSemi(ASTContext &Ctx, |
| SourceLocation loc) { |
| SourceManager &SM = Ctx.getSourceManager(); |
| if (loc.isMacroID()) { |
| if (!SM.isAtEndOfMacroInstantiation(loc)) |
| return SourceLocation(); |
| loc = SM.getInstantiationRange(loc).second; |
| } |
| loc = Lexer::getLocForEndOfToken(loc, /*Offset=*/0, SM, Ctx.getLangOptions()); |
| |
| // Break down the source location. |
| std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc); |
| |
| // Try to load the file buffer. |
| bool invalidTemp = false; |
| llvm::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.getLangOptions(), |
| file.begin(), tokenBegin, file.end()); |
| Token tok; |
| lexer.LexFromRawLexer(tok); |
| if (tok.isNot(tok::semi)) |
| return SourceLocation(); |
| |
| return tok.getLocation().getFileLocWithOffset(1); |
| } |
| |
| 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. |
| |
| ReferenceClear refClear(info.Refs); |
| refClear.clearRefsIn(info.Dcl); |
| for (llvm::SmallVectorImpl<PoolScope>::iterator |
| scpI = info.Scopes.begin(), |
| scpE = info.Scopes.end(); scpI != scpE; ++scpI) { |
| PoolScope &scope = *scpI; |
| refClear.clearRefsIn(*scope.Begin); |
| refClear.clearRefsIn(*scope.End); |
| refClear.clearRefsIn(scope.Releases.begin(), scope.Releases.end()); |
| } |
| |
| // 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(Pass.Ctx, |
| (*retI)->getLocEnd()); |
| 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; |
| ReferenceCollector refColl(info.Refs); |
| refColl.lookFor(VD, S); |
| // 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(Pass.Ctx, retS->getLocEnd()).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; |
| llvm::DenseSet<Expr *> Refs; |
| llvm::SmallVector<PoolScope, 2> Scopes; |
| |
| PoolVarInfo() : Dcl(0) { } |
| }; |
| |
| std::map<VarDecl *, PoolVarInfo> PoolVars; |
| }; |
| |
| } // anonymous namespace |
| |
| static void rewriteAutoreleasePool(MigrationPass &pass) { |
| BodyTransform<AutoreleasePoolRewriter> trans(pass); |
| trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // removeRetainReleaseDealloc |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| class RetainReleaseDeallocRemover : |
| public RecursiveASTVisitor<RetainReleaseDeallocRemover> { |
| Decl *Dcl; |
| Stmt *Body; |
| MigrationPass &Pass; |
| |
| llvm::DenseSet<Expr *> Removables; |
| llvm::OwningPtr<ParentMap> StmtMap; |
| |
| public: |
| RetainReleaseDeallocRemover(Decl *D, MigrationPass &pass) |
| : Dcl(D), Body(0), Pass(pass) { } |
| |
| void transformBody(Stmt *body) { |
| Body = body; |
| RemovablesCollector(Removables).TraverseStmt(body); |
| StmtMap.reset(new ParentMap(body)); |
| TraverseStmt(body); |
| } |
| |
| bool VisitObjCMessageExpr(ObjCMessageExpr *E) { |
| switch (E->getMethodFamily()) { |
| default: |
| return true; |
| case OMF_retain: |
| case OMF_release: |
| case OMF_autorelease: |
| if (E->getReceiverKind() == ObjCMessageExpr::Instance) |
| if (Expr *rec = E->getInstanceReceiver()) { |
| rec = rec->IgnoreParenImpCasts(); |
| if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone){ |
| std::string err = "It is not safe to remove '"; |
| err += E->getSelector().getAsString() + "' message on " |
| "an __unsafe_unretained type"; |
| Pass.TA.reportError(err, rec->getLocStart()); |
| return true; |
| } |
| } |
| case OMF_dealloc: |
| break; |
| } |
| |
| switch (E->getReceiverKind()) { |
| default: |
| return true; |
| case ObjCMessageExpr::SuperInstance: { |
| Transaction Trans(Pass.TA); |
| Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message, |
| diag::err_unavailable, |
| diag::err_unavailable_message, |
| E->getSuperLoc()); |
| if (tryRemoving(E)) |
| return true; |
| Pass.TA.replace(E->getSourceRange(), "self"); |
| return true; |
| } |
| case ObjCMessageExpr::Instance: |
| break; |
| } |
| |
| Expr *rec = E->getInstanceReceiver(); |
| if (!rec) return true; |
| |
| Transaction Trans(Pass.TA); |
| Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message, |
| diag::err_unavailable, |
| diag::err_unavailable_message, |
| rec->getExprLoc()); |
| if (!HasSideEffects(E, Pass.Ctx)) { |
| if (tryRemoving(E)) |
| return true; |
| } |
| Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); |
| |
| return true; |
| } |
| |
| private: |
| bool isRemovable(Expr *E) const { |
| return Removables.count(E); |
| } |
| |
| bool tryRemoving(Expr *E) const { |
| if (isRemovable(E)) { |
| Pass.TA.removeStmt(E); |
| return true; |
| } |
| |
| if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(StmtMap->getParent(E))) |
| return tryRemoving(parenE); |
| |
| if (BinaryOperator * |
| bopE = dyn_cast_or_null<BinaryOperator>(StmtMap->getParent(E))) { |
| if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E && |
| isRemovable(bopE)) { |
| Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange()); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| }; |
| |
| } // anonymous namespace |
| |
| static void removeRetainReleaseDealloc(MigrationPass &pass) { |
| BodyTransform<RetainReleaseDeallocRemover> trans(pass); |
| trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // removeEmptyStatements |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| class EmptyStatementsRemover : |
| public RecursiveASTVisitor<EmptyStatementsRemover> { |
| MigrationPass &Pass; |
| llvm::DenseSet<unsigned> MacroLocs; |
| |
| public: |
| EmptyStatementsRemover(MigrationPass &pass) : Pass(pass) { |
| for (unsigned i = 0, e = Pass.ARCMTMacroLocs.size(); i != e; ++i) |
| MacroLocs.insert(Pass.ARCMTMacroLocs[i].getRawEncoding()); |
| } |
| |
| 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 (CompoundStmt::body_iterator |
| I = S->body_begin(), E = S->body_end(); I != E; ++I) |
| check(*I); |
| return true; |
| } |
| |
| bool isMacroLoc(SourceLocation loc) { |
| if (loc.isInvalid()) return false; |
| return MacroLocs.count(loc.getRawEncoding()); |
| } |
| |
| ASTContext &getContext() { return Pass.Ctx; } |
| |
| private: |
| /// \brief Returns true if the statement became empty due to previous |
| /// transformations. |
| class EmptyChecker : public StmtVisitor<EmptyChecker, bool> { |
| EmptyStatementsRemover &Trans; |
| |
| public: |
| EmptyChecker(EmptyStatementsRemover &trans) : Trans(trans) { } |
| |
| bool VisitNullStmt(NullStmt *S) { |
| return Trans.isMacroLoc(S->getLeadingEmptyMacroLoc()); |
| } |
| bool VisitCompoundStmt(CompoundStmt *S) { |
| if (S->body_empty()) |
| return false; // was already empty, not because of transformations. |
| for (CompoundStmt::body_iterator |
| I = S->body_begin(), E = S->body_end(); I != E; ++I) |
| 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, Trans.getContext())) |
| 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, Trans.getContext())) |
| 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, Trans.getContext())) |
| 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, Trans.getContext())) |
| return false; |
| if (!S->getBody()) |
| return false; |
| return Visit(S->getBody()); |
| } |
| bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { |
| if (!S->getSubStmt()) |
| return false; |
| return Visit(S->getSubStmt()); |
| } |
| }; |
| |
| void check(Stmt *S) { |
| if (!S) return; |
| if (EmptyChecker(*this).Visit(S)) { |
| Transaction Trans(Pass.TA); |
| Pass.TA.removeStmt(S); |
| } |
| } |
| }; |
| |
| } // anonymous namespace |
| |
| static void removeEmptyStatements(MigrationPass &pass) { |
| EmptyStatementsRemover(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
| |
| for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) { |
| Transaction Trans(pass.TA); |
| pass.TA.remove(pass.ARCMTMacroLocs[i]); |
| } |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // changeIvarsOfAssignProperties. |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| class AssignPropertiesTrans { |
| MigrationPass &Pass; |
| struct PropData { |
| ObjCPropertyDecl *PropD; |
| ObjCIvarDecl *IvarD; |
| bool ShouldChangeToWeak; |
| SourceLocation ArcPropAssignErrorLoc; |
| }; |
| |
| typedef llvm::SmallVector<PropData, 2> PropsTy; |
| typedef llvm::DenseMap<unsigned, PropsTy> PropsMapTy; |
| PropsMapTy PropsMap; |
| |
| public: |
| AssignPropertiesTrans(MigrationPass &pass) : Pass(pass) { } |
| |
| void doTransform(ObjCImplementationDecl *D) { |
| SourceManager &SM = Pass.Ctx.getSourceManager(); |
| |
| ObjCInterfaceDecl *IFace = D->getClassInterface(); |
| for (ObjCInterfaceDecl::prop_iterator |
| I = IFace->prop_begin(), E = IFace->prop_end(); I != E; ++I) { |
| ObjCPropertyDecl *propD = *I; |
| unsigned loc = SM.getInstantiationLoc(propD->getAtLoc()).getRawEncoding(); |
| PropsTy &props = PropsMap[loc]; |
| props.push_back(PropData()); |
| props.back().PropD = propD; |
| props.back().IvarD = 0; |
| props.back().ShouldChangeToWeak = false; |
| } |
| |
| typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl> |
| prop_impl_iterator; |
| for (prop_impl_iterator |
| I = prop_impl_iterator(D->decls_begin()), |
| E = prop_impl_iterator(D->decls_end()); I != E; ++I) { |
| VisitObjCPropertyImplDecl(*I); |
| } |
| |
| for (PropsMapTy::iterator |
| I = PropsMap.begin(), E = PropsMap.end(); I != E; ++I) { |
| SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); |
| PropsTy &props = I->second; |
| if (shouldApplyWeakToAllProp(props)) { |
| if (changeAssignToWeak(atLoc)) { |
| // Couldn't add the 'weak' property attribute, |
| // try adding __unsafe_unretained. |
| applyUnsafeUnretained(props); |
| } else { |
| for (PropsTy::iterator |
| PI = props.begin(), PE = props.end(); PI != PE; ++PI) { |
| applyWeak(*PI); |
| } |
| } |
| } else { |
| // We should not add 'weak' attribute since not all properties need it. |
| // So just add __unsafe_unretained to the ivars. |
| applyUnsafeUnretained(props); |
| } |
| } |
| } |
| |
| bool shouldApplyWeakToAllProp(PropsTy &props) { |
| for (PropsTy::iterator |
| PI = props.begin(), PE = props.end(); PI != PE; ++PI) { |
| if (!PI->ShouldChangeToWeak) |
| return false; |
| } |
| return true; |
| } |
| |
| void applyWeak(PropData &prop) { |
| assert(!Pass.Ctx.getLangOptions().ObjCNoAutoRefCountRuntime); |
| |
| Transaction Trans(Pass.TA); |
| Pass.TA.insert(prop.IvarD->getLocation(), "__weak "); |
| Pass.TA.clearDiagnostic(diag::err_arc_assign_property_lifetime, |
| prop.ArcPropAssignErrorLoc); |
| } |
| |
| void applyUnsafeUnretained(PropsTy &props) { |
| for (PropsTy::iterator |
| PI = props.begin(), PE = props.end(); PI != PE; ++PI) { |
| if (PI->ShouldChangeToWeak) { |
| Transaction Trans(Pass.TA); |
| Pass.TA.insert(PI->IvarD->getLocation(), "__unsafe_unretained "); |
| Pass.TA.clearDiagnostic(diag::err_arc_assign_property_lifetime, |
| PI->ArcPropAssignErrorLoc); |
| } |
| } |
| } |
| |
| bool VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { |
| SourceManager &SM = Pass.Ctx.getSourceManager(); |
| |
| if (D->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) |
| return true; |
| ObjCPropertyDecl *propD = D->getPropertyDecl(); |
| if (!propD || propD->isInvalidDecl()) |
| return true; |
| ObjCIvarDecl *ivarD = D->getPropertyIvarDecl(); |
| if (!ivarD || ivarD->isInvalidDecl()) |
| return true; |
| if (!(propD->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_assign)) |
| return true; |
| if (isa<AttributedType>(ivarD->getType().getTypePtr())) |
| return true; |
| if (ivarD->getType().getLocalQualifiers().getObjCLifetime() |
| != Qualifiers::OCL_Strong) |
| return true; |
| if (!Pass.TA.hasDiagnostic( |
| diag::err_arc_assign_property_lifetime, D->getLocation())) |
| return true; |
| |
| // There is a "error: existing ivar for assign property must be |
| // __unsafe_unretained"; fix it. |
| |
| if (Pass.Ctx.getLangOptions().ObjCNoAutoRefCountRuntime) { |
| // We will just add __unsafe_unretained to the ivar. |
| Transaction Trans(Pass.TA); |
| Pass.TA.insert(ivarD->getLocation(), "__unsafe_unretained "); |
| Pass.TA.clearDiagnostic( |
| diag::err_arc_assign_property_lifetime, D->getLocation()); |
| } else { |
| // Mark that we want the ivar to become weak. |
| unsigned loc = SM.getInstantiationLoc(propD->getAtLoc()).getRawEncoding(); |
| PropsTy &props = PropsMap[loc]; |
| for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { |
| if (I->PropD == propD) { |
| I->IvarD = ivarD; |
| I->ShouldChangeToWeak = true; |
| I->ArcPropAssignErrorLoc = D->getLocation(); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| private: |
| bool changeAssignToWeak(SourceLocation atLoc) { |
| 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; |
| llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); |
| if (invalidTemp) |
| return true; |
| |
| const char *tokenBegin = file.data() + locInfo.second; |
| |
| // Lex from the start of the given location. |
| Lexer lexer(SM.getLocForStartOfFile(locInfo.first), |
| Pass.Ctx.getLangOptions(), |
| file.begin(), tokenBegin, file.end()); |
| Token tok; |
| lexer.LexFromRawLexer(tok); |
| if (tok.isNot(tok::at)) return true; |
| lexer.LexFromRawLexer(tok); |
| if (tok.isNot(tok::raw_identifier)) return true; |
| if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength()) |
| != "property") |
| return true; |
| lexer.LexFromRawLexer(tok); |
| if (tok.isNot(tok::l_paren)) return true; |
| |
| SourceLocation LParen = tok.getLocation(); |
| SourceLocation assignLoc; |
| bool isEmpty = false; |
| |
| lexer.LexFromRawLexer(tok); |
| if (tok.is(tok::r_paren)) { |
| isEmpty = true; |
| } else { |
| while (1) { |
| if (tok.isNot(tok::raw_identifier)) return true; |
| llvm::StringRef ident(tok.getRawIdentifierData(), tok.getLength()); |
| if (ident == "assign") |
| assignLoc = tok.getLocation(); |
| |
| do { |
| lexer.LexFromRawLexer(tok); |
| } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren)); |
| if (tok.is(tok::r_paren)) |
| break; |
| lexer.LexFromRawLexer(tok); |
| } |
| } |
| |
| Transaction Trans(Pass.TA); |
| if (assignLoc.isValid()) |
| Pass.TA.replaceText(assignLoc, "assign", "weak"); |
| else |
| Pass.TA.insertAfterToken(LParen, isEmpty ? "weak" : "weak, "); |
| return false; |
| } |
| }; |
| |
| class PropertiesChecker : public RecursiveASTVisitor<PropertiesChecker> { |
| MigrationPass &Pass; |
| |
| public: |
| PropertiesChecker(MigrationPass &pass) : Pass(pass) { } |
| |
| bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) { |
| AssignPropertiesTrans(Pass).doTransform(D); |
| return true; |
| } |
| }; |
| |
| } // anonymous namespace |
| |
| static void changeIvarsOfAssignProperties(MigrationPass &pass) { |
| PropertiesChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // rewriteUnusedDelegateInit |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| class UnusedInitRewriter : public RecursiveASTVisitor<UnusedInitRewriter> { |
| Decl *Dcl; |
| Stmt *Body; |
| MigrationPass &Pass; |
| |
| llvm::DenseSet<Expr *> Removables; |
| |
| public: |
| UnusedInitRewriter(Decl *D, MigrationPass &pass) |
| : Dcl(D), Body(0), Pass(pass) { } |
| |
| void transformBody(Stmt *body) { |
| Body = body; |
| RemovablesCollector(Removables).TraverseStmt(body); |
| TraverseStmt(body); |
| } |
| |
| bool VisitObjCMessageExpr(ObjCMessageExpr *ME) { |
| if (ME->isDelegateInitCall() && |
| isRemovable(ME) && |
| Pass.TA.hasDiagnostic(diag::err_arc_unused_init_message, |
| ME->getExprLoc())) { |
| Transaction Trans(Pass.TA); |
| Pass.TA.clearDiagnostic(diag::err_arc_unused_init_message, |
| ME->getExprLoc()); |
| Pass.TA.insert(ME->getExprLoc(), "self = "); |
| } |
| return true; |
| } |
| |
| private: |
| bool isRemovable(Expr *E) const { |
| return Removables.count(E); |
| } |
| }; |
| |
| } // anonymous namespace |
| |
| static void rewriteUnusedDelegateInit(MigrationPass &pass) { |
| BodyTransform<UnusedInitRewriter> trans(pass); |
| trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // rewriteBlockObjCVariable |
| //===----------------------------------------------------------------------===// |
| |
| 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 |
| |
| static void rewriteBlockObjCVariable(MigrationPass &pass) { |
| BlockObjCVarRewriter trans(pass); |
| trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // removeZeroOutIvarsInDealloc |
| //===----------------------------------------------------------------------===// |
| |
| namespace { |
| |
| class ZeroOutInDeallocRemover : |
| public RecursiveASTVisitor<ZeroOutInDeallocRemover> { |
| typedef RecursiveASTVisitor<ZeroOutInDeallocRemover> base; |
| |
| MigrationPass &Pass; |
| |
| llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*> SynthesizedProperties; |
| ImplicitParamDecl *SelfD; |
| llvm::DenseSet<Expr *> Removables; |
| |
| public: |
| ZeroOutInDeallocRemover(MigrationPass &pass) : Pass(pass), SelfD(0) { } |
| |
| bool VisitObjCMessageExpr(ObjCMessageExpr *ME) { |
| ASTContext &Ctx = Pass.Ctx; |
| TransformActions &TA = Pass.TA; |
| |
| if (ME->getReceiverKind() != ObjCMessageExpr::Instance) |
| return true; |
| Expr *receiver = ME->getInstanceReceiver(); |
| if (!receiver) |
| return true; |
| |
| DeclRefExpr *refE = dyn_cast<DeclRefExpr>(receiver->IgnoreParenCasts()); |
| if (!refE || refE->getDecl() != SelfD) |
| return true; |
| |
| bool BackedBySynthesizeSetter = false; |
| for (llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*>::iterator |
| P = SynthesizedProperties.begin(), |
| E = SynthesizedProperties.end(); P != E; ++P) { |
| ObjCPropertyDecl *PropDecl = P->first; |
| if (PropDecl->getSetterName() == ME->getSelector()) { |
| BackedBySynthesizeSetter = true; |
| break; |
| } |
| } |
| if (!BackedBySynthesizeSetter) |
| return true; |
| |
| // Remove the setter message if RHS is null |
| Transaction Trans(TA); |
| Expr *RHS = ME->getArg(0); |
| bool RHSIsNull = |
| RHS->isNullPointerConstant(Ctx, |
| Expr::NPC_ValueDependentIsNull); |
| if (RHSIsNull && isRemovable(ME)) |
| TA.removeStmt(ME); |
| |
| return true; |
| } |
| |
| bool VisitBinaryOperator(BinaryOperator *BOE) { |
| if (isZeroingPropIvar(BOE) && isRemovable(BOE)) { |
| Transaction Trans(Pass.TA); |
| Pass.TA.removeStmt(BOE); |
| } |
| |
| return true; |
| } |
| |
| bool TraverseObjCMethodDecl(ObjCMethodDecl *D) { |
| if (D->getMethodFamily() != OMF_dealloc) |
| return true; |
| if (!D->hasBody()) |
| return true; |
| |
| ObjCImplDecl *IMD = dyn_cast<ObjCImplDecl>(D->getDeclContext()); |
| if (!IMD) |
| return true; |
| |
| SelfD = D->getSelfDecl(); |
| RemovablesCollector(Removables).TraverseStmt(D->getBody()); |
| |
| // For a 'dealloc' method use, find all property implementations in |
| // this class implementation. |
| for (ObjCImplDecl::propimpl_iterator |
| I = IMD->propimpl_begin(), EI = IMD->propimpl_end(); I != EI; ++I) { |
| ObjCPropertyImplDecl *PID = *I; |
| if (PID->getPropertyImplementation() == |
| ObjCPropertyImplDecl::Synthesize) { |
| ObjCPropertyDecl *PD = PID->getPropertyDecl(); |
| ObjCMethodDecl *setterM = PD->getSetterMethodDecl(); |
| if (!(setterM && setterM->isDefined())) { |
| ObjCPropertyDecl::PropertyAttributeKind AttrKind = |
| PD->getPropertyAttributes(); |
| if (AttrKind & |
| (ObjCPropertyDecl::OBJC_PR_retain | |
| ObjCPropertyDecl::OBJC_PR_copy | |
| ObjCPropertyDecl::OBJC_PR_strong)) |
| SynthesizedProperties[PD] = PID; |
| } |
| } |
| } |
| |
| // Now, remove all zeroing of ivars etc. |
| base::TraverseObjCMethodDecl(D); |
| |
| // clear out for next method. |
| SynthesizedProperties.clear(); |
| SelfD = 0; |
| Removables.clear(); |
| return true; |
| } |
| |
| bool TraverseFunctionDecl(FunctionDecl *D) { return true; } |
| bool TraverseBlockDecl(BlockDecl *block) { return true; } |
| bool TraverseBlockExpr(BlockExpr *block) { return true; } |
| |
| private: |
| bool isRemovable(Expr *E) const { |
| return Removables.count(E); |
| } |
| |
| bool isZeroingPropIvar(Expr *E) { |
| BinaryOperator *BOE = dyn_cast_or_null<BinaryOperator>(E); |
| if (!BOE) return false; |
| |
| if (BOE->getOpcode() == BO_Comma) |
| return isZeroingPropIvar(BOE->getLHS()) && |
| isZeroingPropIvar(BOE->getRHS()); |
| |
| if (BOE->getOpcode() != BO_Assign) |
| return false; |
| |
| ASTContext &Ctx = Pass.Ctx; |
| |
| Expr *LHS = BOE->getLHS(); |
| if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(LHS)) { |
| ObjCIvarDecl *IVDecl = IV->getDecl(); |
| if (!IVDecl->getType()->isObjCObjectPointerType()) |
| return false; |
| bool IvarBacksPropertySynthesis = false; |
| for (llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*>::iterator |
| P = SynthesizedProperties.begin(), |
| E = SynthesizedProperties.end(); P != E; ++P) { |
| ObjCPropertyImplDecl *PropImpDecl = P->second; |
| if (PropImpDecl && PropImpDecl->getPropertyIvarDecl() == IVDecl) { |
| IvarBacksPropertySynthesis = true; |
| break; |
| } |
| } |
| if (!IvarBacksPropertySynthesis) |
| return false; |
| } |
| else if (ObjCPropertyRefExpr *PropRefExp = dyn_cast<ObjCPropertyRefExpr>(LHS)) { |
| // TODO: Using implicit property decl. |
| if (PropRefExp->isImplicitProperty()) |
| return false; |
| if (ObjCPropertyDecl *PDecl = PropRefExp->getExplicitProperty()) { |
| if (!SynthesizedProperties.count(PDecl)) |
| return false; |
| } |
| } |
| else |
| return false; |
| |
| Expr *RHS = BOE->getRHS(); |
| bool RHSIsNull = RHS->isNullPointerConstant(Ctx, |
| Expr::NPC_ValueDependentIsNull); |
| if (RHSIsNull) |
| return true; |
| |
| return isZeroingPropIvar(RHS); |
| } |
| }; |
| |
| } // anonymous namespace |
| |
| static void removeZeroOutIvarsInDealloc(MigrationPass &pass) { |
| ZeroOutInDeallocRemover trans(pass); |
| trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // getAllTransformations. |
| //===----------------------------------------------------------------------===// |
| |
| static void independentTransforms(MigrationPass &pass) { |
| rewriteAutoreleasePool(pass); |
| changeIvarsOfAssignProperties(pass); |
| removeRetainReleaseDealloc(pass); |
| rewriteUnusedDelegateInit(pass); |
| removeZeroOutIvarsInDealloc(pass); |
| makeAssignARCSafe(pass); |
| castNonObjCToObjC(pass); |
| rewriteBlockObjCVariable(pass); |
| rewriteAllocCopyWithZone(pass); |
| } |
| |
| std::vector<TransformFn> arcmt::getAllTransformations() { |
| std::vector<TransformFn> transforms; |
| |
| // This must come first since rewriteAutoreleasePool depends on -release |
| // calls being present to determine the @autorelease ending scope. |
| transforms.push_back(independentTransforms); |
| |
| transforms.push_back(removeEmptyStatements); |
| transforms.push_back(removeDeallocMethod); |
| |
| return transforms; |
| } |