[analyzer] RetainCountChecker: track ObjC boxed expression objects.

llvm-svn: 156699
This commit is contained in:
Jordy Rose 2012-05-12 05:10:43 +00:00
parent 4bd20c50eb
commit 6393f82b5b
4 changed files with 119 additions and 8 deletions

View File

@ -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();

View File

@ -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,

View File

@ -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}}
}

View File

@ -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)
{