[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
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index 955e79a..3dda6d0 100644
--- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -716,6 +716,47 @@
   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::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 8110bd0..41cc809 100644
--- a/lib/StaticAnalyzer/Checkers/Checkers.td
+++ b/lib/StaticAnalyzer/Checkers/Checkers.td
@@ -397,6 +397,10 @@
   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 0000000..8b1e6a5
--- /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