diff options
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp | 45 | ||||
-rw-r--r-- | clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 13 | ||||
-rw-r--r-- | clang/test/Analysis/retain-release-path-notes.m | 48 | ||||
-rw-r--r-- | clang/test/Analysis/retain-release.m | 21 |
4 files changed, 119 insertions, 8 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index aa392b08608..aab5552f666 100644 --- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -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(); diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index 79d74797b3a..97a8cefd13a 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -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, diff --git a/clang/test/Analysis/retain-release-path-notes.m b/clang/test/Analysis/retain-release-path-notes.m index c3f5fcda444..be6336b59f7 100644 --- a/clang/test/Analysis/retain-release-path-notes.m +++ b/clang/test/Analysis/retain-release-path-notes.m @@ -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}} +} diff --git a/clang/test/Analysis/retain-release.m b/clang/test/Analysis/retain-release.m index a27fdac27fb..1274b197bf3 100644 --- a/clang/test/Analysis/retain-release.m +++ b/clang/test/Analysis/retain-release.m @@ -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) { |