Refactor NilReceiverStructRet and NilReceiverLargerThanVoidPtrRet into 
CallAndMessageChecker.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89745 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Analysis/CallAndMessageChecker.cpp b/lib/Analysis/CallAndMessageChecker.cpp
index 8386d03..ebc3a4f 100644
--- a/lib/Analysis/CallAndMessageChecker.cpp
+++ b/lib/Analysis/CallAndMessageChecker.cpp
@@ -14,6 +14,7 @@
 
 #include "clang/Analysis/PathSensitive/CheckerVisitor.h"
 #include "clang/Analysis/PathSensitive/BugReporter.h"
+#include "clang/AST/ParentMap.h"
 #include "GRExprEngineInternalChecks.h"
 
 using namespace clang;
@@ -26,10 +27,13 @@
   BugType *BT_call_arg;
   BugType *BT_msg_undef;
   BugType *BT_msg_arg;
+  BugType *BT_struct_ret;
+  BugType *BT_void_ptr;
 public:
   CallAndMessageChecker() :
     BT_call_null(0), BT_call_undef(0), BT_call_arg(0),
-    BT_msg_undef(0), BT_msg_arg(0) {}
+    BT_msg_undef(0), BT_msg_arg(0), BT_struct_ret(0), BT_void_ptr(0) {}
+
   static void *getTag() {
     static int x = 0;
     return &x;
@@ -119,8 +123,8 @@
     }
 
   // Check for any arguments that are uninitialized/undefined.
-  for (ObjCMessageExpr::const_arg_iterator I = ME->arg_begin(), E = ME->arg_end();
-       I != E; ++I) {
+  for (ObjCMessageExpr::const_arg_iterator I = ME->arg_begin(),
+         E = ME->arg_end(); I != E; ++I) {
     if (state->getSVal(*I).isUndef()) {
       if (ExplodedNode *N = C.GenerateSink()) {
         if (!BT_msg_arg)
@@ -137,4 +141,112 @@
       }
     }
   }
+
+  // Check if the receiver was nil and then return value a struct.
+  if (const Expr *Receiver = ME->getReceiver()) {
+    SVal L_untested = state->getSVal(Receiver);
+    // Assume that the receiver is not NULL.
+    DefinedOrUnknownSVal L = cast<DefinedOrUnknownSVal>(L_untested);
+    const GRState *StNotNull = state->Assume(L, true);
+
+    // Assume that the receiver is NULL.
+    const GRState *StNull = state->Assume(L, false);
+
+    if (StNull) {
+      QualType RetTy = ME->getType();
+      if (RetTy->isRecordType()) {
+        if (C.getPredecessor()->getParentMap().isConsumedExpr(ME)) {
+          // The [0 ...] expressions will return garbage.  Flag either an
+          // explicit or implicit error.  Because of the structure of this
+          // function we currently do not bifurfacte the state graph at
+          // this point.
+          // FIXME: We should bifurcate and fill the returned struct with
+          //  garbage.
+          if (ExplodedNode* N = C.GenerateSink(StNull)) {
+            if (!StNotNull) {
+              if (!BT_struct_ret) {
+                std::string sbuf;
+                llvm::raw_string_ostream os(sbuf);
+                os << "The receiver in the message expression is 'nil' and "
+                  "results in the returned value (of type '"
+                   << ME->getType().getAsString()
+                   << "') to be garbage or otherwise undefined";
+                BT_struct_ret = new BuiltinBug(os.str().c_str());
+              }
+              
+              EnhancedBugReport *R = new EnhancedBugReport(*BT_struct_ret, 
+                                                   BT_struct_ret->getName(), N);
+              R->addRange(Receiver->getSourceRange());
+              R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, 
+                                   Receiver);
+              C.EmitReport(R);
+              return;
+            }
+            else
+              // Do not report implicit bug.
+              return;
+          }
+        }
+      } else {
+        ASTContext &Ctx = C.getASTContext();
+        if (RetTy != Ctx.VoidTy) {
+          if (C.getPredecessor()->getParentMap().isConsumedExpr(ME)) {
+            // sizeof(void *)
+            const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
+            // sizeof(return type)
+            const uint64_t returnTypeSize = Ctx.getTypeSize(ME->getType());
+            
+            if (voidPtrSize < returnTypeSize) {
+              if (ExplodedNode* N = C.GenerateSink(StNull)) {
+                if (!StNotNull) {
+                  if (!BT_struct_ret) {
+                    std::string sbuf;
+                    llvm::raw_string_ostream os(sbuf);
+                    os << "The receiver in the message expression is 'nil' and "
+                      "results in the returned value (of type '"
+                       << ME->getType().getAsString()
+                       << "' and of size "
+                       << returnTypeSize / 8
+                       << " bytes) to be garbage or otherwise undefined";
+                    BT_void_ptr = new BuiltinBug(os.str().c_str());
+                  }
+              
+                  EnhancedBugReport *R = new EnhancedBugReport(*BT_void_ptr, 
+                                                     BT_void_ptr->getName(), N);
+                  R->addRange(Receiver->getSourceRange());
+                  R->addVisitorCreator(
+                          bugreporter::registerTrackNullOrUndefValue, Receiver);
+                  C.EmitReport(R);
+                  return;
+                } else
+                  // Do not report implicit bug.
+                  return;
+              }
+            }
+            else if (!StNotNull) {
+              // Handle the safe cases where the return value is 0 if the
+              // receiver is nil.
+              //
+              // FIXME: For now take the conservative approach that we only
+              // return null values if we *know* that the receiver is nil.
+              // This is because we can have surprises like:
+              //
+              //   ... = [[NSScreens screens] objectAtIndex:0];
+              //
+              // What can happen is that [... screens] could return nil, but
+              // it most likely isn't nil.  We should assume the semantics
+              // of this case unless we have *a lot* more knowledge.
+              //
+              SVal V = C.getValueManager().makeZeroVal(ME->getType());
+              C.GenerateNode(StNull->BindExpr(ME, V));
+              return;
+            }
+          }
+        }
+      }
+    }
+    // Do not propagate null state.
+    if (StNotNull)
+      C.GenerateNode(StNotNull);
+  }
 }