diff --git a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 955e79ae4661..3dda6d04ca73 100644 --- a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/clang/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 { + 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(Val)) + return; + State = State->assume(cast(Val), true); + C.addTransition(State); + } + } + } +} //===----------------------------------------------------------------------===// // Check registration. @@ -744,3 +785,7 @@ void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { void ento::registerObjCLoopChecker(CheckerManager &mgr) { mgr.registerChecker(); } + +void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} diff --git a/clang/lib/StaticAnalyzer/Checkers/Checkers.td b/clang/lib/StaticAnalyzer/Checkers/Checkers.td index 8110bd0e18c2..41cc8090c9df 100644 --- a/clang/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/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/clang/test/Analysis/test-objc-non-nil-return-value-checker.m b/clang/test/Analysis/test-objc-non-nil-return-value-checker.m new file mode 100644 index 000000000000..8b1e6a5054a5 --- /dev/null +++ b/clang/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 {} ++(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