[analyzer] RetainCountChecker: track ObjC boxed expression objects.
llvm-svn: 156699
This commit is contained in:
parent
4bd20c50eb
commit
6393f82b5b
|
@ -1867,6 +1867,15 @@ static inline bool contains(const SmallVectorImpl<ArgEffect>& V,
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool isNumericLiteralExpression(const Expr *E) {
|
||||
// FIXME: This set of cases was copied from SemaExprObjC.
|
||||
return isa<IntegerLiteral>(E) ||
|
||||
isa<CharacterLiteral>(E) ||
|
||||
isa<FloatingLiteral>(E) ||
|
||||
isa<ObjCBoolLiteralExpr>(E) ||
|
||||
isa<CXXBoolLiteralExpr>(E);
|
||||
}
|
||||
|
||||
static bool isPropertyAccess(const Stmt *S, ParentMap &PM) {
|
||||
unsigned maxDepth = 4;
|
||||
while (S && maxDepth) {
|
||||
|
@ -1916,6 +1925,24 @@ PathDiagnosticPiece *CFRefReportVisitor::VisitNode(const ExplodedNode *N,
|
|||
else if (isa<ObjCDictionaryLiteral>(S)) {
|
||||
os << "NSDictionary literal is an object with a +0 retain count";
|
||||
}
|
||||
else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
|
||||
if (isNumericLiteralExpression(BL->getSubExpr()))
|
||||
os << "NSNumber literal is an object with a +0 retain count";
|
||||
else {
|
||||
const ObjCInterfaceDecl *BoxClass = 0;
|
||||
if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
|
||||
BoxClass = Method->getClassInterface();
|
||||
|
||||
// We should always be able to find the boxing class interface,
|
||||
// but consider this future-proofing.
|
||||
if (BoxClass)
|
||||
os << *BoxClass << " b";
|
||||
else
|
||||
os << "B";
|
||||
|
||||
os << "oxed expression produces an object with a +0 retain count";
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
|
||||
// Get the name of the callee (if it is available).
|
||||
|
@ -2324,6 +2351,7 @@ class RetainCountChecker
|
|||
check::PostStmt<CXXConstructExpr>,
|
||||
check::PostStmt<ObjCArrayLiteral>,
|
||||
check::PostStmt<ObjCDictionaryLiteral>,
|
||||
check::PostStmt<ObjCBoxedExpr>,
|
||||
check::PostObjCMessage,
|
||||
check::PreStmt<ReturnStmt>,
|
||||
check::RegionChanges,
|
||||
|
@ -2470,6 +2498,8 @@ public:
|
|||
void checkPostStmt(const CXXConstructExpr *CE, CheckerContext &C) const;
|
||||
void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;
|
||||
void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
|
||||
void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const;
|
||||
|
||||
void checkPostObjCMessage(const ObjCMessage &Msg, CheckerContext &C) const;
|
||||
|
||||
void checkSummary(const RetainSummary &Summ, const CallOrObjCMessage &Call,
|
||||
|
@ -2721,6 +2751,21 @@ void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
|
|||
processObjCLiterals(C, DL);
|
||||
}
|
||||
|
||||
void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
|
||||
CheckerContext &C) const {
|
||||
const ExplodedNode *Pred = C.getPredecessor();
|
||||
const LocationContext *LCtx = Pred->getLocationContext();
|
||||
ProgramStateRef State = Pred->getState();
|
||||
|
||||
if (SymbolRef Sym = State->getSVal(Ex, LCtx).getAsSymbol()) {
|
||||
QualType ResultTy = Ex->getType();
|
||||
State = State->set<RefBindings>(Sym, RefVal::makeNotOwned(RetEffect::ObjC,
|
||||
ResultTy));
|
||||
}
|
||||
|
||||
C.addTransition(State);
|
||||
}
|
||||
|
||||
void RetainCountChecker::checkPostObjCMessage(const ObjCMessage &Msg,
|
||||
CheckerContext &C) const {
|
||||
ProgramStateRef state = C.getState();
|
||||
|
|
|
@ -588,7 +588,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
|||
case Stmt::ObjCIsaExprClass:
|
||||
case Stmt::ObjCProtocolExprClass:
|
||||
case Stmt::ObjCSelectorExprClass:
|
||||
case Expr::ObjCBoxedExprClass:
|
||||
case Stmt::ParenListExprClass:
|
||||
case Stmt::PredefinedExprClass:
|
||||
case Stmt::ShuffleVectorExprClass:
|
||||
|
@ -628,22 +627,24 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
|||
}
|
||||
|
||||
case Expr::ObjCArrayLiteralClass:
|
||||
case Expr::ObjCDictionaryLiteralClass: {
|
||||
case Expr::ObjCDictionaryLiteralClass:
|
||||
// FIXME: explicitly model with a region and the actual contents
|
||||
// of the container. For now, conjure a symbol.
|
||||
case Expr::ObjCBoxedExprClass: {
|
||||
Bldr.takeNodes(Pred);
|
||||
|
||||
ExplodedNodeSet preVisit;
|
||||
getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this);
|
||||
|
||||
// FIXME: explicitly model with a region and the actual contents
|
||||
// of the container. For now, conjure a symbol.
|
||||
ExplodedNodeSet Tmp;
|
||||
StmtNodeBuilder Bldr2(preVisit, Tmp, *currentBuilderContext);
|
||||
|
||||
const Expr *Ex = cast<Expr>(S);
|
||||
QualType resultType = Ex->getType();
|
||||
|
||||
for (ExplodedNodeSet::iterator it = preVisit.begin(), et = preVisit.end();
|
||||
it != et; ++it) {
|
||||
ExplodedNode *N = *it;
|
||||
const Expr *Ex = cast<Expr>(S);
|
||||
QualType resultType = Ex->getType();
|
||||
const LocationContext *LCtx = N->getLocationContext();
|
||||
SVal result =
|
||||
svalBuilder.getConjuredSymbolVal(0, Ex, LCtx, resultType,
|
||||
|
|
|
@ -130,3 +130,51 @@ CFTypeRef CFGetRuleViolation () {
|
|||
return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}}
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
typedef unsigned long NSUInteger;
|
||||
|
||||
@interface NSValue : NSObject
|
||||
@end
|
||||
|
||||
@interface NSNumber : NSValue
|
||||
+ (NSNumber *)numberWithInt:(int)i;
|
||||
@end
|
||||
|
||||
@interface NSString : NSObject
|
||||
+ (NSString *)stringWithUTF8String:(const char *)str;
|
||||
@end
|
||||
|
||||
@interface NSArray : NSObject
|
||||
+ (NSArray *)arrayWithObjects:(const id [])objects count:(NSUInteger)count;
|
||||
@end
|
||||
|
||||
@interface NSDictionary : NSObject
|
||||
+ (id)dictionaryWithObjects:(const id [])objects forKeys:(const id /* <NSCopying> */ [])keys count:(NSUInteger)count;
|
||||
@end
|
||||
|
||||
|
||||
void testNumericLiteral() {
|
||||
id result = @1; // expected-note{{NSNumber literal is an object with a +0 retain count}}
|
||||
[result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
|
||||
}
|
||||
|
||||
void testBoxedInt(int x) {
|
||||
id result = @(x); // expected-note{{NSNumber boxed expression produces an object with a +0 retain count}}
|
||||
[result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
|
||||
}
|
||||
|
||||
void testBoxedString(const char *str) {
|
||||
id result = @(str); // expected-note{{NSString boxed expression produces an object with a +0 retain count}}
|
||||
[result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
|
||||
}
|
||||
|
||||
void testArray(id obj) {
|
||||
id result = @[obj]; // expected-note{{NSArray literal is an object with a +0 retain count}}
|
||||
[result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
|
||||
}
|
||||
|
||||
void testDictionary(id key, id value) {
|
||||
id result = @{key: value}; // expected-note{{NSDictionary literal is an object with a +0 retain count}}
|
||||
[result release]; // expected-warning{{decrement}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
|
||||
}
|
||||
|
|
|
@ -142,9 +142,13 @@ NSFastEnumerationState;
|
|||
@end
|
||||
@class NSString, NSDictionary;
|
||||
@interface NSValue : NSObject <NSCopying, NSCoding> - (void)getValue:(void *)value;
|
||||
@end @interface NSNumber : NSValue - (char)charValue;
|
||||
@end
|
||||
@interface NSNumber : NSValue
|
||||
- (char)charValue;
|
||||
- (id)initWithInt:(int)value;
|
||||
@end @class NSString;
|
||||
+ (NSNumber *)numberWithInt:(int)value;
|
||||
@end
|
||||
@class NSString;
|
||||
@interface NSArray : NSObject <NSCopying, NSMutableCopying, NSCoding, NSFastEnumeration>
|
||||
- (NSUInteger)count;
|
||||
- (id)initWithObjects:(const id [])objects count:(NSUInteger)cnt;
|
||||
|
@ -1812,6 +1816,19 @@ void test_objc_arrays() {
|
|||
}
|
||||
}
|
||||
|
||||
void test_objc_integer_literals() {
|
||||
id value = [@1 retain]; // expected-warning {{leak}}
|
||||
[value description];
|
||||
}
|
||||
|
||||
void test_objc_boxed_expressions(int x, const char *y) {
|
||||
id value = [@(x) retain]; // expected-warning {{leak}}
|
||||
[value description];
|
||||
|
||||
value = [@(y) retain]; // expected-warning {{leak}}
|
||||
[value description];
|
||||
}
|
||||
|
||||
// Test NSLog doesn't escape tracked objects.
|
||||
void rdar11400885(int y)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue