forked from OSchip/llvm-project
[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:
parent
37a12af0af
commit
0b21d82437
|
@ -309,17 +309,8 @@ private:
|
|||
if (RE->getDecl() != Ivar)
|
||||
return true;
|
||||
|
||||
if (ObjCMessageExpr *
|
||||
ME = dyn_cast<ObjCMessageExpr>(E->getRHS()->IgnoreParenCasts()))
|
||||
if (ME->getMethodFamily() == OMF_retain)
|
||||
if (isPlusOneAssign(E))
|
||||
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;
|
||||
|
|
|
@ -64,14 +64,16 @@ public:
|
|||
return true;
|
||||
case OMF_autorelease:
|
||||
if (isRemovable(E)) {
|
||||
// An unused autorelease is badness. If we remove it the receiver
|
||||
// will likely die immediately while previously it was kept alive
|
||||
// by the autorelease pool. This is bad practice in general, leave it
|
||||
// and emit an error to force the user to restructure his code.
|
||||
Pass.TA.reportError("it is not safe to remove an unused 'autorelease' "
|
||||
"message; its receiver may be destroyed immediately",
|
||||
E->getLocStart(), E->getSourceRange());
|
||||
return true;
|
||||
if (!isCommonUnusedAutorelease(E)) {
|
||||
// An unused autorelease is badness. If we remove it the receiver
|
||||
// will likely die immediately while previously it was kept alive
|
||||
// by the autorelease pool. This is bad practice in general, leave it
|
||||
// and emit an error to force the user to restructure his code.
|
||||
Pass.TA.reportError("it is not safe to remove an unused 'autorelease' "
|
||||
"message; its receiver may be destroyed immediately",
|
||||
E->getLocStart(), E->getSourceRange());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Pass through.
|
||||
case OMF_retain:
|
||||
|
@ -156,6 +158,80 @@ public:
|
|||
}
|
||||
|
||||
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
|
||||
/// defined as:
|
||||
///
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "clang/AST/StmtVisitor.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
|
||||
#include "llvm/ADT/StringSwitch.h"
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include <map>
|
||||
|
@ -56,6 +57,47 @@ bool trans::canApplyWeak(ASTContext &Ctx, QualType type,
|
|||
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
|
||||
/// immediately after the semicolon following the statement.
|
||||
/// If no semicolon is found or the location is inside a macro, the returned
|
||||
|
|
|
@ -154,6 +154,8 @@ public:
|
|||
bool canApplyWeak(ASTContext &Ctx, QualType type,
|
||||
bool AllowOnUnknownClass = false);
|
||||
|
||||
bool isPlusOneAssign(const BinaryOperator *E);
|
||||
|
||||
/// \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
|
||||
|
|
|
@ -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_not_retained)) id objc_unretainedObject(objc_objectptr_t pointer);
|
||||
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);
|
||||
|
|
|
@ -3,20 +3,7 @@
|
|||
// RUN: diff %t %s.result
|
||||
// DISABLE: mingw32
|
||||
|
||||
typedef unsigned char BOOL;
|
||||
|
||||
@interface NSObject {
|
||||
id isa;
|
||||
}
|
||||
+new;
|
||||
+alloc;
|
||||
-init;
|
||||
-autorelease;
|
||||
@end
|
||||
|
||||
@interface NSAutoreleasePool : NSObject
|
||||
- drain;
|
||||
@end
|
||||
#include "Common.h"
|
||||
|
||||
@interface A : NSObject {
|
||||
@package
|
||||
|
@ -24,8 +11,13 @@ typedef unsigned char BOOL;
|
|||
}
|
||||
@end
|
||||
|
||||
@interface B : NSObject
|
||||
@interface B : NSObject {
|
||||
id _prop;
|
||||
xpc_object_t _xpc_prop;
|
||||
}
|
||||
- (BOOL)containsSelf:(A*)a;
|
||||
@property (retain) id prop;
|
||||
@property (retain) xpc_object_t xpc_prop;
|
||||
@end
|
||||
|
||||
@implementation A
|
||||
|
@ -35,6 +27,26 @@ typedef unsigned char BOOL;
|
|||
- (BOOL)containsSelf:(A*)a {
|
||||
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
|
||||
|
||||
void NSLog(id, ...);
|
||||
|
@ -47,3 +59,8 @@ int main (int argc, const char * argv[]) {
|
|||
[pool drain];
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test(A *prevVal, A *newVal) {
|
||||
[prevVal autorelease];
|
||||
prevVal = [newVal retain];
|
||||
}
|
||||
|
|
|
@ -3,20 +3,7 @@
|
|||
// RUN: diff %t %s.result
|
||||
// DISABLE: mingw32
|
||||
|
||||
typedef unsigned char BOOL;
|
||||
|
||||
@interface NSObject {
|
||||
id isa;
|
||||
}
|
||||
+new;
|
||||
+alloc;
|
||||
-init;
|
||||
-autorelease;
|
||||
@end
|
||||
|
||||
@interface NSAutoreleasePool : NSObject
|
||||
- drain;
|
||||
@end
|
||||
#include "Common.h"
|
||||
|
||||
@interface A : NSObject {
|
||||
@package
|
||||
|
@ -24,8 +11,13 @@ typedef unsigned char BOOL;
|
|||
}
|
||||
@end
|
||||
|
||||
@interface B : NSObject
|
||||
@interface B : NSObject {
|
||||
id _prop;
|
||||
xpc_object_t _xpc_prop;
|
||||
}
|
||||
- (BOOL)containsSelf:(A*)a;
|
||||
@property (strong) id prop;
|
||||
@property (strong) xpc_object_t xpc_prop;
|
||||
@end
|
||||
|
||||
@implementation A
|
||||
|
@ -35,6 +27,23 @@ typedef unsigned char BOOL;
|
|||
- (BOOL)containsSelf:(A*)a {
|
||||
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
|
||||
|
||||
void NSLog(id, ...);
|
||||
|
@ -47,3 +56,7 @@ int main (int argc, const char * argv[]) {
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void test(A *prevVal, A *newVal) {
|
||||
prevVal = newVal;
|
||||
}
|
||||
|
|
|
@ -91,6 +91,9 @@ void test1(A *a, BOOL b, struct UnsafeS *unsafeS) {
|
|||
[a release];
|
||||
[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 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;
|
||||
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}} \
|
||||
|
|
|
@ -4,17 +4,6 @@
|
|||
|
||||
#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);
|
||||
|
||||
void func(dispatch_object_t o) {
|
||||
|
|
|
@ -4,17 +4,6 @@
|
|||
|
||||
#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);
|
||||
|
||||
void func(dispatch_object_t o) {
|
||||
|
|
Loading…
Reference in New Issue