[analyzer] Warn when nil receiver results in forming null reference

This also allows us to ensure IDC/return null suppression gets triggered in such cases.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@178686 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index 75c7df8..4965d22 100644
--- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -366,17 +366,23 @@
 
   if (!BT_msg_ret)
     BT_msg_ret.reset(
-      new BuiltinBug("Receiver in message expression is "
-                     "'nil' and returns a garbage value"));
+      new BuiltinBug("Receiver in message expression is 'nil'"));
 
   const ObjCMessageExpr *ME = msg.getOriginExpr();
 
+  QualType ResTy = msg.getResultType();
+
   SmallString<200> buf;
   llvm::raw_svector_ostream os(buf);
   os << "The receiver of message '" << ME->getSelector().getAsString()
-     << "' is nil and returns a value of type '";
-  msg.getResultType().print(os, C.getLangOpts());
-  os << "' that will be garbage";
+     << "' is nil";
+  if (ResTy->isReferenceType()) {
+    os << ", which results in forming a null reference";
+  } else {
+    os << " and returns a value of type '";
+    msg.getResultType().print(os, C.getLangOpts());
+    os << "' that will be garbage";
+  }
 
   BugReport *report = new BugReport(*BT_msg_ret, os.str(), N);
   report->addRange(ME->getReceiverRange());
@@ -419,13 +425,14 @@
     const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
     const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);
 
-    if (voidPtrSize < returnTypeSize &&
-        !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) &&
-          (Ctx.FloatTy == CanRetTy ||
-           Ctx.DoubleTy == CanRetTy ||
-           Ctx.LongDoubleTy == CanRetTy ||
-           Ctx.LongLongTy == CanRetTy ||
-           Ctx.UnsignedLongLongTy == CanRetTy))) {
+    if (CanRetTy.getTypePtr()->isReferenceType()||
+        (voidPtrSize < returnTypeSize &&
+         !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) &&
+           (Ctx.FloatTy == CanRetTy ||
+            Ctx.DoubleTy == CanRetTy ||
+            Ctx.LongDoubleTy == CanRetTy ||
+            Ctx.LongLongTy == CanRetTy ||
+            Ctx.UnsignedLongLongTy == CanRetTy)))) {
       if (ExplodedNode *N = C.generateSink(state, 0 , &Tag))
         emitNilReceiverBug(C, Msg, N);
       return;
diff --git a/test/Analysis/reference.mm b/test/Analysis/reference.mm
new file mode 100644
index 0000000..c5546aa
--- /dev/null
+++ b/test/Analysis/reference.mm
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -verify -Wno-null-dereference %s
+
+@interface Foo
+- (int &)ref;
+@end
+
+Foo *getFoo() { return 0; }
+
+void testNullPointerSuppression() {
+	getFoo().ref = 1;
+}
+
+void testPositiveNullReference() {
+  Foo *x = 0;
+	x.ref = 1; // expected-warning {{The receiver of message 'ref' is nil, which results in forming a null reference}}
+}
+