[arcmt] Remove an unused -autorelease, without failing with error, for this

idiom that is used commonly in setters:

   [backingValue autorelease];
   backingValue = [newValue retain]; // in general a +1 assign

rdar://9914061

llvm-svn: 157347
This commit is contained in:
Argyrios Kyrtzidis 2012-05-23 21:50:04 +00:00
parent 37a12af0af
commit 0b21d82437
10 changed files with 203 additions and 70 deletions

View File

@ -309,17 +309,8 @@ private:
if (RE->getDecl() != Ivar) if (RE->getDecl() != Ivar)
return true; return true;
if (ObjCMessageExpr * if (isPlusOneAssign(E))
ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
if (ME->getMethodFamily() == OMF_retain)
return false; return false;
ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
while (implCE && implCE->getCastKind() == CK_BitCast)
implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
return false;
} }
return true; return true;

View File

@ -64,14 +64,16 @@ public:
return true; return true;
case OMF_autorelease: case OMF_autorelease:
if (isRemovable(E)) { if (isRemovable(E)) {
// An unused autorelease is badness. If we remove it the receiver if (!isCommonUnusedAutorelease(E)) {
// will likely die immediately while previously it was kept alive // An unused autorelease is badness. If we remove it the receiver
// by the autorelease pool. This is bad practice in general, leave it // will likely die immediately while previously it was kept alive
// and emit an error to force the user to restructure his code. // by the autorelease pool. This is bad practice in general, leave it
Pass.TA.reportError("it is not safe to remove an unused 'autorelease' " // and emit an error to force the user to restructure his code.
"message; its receiver may be destroyed immediately", Pass.TA.reportError("it is not safe to remove an unused 'autorelease' "
E->getLocStart(), E->getSourceRange()); "message; its receiver may be destroyed immediately",
return true; E->getLocStart(), E->getSourceRange());
return true;
}
} }
// Pass through. // Pass through.
case OMF_retain: case OMF_retain:
@ -156,6 +158,80 @@ public:
} }
private: private:
/// \brief Checks for idioms where an unused -autorelease is common.
///
/// Currently only returns true for this idiom which is common in property
/// setters:
///
/// [backingValue autorelease];
/// backingValue = [newValue retain]; // in general a +1 assign
///
bool isCommonUnusedAutorelease(ObjCMessageExpr *E) {
Expr *Rec = E->getInstanceReceiver();
if (!Rec)
return false;
Decl *RefD = getReferencedDecl(Rec);
if (!RefD)
return false;
Stmt *OuterS = E, *InnerS;
do {
InnerS = OuterS;
OuterS = StmtMap->getParent(InnerS);
}
while (OuterS && (isa<ParenExpr>(OuterS) ||
isa<CastExpr>(OuterS) ||
isa<ExprWithCleanups>(OuterS)));
if (!OuterS)
return false;
// Find next statement after the -autorelease.
Stmt::child_iterator currChildS = OuterS->child_begin();
Stmt::child_iterator childE = OuterS->child_end();
for (; currChildS != childE; ++currChildS) {
if (*currChildS == InnerS)
break;
}
if (currChildS == childE)
return false;
++currChildS;
if (currChildS == childE)
return false;
Stmt *nextStmt = *currChildS;
if (!nextStmt)
return false;
nextStmt = nextStmt->IgnoreImplicit();
// Check for "RefD = [+1 retained object];".
if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(nextStmt)) {
if (RefD != getReferencedDecl(Bop->getLHS()))
return false;
if (isPlusOneAssign(Bop))
return true;
}
return false;
}
Decl *getReferencedDecl(Expr *E) {
if (!E)
return 0;
E = E->IgnoreParenCasts();
if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E))
return DRE->getDecl();
if (MemberExpr *ME = dyn_cast<MemberExpr>(E))
return ME->getMemberDecl();
if (ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(E))
return IRE->getDecl();
return 0;
}
/// \brief Check if the retain/release is due to a GCD/XPC macro that are /// \brief Check if the retain/release is due to a GCD/XPC macro that are
/// defined as: /// defined as:
/// ///

View File

@ -14,6 +14,7 @@
#include "clang/AST/StmtVisitor.h" #include "clang/AST/StmtVisitor.h"
#include "clang/Lex/Lexer.h" #include "clang/Lex/Lexer.h"
#include "clang/Basic/SourceManager.h" #include "clang/Basic/SourceManager.h"
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseSet.h"
#include <map> #include <map>
@ -56,6 +57,47 @@ bool trans::canApplyWeak(ASTContext &Ctx, QualType type,
return true; return true;
} }
bool trans::isPlusOneAssign(const BinaryOperator *E) {
if (E->getOpcode() != BO_Assign)
return false;
if (const ObjCMessageExpr *
ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
if (ME->getMethodFamily() == OMF_retain)
return true;
if (const CallExpr *
callE = dyn_cast<CallExpr>(E->getRHS()->IgnoreParenCasts())) {
if (const FunctionDecl *FD = callE->getDirectCallee()) {
if (FD->getAttr<CFReturnsRetainedAttr>())
return true;
if (FD->isGlobal() &&
FD->getIdentifier() &&
FD->getParent()->isTranslationUnit() &&
FD->getLinkage() == ExternalLinkage &&
ento::cocoa::isRefType(callE->getType(), "CF",
FD->getIdentifier()->getName())) {
StringRef fname = FD->getIdentifier()->getName();
if (fname.endswith("Retain") ||
fname.find("Create") != StringRef::npos ||
fname.find("Copy") != StringRef::npos) {
return true;
}
}
}
}
const ImplicitCastExpr *implCE = dyn_cast<ImplicitCastExpr>(E->getRHS());
while (implCE && implCE->getCastKind() == CK_BitCast)
implCE = dyn_cast<ImplicitCastExpr>(implCE->getSubExpr());
if (implCE && implCE->getCastKind() == CK_ARCConsumeObject)
return true;
return false;
}
/// \brief 'Loc' is the end of a statement range. This returns the location /// \brief 'Loc' is the end of a statement range. This returns the location
/// immediately after the semicolon following the statement. /// immediately after the semicolon following the statement.
/// If no semicolon is found or the location is inside a macro, the returned /// If no semicolon is found or the location is inside a macro, the returned

View File

@ -154,6 +154,8 @@ public:
bool canApplyWeak(ASTContext &Ctx, QualType type, bool canApplyWeak(ASTContext &Ctx, QualType type,
bool AllowOnUnknownClass = false); bool AllowOnUnknownClass = false);
bool isPlusOneAssign(const BinaryOperator *E);
/// \brief 'Loc' is the end of a statement range. This returns the location /// \brief 'Loc' is the end of a statement range. This returns the location
/// immediately after the semicolon following the statement. /// immediately after the semicolon following the statement.
/// If no semicolon is found or the location is inside a macro, the returned /// If no semicolon is found or the location is inside a macro, the returned

View File

@ -68,3 +68,14 @@ typedef const void* objc_objectptr_t;
extern __attribute__((ns_returns_retained)) id objc_retainedObject(objc_objectptr_t __attribute__((cf_consumed)) pointer); extern __attribute__((ns_returns_retained)) id objc_retainedObject(objc_objectptr_t __attribute__((cf_consumed)) pointer);
extern __attribute__((ns_returns_not_retained)) id objc_unretainedObject(objc_objectptr_t pointer); extern __attribute__((ns_returns_not_retained)) id objc_unretainedObject(objc_objectptr_t pointer);
extern objc_objectptr_t objc_unretainedPointer(id object); extern objc_objectptr_t objc_unretainedPointer(id object);
#define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; })
#define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; })
#define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; })
#define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; })
typedef id dispatch_object_t;
typedef id xpc_object_t;
void _dispatch_object_validate(dispatch_object_t object);
void _xpc_object_validate(xpc_object_t object);

View File

@ -3,20 +3,7 @@
// RUN: diff %t %s.result // RUN: diff %t %s.result
// DISABLE: mingw32 // DISABLE: mingw32
typedef unsigned char BOOL; #include "Common.h"
@interface NSObject {
id isa;
}
+new;
+alloc;
-init;
-autorelease;
@end
@interface NSAutoreleasePool : NSObject
- drain;
@end
@interface A : NSObject { @interface A : NSObject {
@package @package
@ -24,8 +11,13 @@ typedef unsigned char BOOL;
} }
@end @end
@interface B : NSObject @interface B : NSObject {
id _prop;
xpc_object_t _xpc_prop;
}
- (BOOL)containsSelf:(A*)a; - (BOOL)containsSelf:(A*)a;
@property (retain) id prop;
@property (retain) xpc_object_t xpc_prop;
@end @end
@implementation A @implementation A
@ -35,6 +27,26 @@ typedef unsigned char BOOL;
- (BOOL)containsSelf:(A*)a { - (BOOL)containsSelf:(A*)a {
return a->object == self; return a->object == self;
} }
-(id) prop {
return _prop;
}
-(void) setProp:(id) newVal {
[_prop autorelease];
_prop = [newVal retain];
}
-(void) setProp2:(CFTypeRef) newVal {
[_prop autorelease];
_prop = (id)CFRetain(newVal);
}
-(id) xpc_prop {
return _xpc_prop;
}
-(void) setXpc_prop:(xpc_object_t) newVal {
[_xpc_prop autorelease];
_xpc_prop = xpc_retain(newVal);
}
@end @end
void NSLog(id, ...); void NSLog(id, ...);
@ -47,3 +59,8 @@ int main (int argc, const char * argv[]) {
[pool drain]; [pool drain];
return 0; return 0;
} }
void test(A *prevVal, A *newVal) {
[prevVal autorelease];
prevVal = [newVal retain];
}

View File

@ -3,20 +3,7 @@
// RUN: diff %t %s.result // RUN: diff %t %s.result
// DISABLE: mingw32 // DISABLE: mingw32
typedef unsigned char BOOL; #include "Common.h"
@interface NSObject {
id isa;
}
+new;
+alloc;
-init;
-autorelease;
@end
@interface NSAutoreleasePool : NSObject
- drain;
@end
@interface A : NSObject { @interface A : NSObject {
@package @package
@ -24,8 +11,13 @@ typedef unsigned char BOOL;
} }
@end @end
@interface B : NSObject @interface B : NSObject {
id _prop;
xpc_object_t _xpc_prop;
}
- (BOOL)containsSelf:(A*)a; - (BOOL)containsSelf:(A*)a;
@property (strong) id prop;
@property (strong) xpc_object_t xpc_prop;
@end @end
@implementation A @implementation A
@ -35,6 +27,23 @@ typedef unsigned char BOOL;
- (BOOL)containsSelf:(A*)a { - (BOOL)containsSelf:(A*)a {
return a->object == self; return a->object == self;
} }
-(id) prop {
return _prop;
}
-(void) setProp:(id) newVal {
_prop = newVal;
}
-(void) setProp2:(CFTypeRef) newVal {
_prop = (__bridge_transfer id)CFRetain(newVal);
}
-(id) xpc_prop {
return _xpc_prop;
}
-(void) setXpc_prop:(xpc_object_t) newVal {
_xpc_prop = newVal;
}
@end @end
void NSLog(id, ...); void NSLog(id, ...);
@ -47,3 +56,7 @@ int main (int argc, const char * argv[]) {
} }
return 0; return 0;
} }
void test(A *prevVal, A *newVal) {
prevVal = newVal;
}

View File

@ -91,6 +91,9 @@ void test1(A *a, BOOL b, struct UnsafeS *unsafeS) {
[a release]; [a release];
[a autorelease]; // expected-error {{it is not safe to remove an unused 'autorelease' message; its receiver may be destroyed immediately}} \ [a autorelease]; // expected-error {{it is not safe to remove an unused 'autorelease' message; its receiver may be destroyed immediately}} \
// expected-error {{ARC forbids explicit message send}} // expected-error {{ARC forbids explicit message send}}
[a autorelease]; // expected-error {{it is not safe to remove an unused 'autorelease' message; its receiver may be destroyed immediately}} \
// expected-error {{ARC forbids explicit message send}}
a = 0;
CFStringRef cfstr; CFStringRef cfstr;
NSString *str = (NSString *)cfstr; // expected-error {{cast of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type 'NSString *' requires a bridged cast}} \ NSString *str = (NSString *)cfstr; // expected-error {{cast of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type 'NSString *' requires a bridged cast}} \

View File

@ -4,17 +4,6 @@
#include "Common.h" #include "Common.h"
#define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; })
#define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; })
#define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; })
#define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; })
typedef id dispatch_object_t;
typedef id xpc_object_t;
void _dispatch_object_validate(dispatch_object_t object);
void _xpc_object_validate(xpc_object_t object);
dispatch_object_t getme(void); dispatch_object_t getme(void);
void func(dispatch_object_t o) { void func(dispatch_object_t o) {

View File

@ -4,17 +4,6 @@
#include "Common.h" #include "Common.h"
#define dispatch_retain(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); (void)[_o retain]; })
#define dispatch_release(object) ({ dispatch_object_t _o = (object); _dispatch_object_validate(_o); [_o release]; })
#define xpc_retain(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o retain]; })
#define xpc_release(object) ({ xpc_object_t _o = (object); _xpc_object_validate(_o); [_o release]; })
typedef id dispatch_object_t;
typedef id xpc_object_t;
void _dispatch_object_validate(dispatch_object_t object);
void _xpc_object_validate(xpc_object_t object);
dispatch_object_t getme(void); dispatch_object_t getme(void);
void func(dispatch_object_t o) { void func(dispatch_object_t o) {