diff options
author | Argyrios Kyrtzidis <akyrtzi@gmail.com> | 2011-07-27 05:28:18 +0000 |
---|---|---|
committer | Argyrios Kyrtzidis <akyrtzi@gmail.com> | 2011-07-27 05:28:18 +0000 |
commit | 18fd0c6915b45c4daafe18e3cd324c13306f913f (patch) | |
tree | fb1e7ed5e727808a7c7e10189aced11902c070a5 /lib/ARCMigrate/TransUnbridgedCasts.cpp | |
parent | 3ef1ad2d28ef5a9b6ac7ec0bd4b2360a4ae3ee8b (diff) |
[arcmt] More automatic transformations and safety improvements; rdar://9615812 :
- Replace calling -zone with 'nil'. -zone is obsolete in ARC.
- Allow removing retain/release on a static global var.
- Fix assertion hit when scanning for name references outside a NSAutoreleasePool scope.
- Automatically add bridged casts for results of objc method calls and when calling CFRetain, for example:
NSString *s;
CFStringRef ref = [s string]; -> CFStringRef ref = (__bridge CFStringRef)([s string]);
ref = s.string; -> ref = (__bridge CFStringRef)(s.string);
ref = [NSString new]; -> ref = (__bridge_retained CFStringRef)([NSString new]);
ref = [s newString]; -> ref = (__bridge_retained CFStringRef)([s newString]);
ref = [[NSString alloc] init]; -> ref = (__bridge_retained CFStringRef)([[NSString alloc] init]);
ref = [[s string] retain]; -> ref = (__bridge_retained CFStringRef)([s string]);
ref = CFRetain(s); -> ref = (__bridge_retained CFTypeRef)(s);
ref = [s retain]; -> ref = (__bridge_retained CFStringRef)(s);
- Emit migrator error when trying to cast to CF type the result of autorelease/release:
for
CFStringRef f3() {
return (CFStringRef)[[[NSString alloc] init] autorelease];
}
emits:
t.m:12:10: error: [rewriter] it is not safe to cast to 'CFStringRef' the result of 'autorelease' message; a __bridge cast may result in a pointer to a destroyed object and a __bridge_retained may leak the object
return (CFStringRef)[[[NSString alloc] init] autorelease];
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
t.m:12:3: note: [rewriter] remove the cast and change return type of function to 'NSString *' to have the object automatically autoreleased
return (CFStringRef)[[[NSString alloc] init] autorelease];
^
- Before changing attributes to weak/unsafe_unretained, check if the backing ivar
is set to a +1 object, in which case use 'strong' instead.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@136208 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/ARCMigrate/TransUnbridgedCasts.cpp')
-rw-r--r-- | lib/ARCMigrate/TransUnbridgedCasts.cpp | 94 |
1 files changed, 88 insertions, 6 deletions
diff --git a/lib/ARCMigrate/TransUnbridgedCasts.cpp b/lib/ARCMigrate/TransUnbridgedCasts.cpp index 74bad5e180..7e922f3a27 100644 --- a/lib/ARCMigrate/TransUnbridgedCasts.cpp +++ b/lib/ARCMigrate/TransUnbridgedCasts.cpp @@ -36,6 +36,7 @@ #include "Internals.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" #include "clang/Sema/SemaDiagnostic.h" +#include "clang/AST/ParentMap.h" #include "clang/Basic/SourceManager.h" using namespace clang; @@ -47,11 +48,18 @@ namespace { class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{ MigrationPass &Pass; IdentifierInfo *SelfII; + llvm::OwningPtr<ParentMap> StmtMap; + public: UnbridgedCastRewriter(MigrationPass &pass) : Pass(pass) { SelfII = &Pass.Ctx.Idents.get("self"); } + void transformBody(Stmt *body) { + StmtMap.reset(new ParentMap(body)); + TraverseStmt(body); + } + bool VisitCastExpr(CastExpr *E) { if (E->getCastKind() != CK_AnyPointerToObjCPointerCast && E->getCastKind() != CK_BitCast) @@ -138,13 +146,21 @@ private: } void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind) { + Transaction Trans(Pass.TA); + rewriteToBridgedCast(E, Kind, Trans); + } + + void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind, + Transaction &Trans) { 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())) + E->getLocStart())) { + Trans.abort(); return; + } StringRef bridge; switch(Kind) { @@ -156,7 +172,6 @@ private: bridge = "__bridge_retained "; break; } - Transaction Trans(TA); TA.clearDiagnostic(diag::err_arc_mismatched_cast, diag::err_arc_cast_requires_bridge, E->getLocStart()); @@ -180,16 +195,83 @@ private: } } + void rewriteCastForCFRetain(CastExpr *castE, CallExpr *callE) { + Transaction Trans(Pass.TA); + Pass.TA.replace(callE->getSourceRange(), callE->getArg(0)->getSourceRange()); + rewriteToBridgedCast(castE, OBC_BridgeRetained, Trans); + } + void transformObjCToNonObjCCast(CastExpr *E) { if (isSelf(E->getSubExpr())) return rewriteToBridgedCast(E, OBC_Bridge); + + CallExpr *callE; + if (isPassedToCFRetain(E, callE)) + return rewriteCastForCFRetain(E, callE); + + ObjCMethodFamily family = getFamilyOfMessage(E->getSubExpr()); + if (family == OMF_retain) + return rewriteToBridgedCast(E, OBC_BridgeRetained); + + if (family == OMF_autorelease || family == OMF_release) { + std::string err = "it is not safe to cast to '"; + err += E->getType().getAsString(Pass.Ctx.PrintingPolicy); + err += "' the result of '"; + err += family == OMF_autorelease ? "autorelease" : "release"; + err += "' message; a __bridge cast may result in a pointer to a " + "destroyed object and a __bridge_retained may leak the object"; + Pass.TA.reportError(err, E->getLocStart(), + E->getSubExpr()->getSourceRange()); + Stmt *parent = E; + do { + parent = StmtMap->getParentIgnoreParenImpCasts(parent); + } while (parent && isa<ExprWithCleanups>(parent)); + + if (ReturnStmt *retS = dyn_cast_or_null<ReturnStmt>(parent)) { + std::string note = "remove the cast and change return type of function " + "to '"; + note += E->getSubExpr()->getType().getAsString(Pass.Ctx.PrintingPolicy); + note += "' to have the object automatically autoreleased"; + Pass.TA.reportNote(note, retS->getLocStart()); + } + } + + if (ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getSubExpr())){ + if (implCE->getCastKind() == CK_ObjCConsumeObject) + return rewriteToBridgedCast(E, OBC_BridgeRetained); + if (implCE->getCastKind() == CK_ObjCReclaimReturnedObject) + return rewriteToBridgedCast(E, OBC_Bridge); + } + } + + static ObjCMethodFamily getFamilyOfMessage(Expr *E) { + E = E->IgnoreParenCasts(); + if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) + return ME->getMethodFamily(); + + return OMF_None; + } + + bool isPassedToCFRetain(Expr *E, CallExpr *&callE) const { + if ((callE = dyn_cast_or_null<CallExpr>( + StmtMap->getParentIgnoreParenImpCasts(E)))) + if (FunctionDecl * + FD = dyn_cast_or_null<FunctionDecl>(callE->getCalleeDecl())) + if (FD->getName() == "CFRetain" && FD->getNumParams() == 1 && + FD->getParent()->isTranslationUnit() && + FD->getLinkage() == ExternalLinkage) + return true; + + return false; } - bool isSelf(Expr *E) { + bool isSelf(Expr *E) const { E = E->IgnoreParenLValueCasts(); if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) - if (DRE->getDecl()->getIdentifier() == SelfII) - return true; + if (ImplicitParamDecl *IPD = dyn_cast<ImplicitParamDecl>(DRE->getDecl())) + if (IPD->getIdentifier() == SelfII) + return true; + return false; } }; @@ -197,6 +279,6 @@ private: } // end anonymous namespace void trans::rewriteUnbridgedCasts(MigrationPass &pass) { - UnbridgedCastRewriter trans(pass); + BodyTransform<UnbridgedCastRewriter> trans(pass); trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); } |