From 00fc11fc8b3f0bb5c8f1de337df149b0733d986e Mon Sep 17 00:00:00 2001
From: Douglas Gregor <dgregor@apple.com>
Date: Fri, 19 Jun 2015 23:17:51 +0000
Subject: [PATCH] CF_RETURNS_[NOT_]RETAINED on a param makes the inner pointer
 __nullable.

That is,

  void cf2(CFTypeRef * __nullable p CF_RETURNS_NOT_RETAINED);

is equivalent to

  void cf2(CFTypeRef __nullable * __nullable p CF_RETURNS_NOT_RETAINED);

More rdar://problem/18742441

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@240186 91177308-0d34-0410-b5e6-96231b3b80d8
---
 lib/Sema/SemaType.cpp                         | 42 +++++++++++++++++--
 test/Analysis/retain-release.m                |  7 ++++
 .../Inputs/nullability-consistency-8.h        | 16 +++++++
 3 files changed, 62 insertions(+), 3 deletions(-)

diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index d3b773e5350..d950d329b4e 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -2614,6 +2614,8 @@ namespace {
     SingleLevelPointer,
     // Multi-level pointer (of any pointer kind).
     MultiLevelPointer,
+    // CFFooRef*
+    MaybePointerToCFRef,
     // CFErrorRef*
     CFErrorRefPointer,
     // NSError**
@@ -2754,6 +2756,9 @@ static PointerDeclaratorKind classifyPointerDeclarator(Sema &S,
   case 1:
     return PointerDeclaratorKind::SingleLevelPointer;
 
+  case 2:
+    return PointerDeclaratorKind::MaybePointerToCFRef;
+
   default:
     return PointerDeclaratorKind::MultiLevelPointer;
   }
@@ -2894,6 +2899,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
   // Determine whether we should infer __nonnull on pointer types.
   Optional<NullabilityKind> inferNullability;
   bool inferNullabilityCS = false;
+  bool inferNullabilityInnerOnly = false;
+  bool inferNullabilityInnerOnlyComplete = false;
 
   // Are we in an assume-nonnull region?
   bool inAssumeNonNullRegion = false;
@@ -3007,6 +3014,31 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
         if (isFunctionOrMethod && inAssumeNonNullRegion)
           inferNullability = NullabilityKind::Nullable;
         break;
+
+      case PointerDeclaratorKind::MaybePointerToCFRef:
+        if (isFunctionOrMethod) {
+          // On pointer-to-pointer parameters marked cf_returns_retained or
+          // cf_returns_not_retained, if the outer pointer is explicit then
+          // infer the inner pointer as __nullable.
+          auto hasCFReturnsAttr = [](const AttributeList *NextAttr) -> bool {
+            while (NextAttr) {
+              if (NextAttr->getKind() == AttributeList::AT_CFReturnsRetained ||
+                  NextAttr->getKind() == AttributeList::AT_CFReturnsNotRetained)
+                return true;
+              NextAttr = NextAttr->getNext();
+            }
+            return false;
+          };
+          if (const auto *InnermostChunk = D.getInnermostNonParenChunk()) {
+            if (hasCFReturnsAttr(D.getAttributes()) ||
+                hasCFReturnsAttr(InnermostChunk->getAttrs()) ||
+                hasCFReturnsAttr(D.getDeclSpec().getAttributes().getList())) {
+              inferNullability = NullabilityKind::Nullable;
+              inferNullabilityInnerOnly = true;
+            }
+          }
+        }
+        break;
       }
       break;
 
@@ -3047,9 +3079,10 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
       return nullptr;
 
     // If we're supposed to infer nullability, do so now.
-    if (inferNullability) {
-      auto syntax = inferNullabilityCS ? AttributeList::AS_ContextSensitiveKeyword
-                                       : AttributeList::AS_Keyword;
+    if (inferNullability && !inferNullabilityInnerOnlyComplete) {
+      AttributeList::Syntax syntax
+        = inferNullabilityCS ? AttributeList::AS_ContextSensitiveKeyword
+                             : AttributeList::AS_Keyword;
       AttributeList *nullabilityAttr = state.getDeclarator().getAttributePool()
                                          .create(
                                            S.getNullabilityKeyword(
@@ -3059,6 +3092,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
                                            nullptr, 0, syntax);
 
       spliceAttrIntoList(*nullabilityAttr, attrs);
+
+      if (inferNullabilityInnerOnly)
+        inferNullabilityInnerOnlyComplete = true;
       return nullabilityAttr;
     }
 
diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m
index e3ad4090373..1dbcda507c1 100644
--- a/test/Analysis/retain-release.m
+++ b/test/Analysis/retain-release.m
@@ -2164,6 +2164,13 @@ void testCFReturnsNotRetained() {
   CFRelease(obj); // // expected-warning {{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
 }
 
+void testCFReturnsNotRetainedAnnotated() {
+  extern void getViaParam2(CFTypeRef * __nonnull CF_RETURNS_NOT_RETAINED outObj);
+  CFTypeRef obj;
+  getViaParam2(&obj);
+  CFRelease(obj); // // expected-warning {{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}}
+}
+
 void testCFReturnsRetained() {
   extern int copyViaParam(CFTypeRef * CF_RETURNS_RETAINED outObj);
   CFTypeRef obj;
diff --git a/test/SemaObjCXX/Inputs/nullability-consistency-8.h b/test/SemaObjCXX/Inputs/nullability-consistency-8.h
index e7cf4b3ee8a..890bb4db546 100644
--- a/test/SemaObjCXX/Inputs/nullability-consistency-8.h
+++ b/test/SemaObjCXX/Inputs/nullability-consistency-8.h
@@ -9,3 +9,19 @@ void func2(mynonnull i);
 
 void func3(int *); // expected-warning{{pointer is missing a nullability type specifier}}
 
+#define CF_RETURNS_NOT_RETAINED __attribute__((cf_returns_not_retained))
+typedef void *CFTypeRef;
+void cf1(CFTypeRef * p CF_RETURNS_NOT_RETAINED); // expected-warning {{pointer is missing a nullability type specifier}}
+
+void cf2(CFTypeRef * __nullable p CF_RETURNS_NOT_RETAINED);
+void cf3(CFTypeRef * __nonnull p CF_RETURNS_NOT_RETAINED);
+
+void cf4(CFTypeRef __nullable * __nullable p CF_RETURNS_NOT_RETAINED);
+void cf5(CFTypeRef __nonnull * __nullable p CF_RETURNS_NOT_RETAINED);
+
+void cf6(CFTypeRef * __nullable CF_RETURNS_NOT_RETAINED p);
+void cf7(CF_RETURNS_NOT_RETAINED CFTypeRef * __nonnull p);
+
+typedef CFTypeRef __nullable *CFTypeRefPtr;
+void cfp1(CFTypeRefPtr p CF_RETURNS_NOT_RETAINED); // expected-warning {{pointer is missing a nullability type specifier}}
+void cfp2(CFTypeRefPtr __nonnull p CF_RETURNS_NOT_RETAINED);
-- 
GitLab