diff options
author | Anna Zaks <ganna@apple.com> | 2012-08-22 21:19:56 +0000 |
---|---|---|
committer | Anna Zaks <ganna@apple.com> | 2012-08-22 21:19:56 +0000 |
commit | 266636128f87c167ff5a99e2e6e6136ab2495f08 (patch) | |
tree | 0463cf2ed870e2b27b61eaf4143395345fc7da54 | |
parent | 682ad162f144ff8e8e5303d30bc48b6a6a41d0d6 (diff) |
[analyzer] Add osx.cocoa.NonNilReturnValue checker.
The checker adds assumptions that the return values from the known APIs
are non-nil. Teach the checker about NSArray/NSMutableArray/NSOrderedSet
objectAtIndex, objectAtIndexedSubscript.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162398 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp | 45 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/Checkers.td | 4 | ||||
-rw-r--r-- | test/Analysis/test-objc-non-nil-return-value-checker.m | 50 |
3 files changed, 99 insertions, 0 deletions
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 955e79ae46..3dda6d04ca 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -716,6 +716,47 @@ void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, C.addTransition(State); } +namespace { +/// \class ObjCNonNilReturnValueChecker +/// \brief The checker restricts the return values of APIs known to never +/// return nil. +class ObjCNonNilReturnValueChecker + : public Checker<check::PostObjCMessage> { + mutable bool Initialized; + mutable Selector ObjectAtIndex; + mutable Selector ObjectAtIndexedSubscript; +public: + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; +}; +} + +void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) + const { + ProgramStateRef State = C.getState(); + + if (!Initialized) { + ASTContext &Ctx = C.getASTContext(); + ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx); + ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx); + } + + // Check the receiver type. + if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) { + FoundationClass Cl = findKnownClass(Interface); + if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) { + Selector Sel = M.getSelector(); + if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) { + // Go ahead and assume the value is non-nil. + SVal Val = State->getSVal(M.getOriginExpr(), C.getLocationContext()); + if (!isa<DefinedOrUnknownSVal>(Val)) + return; + State = State->assume(cast<DefinedOrUnknownSVal>(Val), true); + C.addTransition(State); + } + } + } +} //===----------------------------------------------------------------------===// // Check registration. @@ -744,3 +785,7 @@ void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { void ento::registerObjCLoopChecker(CheckerManager &mgr) { mgr.registerChecker<ObjCLoopChecker>(); } + +void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCNonNilReturnValueChecker>(); +} diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 8110bd0e18..41cc8090c9 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -397,6 +397,10 @@ def ObjCLoopChecker : Checker<"Loops">, HelpText<"Improved modeling of loops using Cocoa collection types">, DescFile<"BasicObjCFoundationChecks.cpp">; +def ObjCNonNilReturnValueChecker : Checker<"NonNilReturnValue">, + HelpText<"Model the APIs that are guaranteed to return a non-nil value">, + DescFile<"BasicObjCFoundationChecks.cpp">; + def NSErrorChecker : Checker<"NSError">, HelpText<"Check usage of NSError** parameters">, DescFile<"NSErrorChecker.cpp">; diff --git a/test/Analysis/test-objc-non-nil-return-value-checker.m b/test/Analysis/test-objc-non-nil-return-value-checker.m new file mode 100644 index 0000000000..8b1e6a5054 --- /dev/null +++ b/test/Analysis/test-objc-non-nil-return-value-checker.m @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=osx.cocoa.NonNilReturnValue,debug.ExprInspection -verify %s + +typedef unsigned int NSUInteger; +typedef signed char BOOL; + +@protocol NSObject - (BOOL)isEqual:(id)object; @end + +@interface NSObject <NSObject> {} ++(id)alloc; ++(id)new; +-(id)init; +-(id)autorelease; +-(id)copy; +- (Class)class; +-(id)retain; +@end + +@interface NSArray : NSObject +- (id)objectAtIndex:(unsigned long)index; +@end + +@interface NSArray (NSExtendedArray) +- (id)objectAtIndexedSubscript:(NSUInteger)idx; +@end + +@interface NSMutableArray : NSArray +- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject; +@end + +@interface NSOrderedSet : NSObject +@end +@interface NSOrderedSet (NSOrderedSetCreation) +- (id)objectAtIndexedSubscript:(NSUInteger)idx; +@end + +void clang_analyzer_eval(id); + +void assumeThatNSArrayObjectAtIndexIsNeverNull(NSArray *A, NSUInteger i) { + clang_analyzer_eval([A objectAtIndex: i]); // expected-warning {{TRUE}} + id subscriptObj = A[1]; + clang_analyzer_eval(subscriptObj); // expected-warning {{TRUE}} +} + +void assumeThatNSMutableArrayObjectAtIndexIsNeverNull(NSMutableArray *A, NSUInteger i) { + clang_analyzer_eval([A objectAtIndex: i]); // expected-warning {{TRUE}} +} + +void assumeThatNSArrayObjectAtIndexedSubscriptIsNeverNull(NSOrderedSet *A, NSUInteger i) { + clang_analyzer_eval(A[i]); // expected-warning {{TRUE}} +}
\ No newline at end of file |