From 18fd0c6915b45c4daafe18e3cd324c13306f913f Mon Sep 17 00:00:00 2001 From: Argyrios Kyrtzidis Date: Wed, 27 Jul 2011 05:28:18 +0000 Subject: [PATCH] [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 --- include/clang/AST/ParentMap.h | 1 + lib/ARCMigrate/TransAPIUses.cpp | 24 ++++- lib/ARCMigrate/TransAutoreleasePool.cpp | 3 + lib/ARCMigrate/TransProperties.cpp | 83 ++++++++++++++-- lib/ARCMigrate/TransRetainReleaseDealloc.cpp | 7 +- lib/ARCMigrate/TransUnbridgedCasts.cpp | 94 +++++++++++++++++-- lib/ARCMigrate/Transforms.cpp | 10 +- lib/ARCMigrate/Transforms.h | 3 +- lib/AST/ParentMap.cpp | 8 ++ test/ARCMT/Common.h | 8 ++ test/ARCMT/api.m | 9 ++ test/ARCMT/api.m.result | 9 ++ test/ARCMT/assign-prop-with-arc-runtime.m | 13 ++- .../assign-prop-with-arc-runtime.m.result | 13 ++- test/ARCMT/atautorelease.m | 14 +++ test/ARCMT/atautorelease.m.result | 14 +++ test/ARCMT/nonobjc-to-objc-cast-2.m | 23 ++++- test/ARCMT/nonobjc-to-objc-cast.m | 18 ++++ test/ARCMT/nonobjc-to-objc-cast.m.result | 18 ++++ test/ARCMT/releases.m | 12 +++ test/ARCMT/releases.m.result | 11 +++ 21 files changed, 367 insertions(+), 28 deletions(-) create mode 100644 test/ARCMT/api.m create mode 100644 test/ARCMT/api.m.result diff --git a/include/clang/AST/ParentMap.h b/include/clang/AST/ParentMap.h index 22c1e7269f..62eae02c15 100644 --- a/include/clang/AST/ParentMap.h +++ b/include/clang/AST/ParentMap.h @@ -32,6 +32,7 @@ public: Stmt *getParent(Stmt*) const; Stmt *getParentIgnoreParens(Stmt *) const; Stmt *getParentIgnoreParenCasts(Stmt *) const; + Stmt *getParentIgnoreParenImpCasts(Stmt *) const; Stmt *getOuterParenParent(Stmt *) const; const Stmt *getParent(const Stmt* S) const { diff --git a/lib/ARCMigrate/TransAPIUses.cpp b/lib/ARCMigrate/TransAPIUses.cpp index 228ca3e091..781ad7742d 100644 --- a/lib/ARCMigrate/TransAPIUses.cpp +++ b/lib/ARCMigrate/TransAPIUses.cpp @@ -9,17 +9,19 @@ // // checkAPIUses: // -// Emits error with some API uses that are not safe in ARC mode: +// Emits error/fix with some API uses that are obsolete or not safe in ARC mode: // // - NSInvocation's [get/set]ReturnValue and [get/set]Argument are only safe // with __unsafe_unretained objects. // - When a NSData's 'bytes' family of methods are used on a local var, // add __attribute__((objc_precise_lifetime)) to make it safer. +// - Calling -zone gets replaced with 'nil'. // //===----------------------------------------------------------------------===// #include "Transforms.h" #include "Internals.h" +#include "clang/Sema/SemaDiagnostic.h" using namespace clang; using namespace arcmt; @@ -35,6 +37,8 @@ class APIChecker : public RecursiveASTVisitor { Selector bytesSel, getBytesSel, getBytesLengthSel, getBytesRangeSel; + Selector zoneSel; + llvm::DenseSet ChangedNSDataVars; public: APIChecker(MigrationPass &pass) : Pass(pass) { @@ -57,9 +61,12 @@ public: getBytesLengthSel = sels.getSelector(2, selIds); selIds[1] = &ids.get("range"); getBytesRangeSel = sels.getSelector(2, selIds); + + zoneSel = sels.getNullarySelector(&ids.get("zone")); } bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + // NSInvocation. if (E->isInstanceMessage() && E->getReceiverInterface() && E->getReceiverInterface()->getName() == "NSInvocation") { @@ -91,6 +98,7 @@ public: return true; } + // NSData. if (E->isInstanceMessage() && E->getReceiverInterface() && E->getReceiverInterface()->getName() == "NSData" && @@ -111,6 +119,20 @@ public: } } + // -zone. + if (E->isInstanceMessage() && + E->getInstanceReceiver() && + E->getSelector() == zoneSel && + Pass.TA.hasDiagnostic(diag::err_unavailable, + diag::err_unavailable_message, + E->getInstanceReceiver()->getExprLoc())) { + // Calling -zone is meaningless in ARC, change it to nil. + Transaction Trans(Pass.TA); + Pass.TA.clearDiagnostic(diag::err_unavailable, + diag::err_unavailable_message, + E->getInstanceReceiver()->getExprLoc()); + Pass.TA.replace(E->getSourceRange(), getNilString(Pass.Ctx)); + } return true; } }; diff --git a/lib/ARCMigrate/TransAutoreleasePool.cpp b/lib/ARCMigrate/TransAutoreleasePool.cpp index b9c22b0308..08561f97f9 100644 --- a/lib/ARCMigrate/TransAutoreleasePool.cpp +++ b/lib/ARCMigrate/TransAutoreleasePool.cpp @@ -286,6 +286,9 @@ private: } bool isInScope(SourceLocation loc) { + if (loc.isInvalid()) + return false; + SourceManager &SM = Ctx.getSourceManager(); if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin())) return false; diff --git a/lib/ARCMigrate/TransProperties.cpp b/lib/ARCMigrate/TransProperties.cpp index 365168e933..943eea2646 100644 --- a/lib/ARCMigrate/TransProperties.cpp +++ b/lib/ARCMigrate/TransProperties.cpp @@ -45,6 +45,7 @@ namespace { class PropertiesRewriter { MigrationPass &Pass; + ObjCImplementationDecl *CurImplD; struct PropData { ObjCPropertyDecl *PropD; @@ -62,6 +63,7 @@ public: PropertiesRewriter(MigrationPass &pass) : Pass(pass) { } void doTransform(ObjCImplementationDecl *D) { + CurImplD = D; ObjCInterfaceDecl *iface = D->getClassInterface(); if (!iface) return; @@ -134,8 +136,16 @@ private: return; } - if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) + if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) { + if (hasIvarAssignedAPlusOneObject(props)) { + rewriteAttribute("assign", "strong", atLoc); + return; + } return rewriteAssign(props, atLoc); + } + + if (hasIvarAssignedAPlusOneObject(props)) + return maybeAddStrongAttr(props, atLoc); return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); } @@ -162,15 +172,15 @@ private: void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, SourceLocation atLoc) const { ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); - if ((propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) && - hasNoBackingIvars(props)) - return; bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); - bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", - atLoc); - if (!addedAttr) - canUseWeak = false; + if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) || + !hasAllIvarsBacked(props)) { + bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", + atLoc); + if (!addedAttr) + canUseWeak = false; + } for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { if (isUserDeclared(I->IvarD)) @@ -186,6 +196,25 @@ private: } } + void maybeAddStrongAttr(PropsTy &props, SourceLocation atLoc) const { + ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); + + if (!(propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) || + !hasAllIvarsBacked(props)) { + addAttribute("strong", atLoc); + } + + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + if (I->ImplD) { + Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, + I->ImplD->getLocation()); + Pass.TA.clearDiagnostic( + diag::err_arc_objc_property_default_assign_on_object, + I->ImplD->getLocation()); + } + } + } + bool rewriteAttribute(StringRef fromAttr, StringRef toAttr, SourceLocation atLoc) const { if (atLoc.isMacroID()) @@ -290,6 +319,40 @@ private: return true; } + class PlusOneAssign : public RecursiveASTVisitor { + ObjCIvarDecl *Ivar; + public: + PlusOneAssign(ObjCIvarDecl *D) : Ivar(D) {} + + bool VisitBinAssign(BinaryOperator *E) { + Expr *lhs = E->getLHS()->IgnoreParenImpCasts(); + if (ObjCIvarRefExpr *RE = dyn_cast(lhs)) { + if (RE->getDecl() != Ivar) + return true; + + ImplicitCastExpr *implCE = dyn_cast(E->getRHS()); + while (implCE && implCE->getCastKind() == CK_BitCast) + implCE = dyn_cast(implCE->getSubExpr()); + + if (implCE && implCE->getCastKind() == CK_ObjCConsumeObject) + return false; + } + + return true; + } + }; + + bool hasIvarAssignedAPlusOneObject(PropsTy &props) const { + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + PlusOneAssign oneAssign(I->IvarD); + bool notFound = oneAssign.TraverseDecl(CurImplD); + if (!notFound) + return true; + } + + return false; + } + bool hasIvarWithExplicitOwnership(PropsTy &props) const { for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { if (isUserDeclared(I->IvarD)) { @@ -304,9 +367,9 @@ private: return false; } - bool hasNoBackingIvars(PropsTy &props) const { + bool hasAllIvarsBacked(PropsTy &props) const { for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) - if (I->IvarD) + if (!isUserDeclared(I->IvarD)) return false; return true; diff --git a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp index 4a5cdf8056..394f8480e1 100644 --- a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp +++ b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp @@ -129,10 +129,9 @@ public: // Change the -release to "receiver = nil" in a finally to avoid a leak // when an exception is thrown. Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); - if (Pass.Ctx.Idents.get("nil").hasMacroDefinition()) - Pass.TA.insertAfterToken(rec->getLocEnd(), " = nil"); - else - Pass.TA.insertAfterToken(rec->getLocEnd(), " = 0"); + std::string str = " = "; + str += getNilString(Pass.Ctx); + Pass.TA.insertAfterToken(rec->getLocEnd(), str); return true; } 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{ MigrationPass &Pass; IdentifierInfo *SelfII; + llvm::OwningPtr 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(parent)); + + if (ReturnStmt *retS = dyn_cast_or_null(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(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(E)) + return ME->getMethodFamily(); + + return OMF_None; + } + + bool isPassedToCFRetain(Expr *E, CallExpr *&callE) const { + if ((callE = dyn_cast_or_null( + StmtMap->getParentIgnoreParenImpCasts(E)))) + if (FunctionDecl * + FD = dyn_cast_or_null(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(E)) - if (DRE->getDecl()->getIdentifier() == SelfII) - return true; + if (ImplicitParamDecl *IPD = dyn_cast(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 trans(pass); trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); } diff --git a/lib/ARCMigrate/Transforms.cpp b/lib/ARCMigrate/Transforms.cpp index c584d6a243..1d0e26153c 100644 --- a/lib/ARCMigrate/Transforms.cpp +++ b/lib/ARCMigrate/Transforms.cpp @@ -154,7 +154,8 @@ bool trans::hasSideEffects(Expr *E, ASTContext &Ctx) { bool trans::isGlobalVar(Expr *E) { E = E->IgnoreParenCasts(); if (DeclRefExpr *DRE = dyn_cast(E)) - return DRE->getDecl()->getDeclContext()->isFileContext(); + return DRE->getDecl()->getDeclContext()->isFileContext() && + DRE->getDecl()->getLinkage() == ExternalLinkage; if (ConditionalOperator *condOp = dyn_cast(E)) return isGlobalVar(condOp->getTrueExpr()) && isGlobalVar(condOp->getFalseExpr()); @@ -162,6 +163,13 @@ bool trans::isGlobalVar(Expr *E) { return false; } +StringRef trans::getNilString(ASTContext &Ctx) { + if (Ctx.Idents.get("nil").hasMacroDefinition()) + return "nil"; + else + return "0"; +} + namespace { class ReferenceClear : public RecursiveASTVisitor { diff --git a/lib/ARCMigrate/Transforms.h b/lib/ARCMigrate/Transforms.h index 150893e643..6b39e90aea 100644 --- a/lib/ARCMigrate/Transforms.h +++ b/lib/ARCMigrate/Transforms.h @@ -56,7 +56,8 @@ SourceLocation findLocationAfterSemi(SourceLocation loc, ASTContext &Ctx); bool hasSideEffects(Expr *E, ASTContext &Ctx); bool isGlobalVar(Expr *E); - +/// \brief Returns "nil" or "0" if 'nil' macro is not actually defined. +StringRef getNilString(ASTContext &Ctx); template class BodyTransform : public RecursiveASTVisitor > { diff --git a/lib/AST/ParentMap.cpp b/lib/AST/ParentMap.cpp index b7b2005e9f..5eef83a81a 100644 --- a/lib/AST/ParentMap.cpp +++ b/lib/AST/ParentMap.cpp @@ -66,6 +66,14 @@ Stmt *ParentMap::getParentIgnoreParenCasts(Stmt *S) const { return S; } +Stmt *ParentMap::getParentIgnoreParenImpCasts(Stmt *S) const { + do { + S = getParent(S); + } while (S && isa(S) && cast(S)->IgnoreParenImpCasts() != S); + + return S; +} + Stmt *ParentMap::getOuterParenParent(Stmt *S) const { Stmt *Paren = 0; while (isa(S)) { diff --git a/test/ARCMT/Common.h b/test/ARCMT/Common.h index c57f3e75e4..f10ae0d2a7 100644 --- a/test/ARCMT/Common.h +++ b/test/ARCMT/Common.h @@ -4,6 +4,8 @@ #define NS_AUTOMATED_REFCOUNT_UNAVAILABLE #endif +#define nil ((void*) 0) + typedef int BOOL; typedef unsigned NSUInteger; typedef int int32_t; @@ -11,8 +13,14 @@ typedef unsigned char uint8_t; typedef int32_t UChar32; typedef unsigned char UChar; +typedef struct _NSZone NSZone; + +typedef const void * CFTypeRef; +CFTypeRef CFRetain(CFTypeRef cf); + @protocol NSObject - (BOOL)isEqual:(id)object; +- (NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE; - (id)retain NS_AUTOMATED_REFCOUNT_UNAVAILABLE; - (NSUInteger)retainCount NS_AUTOMATED_REFCOUNT_UNAVAILABLE; - (oneway void)release NS_AUTOMATED_REFCOUNT_UNAVAILABLE; diff --git a/test/ARCMT/api.m b/test/ARCMT/api.m new file mode 100644 index 0000000000..75401e5772 --- /dev/null +++ b/test/ARCMT/api.m @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +void test(NSObject *o) { + NSZone *z = [o zone]; +} diff --git a/test/ARCMT/api.m.result b/test/ARCMT/api.m.result new file mode 100644 index 0000000000..bd9c6a3195 --- /dev/null +++ b/test/ARCMT/api.m.result @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +void test(NSObject *o) { + NSZone *z = nil; +} diff --git a/test/ARCMT/assign-prop-with-arc-runtime.m b/test/ARCMT/assign-prop-with-arc-runtime.m index 4e4ae774a2..1671d6d020 100644 --- a/test/ARCMT/assign-prop-with-arc-runtime.m +++ b/test/ARCMT/assign-prop-with-arc-runtime.m @@ -20,8 +20,9 @@ typedef _NSCachedAttributedString *BadClassForWeak; id not_safe1; NSObject *not_safe2; Forw *not_safe3; + Foo *assign_plus1; } -@property (readonly,assign) Foo *x; +@property (readonly) Foo *x; @property (assign) Foo *w; @property Foo *q1, *q2; @property (assign) WeakOptOut *oo; @@ -29,12 +30,22 @@ typedef _NSCachedAttributedString *BadClassForWeak; @property (assign) id not_safe1; @property () NSObject *not_safe2; @property Forw *not_safe3; +@property (readonly) Foo *assign_plus1; +@property (readonly) Foo *assign_plus2; @property (assign) Foo *no_user_ivar1; @property (readonly) Foo *no_user_ivar2; + +-(void)test; @end @implementation Foo @synthesize x,w,q1,q2,oo,bcw,not_safe1,not_safe2,not_safe3; @synthesize no_user_ivar1, no_user_ivar2; +@synthesize assign_plus1, assign_plus2; + +-(void)test { + assign_plus1 = [[Foo alloc] init]; + assign_plus2 = [Foo new]; +} @end diff --git a/test/ARCMT/assign-prop-with-arc-runtime.m.result b/test/ARCMT/assign-prop-with-arc-runtime.m.result index eb34c168e4..d30a2ac00b 100644 --- a/test/ARCMT/assign-prop-with-arc-runtime.m.result +++ b/test/ARCMT/assign-prop-with-arc-runtime.m.result @@ -20,8 +20,9 @@ typedef _NSCachedAttributedString *BadClassForWeak; id __unsafe_unretained not_safe1; NSObject *__unsafe_unretained not_safe2; Forw *__unsafe_unretained not_safe3; + Foo *assign_plus1; } -@property (readonly,weak) Foo *x; +@property (readonly) Foo *x; @property (weak) Foo *w; @property (weak) Foo *q1, *q2; @property (unsafe_unretained) WeakOptOut *oo; @@ -29,12 +30,22 @@ typedef _NSCachedAttributedString *BadClassForWeak; @property (unsafe_unretained) id not_safe1; @property (unsafe_unretained) NSObject *not_safe2; @property (unsafe_unretained) Forw *not_safe3; +@property (readonly) Foo *assign_plus1; +@property (strong, readonly) Foo *assign_plus2; @property (weak) Foo *no_user_ivar1; @property (weak, readonly) Foo *no_user_ivar2; + +-(void)test; @end @implementation Foo @synthesize x,w,q1,q2,oo,bcw,not_safe1,not_safe2,not_safe3; @synthesize no_user_ivar1, no_user_ivar2; +@synthesize assign_plus1, assign_plus2; + +-(void)test { + assign_plus1 = [[Foo alloc] init]; + assign_plus2 = [Foo new]; +} @end diff --git a/test/ARCMT/atautorelease.m b/test/ARCMT/atautorelease.m index bdf77197a8..1ba85b4fe4 100644 --- a/test/ARCMT/atautorelease.m +++ b/test/ARCMT/atautorelease.m @@ -45,3 +45,17 @@ int main2(int argc, char *argv[]) { [pool release]; return result; } + +@interface Foo : NSObject +@property (assign) id myProp; +@end + +@implementation Foo +@synthesize myProp; + +-(void)test:(id)p { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [pool drain]; + self.myProp = p; +} +@end diff --git a/test/ARCMT/atautorelease.m.result b/test/ARCMT/atautorelease.m.result index ccce1bd875..516fa2d5d3 100644 --- a/test/ARCMT/atautorelease.m.result +++ b/test/ARCMT/atautorelease.m.result @@ -44,3 +44,17 @@ int main2(int argc, char *argv[]) { return result; } } + +@interface Foo : NSObject +@property (unsafe_unretained) id myProp; +@end + +@implementation Foo +@synthesize myProp; + +-(void)test:(id)p { + @autoreleasepool { + } + self.myProp = p; +} +@end diff --git a/test/ARCMT/nonobjc-to-objc-cast-2.m b/test/ARCMT/nonobjc-to-objc-cast-2.m index 46ef02400e..08765b11f7 100644 --- a/test/ARCMT/nonobjc-to-objc-cast-2.m +++ b/test/ARCMT/nonobjc-to-objc-cast-2.m @@ -1,9 +1,13 @@ // RUN: %clang_cc1 -arcmt-check -verify -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi %s -typedef int BOOL; -typedef const struct __CFString * CFStringRef; +#include "Common.h" + +@interface NSString : NSObject +-(id)string; +-(id)newString; +@end -@class NSString; +typedef const struct __CFString * CFStringRef; void f(BOOL b) { CFStringRef cfstr; @@ -12,3 +16,16 @@ void f(BOOL b) { // expected-note{{use __bridge_transfer to transfer ownership of a +1 'CFStringRef' (aka 'const struct __CFString *') into ARC}} void *vp = str; // expected-error {{disallowed}} } + +void f2(NSString *s) { + CFStringRef ref; + ref = [(CFStringRef)[s string] retain]; // expected-error {{cast of Objective-C pointer type 'id' to C pointer type 'CFStringRef' (aka 'const struct __CFString *') requires a bridged cast}} \ + // expected-error {{ bad receiver type 'CFStringRef' (aka 'const struct __CFString *')}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_retained to make an ARC object available as a +1 'CFStringRef' (aka 'const struct __CFString *')}} +} + +CFStringRef f3() { + return (CFStringRef)[[[NSString alloc] init] autorelease]; // expected-error {{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}} \ + // expected-note {{remove the cast and change return type of function to 'NSString *' to have the object automatically autoreleased}} +} diff --git a/test/ARCMT/nonobjc-to-objc-cast.m b/test/ARCMT/nonobjc-to-objc-cast.m index 4e1e293efb..b4e4080149 100644 --- a/test/ARCMT/nonobjc-to-objc-cast.m +++ b/test/ARCMT/nonobjc-to-objc-cast.m @@ -5,6 +5,8 @@ #include "Common.h" @interface NSString : NSObject +-(id)string; +-(id)newString; @end typedef const struct __CFString * CFStringRef; @@ -27,6 +29,7 @@ void f(BOOL b, id p) { CFUUIDRef _uuid; NSString *_uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid); _uuidString = [(NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid) autorelease]; + _uuidString = CFRetain(_uuid); } @implementation NSString (StrExt) @@ -36,3 +39,18 @@ void f(BOOL b, id p) { return self; } @end + +void f2(NSString *s) { + CFStringRef ref = [s string]; + ref = (CFStringRef)[s string]; + ref = s.string; + ref = [NSString new]; + ref = [s newString]; + ref = (CFStringRef)[NSString new]; + ref = [[NSString alloc] init]; + ref = [[s string] retain]; + ref = CFRetain((CFStringRef)[s string]); + ref = CFRetain([s string]); + ref = CFRetain(s); + ref = [s retain]; +} diff --git a/test/ARCMT/nonobjc-to-objc-cast.m.result b/test/ARCMT/nonobjc-to-objc-cast.m.result index 53dde7553d..1fc2774c14 100644 --- a/test/ARCMT/nonobjc-to-objc-cast.m.result +++ b/test/ARCMT/nonobjc-to-objc-cast.m.result @@ -5,6 +5,8 @@ #include "Common.h" @interface NSString : NSObject +-(id)string; +-(id)newString; @end typedef const struct __CFString * CFStringRef; @@ -27,6 +29,7 @@ void f(BOOL b, id p) { CFUUIDRef _uuid; NSString *_uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid); _uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid); + _uuidString = (__bridge_transfer NSString *)(CFRetain(_uuid)); } @implementation NSString (StrExt) @@ -36,3 +39,18 @@ void f(BOOL b, id p) { return self; } @end + +void f2(NSString *s) { + CFStringRef ref = (__bridge CFStringRef)([s string]); + ref = (__bridge CFStringRef)[s string]; + ref = (__bridge CFStringRef)(s.string); + ref = (__bridge_retained CFStringRef)([NSString new]); + ref = (__bridge_retained CFStringRef)([s newString]); + ref = (__bridge_retained CFStringRef)[NSString new]; + ref = (__bridge_retained CFStringRef)([[NSString alloc] init]); + ref = (__bridge_retained CFStringRef)([s string]); + ref = (__bridge_retained CFStringRef)[s string]; + ref = (__bridge_retained CFTypeRef)([s string]); + ref = (__bridge_retained CFTypeRef)(s); + ref = (__bridge_retained CFStringRef)(s); +} diff --git a/test/ARCMT/releases.m b/test/ARCMT/releases.m index d5f21dc4c2..93db37c8a2 100644 --- a/test/ARCMT/releases.m +++ b/test/ARCMT/releases.m @@ -85,3 +85,15 @@ void test2(id p) { RELEASE_MACRO(p); RELEASE_MACRO2(p); } + +@implementation Foo2 + +static id internal_var = 0; + ++ (void)setIt:(id)newone { + if (internal_var != newone) { + [internal_var release]; + internal_var = [newone retain]; + } +} +@end diff --git a/test/ARCMT/releases.m.result b/test/ARCMT/releases.m.result index 1d36f3e6b7..721060b38d 100644 --- a/test/ARCMT/releases.m.result +++ b/test/ARCMT/releases.m.result @@ -77,3 +77,14 @@ void block_test(Foo *p) { void test2(id p) { } + +@implementation Foo2 + +static id internal_var = 0; + ++ (void)setIt:(id)newone { + if (internal_var != newone) { + internal_var = newone; + } +} +@end -- 2.40.0