[clang] Add the support for adoptCFNullable/adoptNSNullable (PR #194539)

Ryosuke Niwa via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 27 21:56:35 PDT 2026


https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/194539

These are two new "adopt" functions to be introduced in WebKit.

>From ac35084dae22ca88b5bb5388ec8f2a5d24d7d562 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Mon, 27 Apr 2026 21:55:35 -0700
Subject: [PATCH] Add the support for adoptCFNullable/adoptNSNullable

These are two new "adopt" functions to be introduced in WebKit.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp     |  3 ++-
 .../WebKit/RetainPtrCtorAdoptChecker.cpp      |  4 +++-
 .../Checkers/WebKit/objc-mock-types.h         | 20 +++++++++++++++++++
 .../WebKit/retain-ptr-ctor-adopt-use-arc.mm   |  6 ++++++
 .../WebKit/retain-ptr-ctor-adopt-use.mm       |  6 ++++++
 .../Checkers/WebKit/unretained-call-args.mm   |  2 ++
 6 files changed, 39 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d239ed4c8a5ae..b8c8359c22773 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -175,7 +175,8 @@ bool isCtorOfCheckedPtr(const clang::FunctionDecl *F) {
 bool isCtorOfRetainPtrOrOSPtr(const clang::FunctionDecl *F) {
   const std::string &FunctionName = safeGetName(F);
   return FunctionName == "RetainPtr" || FunctionName == "adoptNS" ||
-         FunctionName == "adoptCF" || FunctionName == "retainPtr" ||
+         FunctionName == "adoptNSNullable" || FunctionName == "adoptCF" ||
+         FunctionName == "adoptCFNullable" || FunctionName == "retainPtr" ||
          FunctionName == "RetainPtrArc" || FunctionName == "adoptNSArc" ||
          FunctionName == "adoptOSObject" || FunctionName == "adoptOSObjectArc";
 }
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
index 2af9067f8f808..afa9fc7b1c4a4 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
@@ -122,11 +122,13 @@ class RetainPtrCtorAdoptChecker
 
   bool isAdoptFnName(const std::string &Name) const {
     return isAdoptNS(Name) || Name == "adoptCF" || Name == "adoptCFArc" ||
+           Name == "adoptCFNullable" || Name == "adoptCFNullableArc" ||
            Name == "adoptOSObject" || Name == "adoptOSObjectArc";
   }
 
   bool isAdoptNS(const std::string &Name) const {
-    return Name == "adoptNS" || Name == "adoptNSArc";
+    return Name == "adoptNS" || Name == "adoptNSArc" ||
+           Name == "adoptNSNullable" || Name == "adoptNSNullableArc";
   }
 
   void visitCallExpr(const CallExpr *CE, const Decl *DeclWithIssue) const {
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index 124821a8ded55..503d11bea3089 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -265,7 +265,9 @@ void WTFCrash(void);
 
 template<typename T> class RetainPtr;
 template<typename T> RetainPtr<T> adoptNS(T*);
+template<typename T> RetainPtr<T> adoptNSNullable(T*);
 template<typename T> RetainPtr<T> adoptCF(T);
+template<typename T> RetainPtr<T> adoptCFNullable(T);
 
 template <typename T, typename S> T *downcast(S *t) { return static_cast<T*>(t); }
 
@@ -367,7 +369,9 @@ template <typename T> struct RetainPtr {
   CFTypeRef toCFTypeRef(const void* ptr) { return (CFTypeRef)ptr; }
 
   template <typename U> friend RetainPtr<U> adoptNS(U*);
+  template <typename U> friend RetainPtr<U> adoptNSNullable(U*);
   template <typename U> friend RetainPtr<U> adoptCF(U);
+  template <typename U> friend RetainPtr<U> adoptCFNullable(U);
 
   enum AdoptTag { Adopt };
   RetainPtr(PtrType t, AdoptTag) : t(t) { }
@@ -395,11 +399,25 @@ RetainPtr<T> adoptNS(T* t) {
 #endif
 }
 
+template <typename T>
+RetainPtr<T> adoptNSNullable(T* t) {
+#if __has_feature(objc_arc)
+  return t;
+#else
+  return RetainPtr<T>(t, RetainPtr<T>::Adopt);
+#endif
+}
+
 template <typename T>
 RetainPtr<T> adoptCF(T t) {
   return RetainPtr<T>(t, RetainPtr<T>::Adopt);
 }
 
+template <typename T>
+RetainPtr<T> adoptCFNullable(T t) {
+  return RetainPtr<T>(t, RetainPtr<T>::Adopt);
+}
+
 template<typename T> inline RetainPtr<T> retainPtr(T ptr)
 {
   return ptr;
@@ -681,7 +699,9 @@ WTF_DECLARE_CF_MUTABLE_TYPE_TRAIT(CFDictionary, CFMutableDictionary);
 
 using WTF::RetainPtr;
 using WTF::adoptNS;
+using WTF::adoptNSNullable;
 using WTF::adoptCF;
+using WTF::adoptCFNullable;
 using WTF::retainPtr;
 using WTF::OSObjectPtr;
 using WTF::adoptOSObject;
diff --git a/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use-arc.mm b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use-arc.mm
index 79ea95b088167..135fe651a9f16 100644
--- a/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use-arc.mm
+++ b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use-arc.mm
@@ -12,9 +12,11 @@ void basic_correct() {
   auto ns4 = adoptNS([ns3 mutableCopy]);
   auto ns5 = adoptNS([ns3 copyWithValue:3]);
   auto ns6 = retainPtr([ns3 next]);
+  auto ns7 = adoptNSNullable([[SomeObj alloc] init]);
   CFMutableArrayRef cf1 = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 10));
   auto cf2 = adoptCF(SecTaskCreateFromSelf(kCFAllocatorDefault));
   auto cf3 = adoptCF(checked_cf_cast<CFArrayRef>(CFCopyArray(cf1)));
+  auto cf4 = adoptCFNullable(CFArrayCreateMutable(kCFAllocatorDefault, 10));
 }
 
 CFMutableArrayRef provide_cf();
@@ -24,6 +26,8 @@ void basic_wrong() {
   // expected-warning at -1{{Incorrect use of RetainPtr constructor. The argument is +1 and results in a memory leak when ARC is disabled [alpha.webkit.RetainPtrCtorAdoptChecker]}}
   auto ns2 = adoptNS([ns1.get() next]);
   // expected-warning at -1{{Incorrect use of adoptNS. The argument is +0 and results in an use-after-free when ARC is disabled [alpha.webkit.RetainPtrCtorAdoptChecker]}}
+  auto ns3 = adoptNSNullable([ns1.get() next]);
+  // expected-warning at -1{{Incorrect use of adoptNSNullable. The argument is +0 and results in an use-after-free when ARC is disabled [alpha.webkit.RetainPtrCtorAdoptChecker]}}
   RetainPtr<CFMutableArrayRef> cf1 = CFArrayCreateMutable(kCFAllocatorDefault, 10);
   // expected-warning at -1{{Incorrect use of RetainPtr constructor. The argument is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
   RetainPtr<CFMutableArrayRef> cf2 = adoptCF(provide_cf());
@@ -32,6 +36,8 @@ void basic_wrong() {
   // expected-warning at -1{{Incorrect use of RetainPtr constructor. The argument is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
   CFCopyArray(cf1);
   // expected-warning at -1{{The return value is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
+  RetainPtr<CFMutableArrayRef> cf4 = adoptCFNullable(provide_cf());
+  // expected-warning at -1{{Incorrect use of adoptCFNullable. The argument is +0 and results in an use-after-free when ARC is disabled [alpha.webkit.RetainPtrCtorAdoptChecker]}}
 }
 
 void basic_correct_arc() {
diff --git a/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm
index 20f951b27a149..c337752d6bd21 100644
--- a/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm
+++ b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm
@@ -15,9 +15,11 @@ void basic_correct() {
   auto ns6 = retainPtr([ns3 next]);
   auto ns7 = retainPtr((SomeObj *)0);
   auto ns8 = adoptNS(nil);
+  auto ns9 = adoptNSNullable([[SomeObj alloc] init]);
   CFMutableArrayRef cf1 = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 10));
   auto cf2 = adoptCF(SecTaskCreateFromSelf(kCFAllocatorDefault));
   auto cf3 = adoptCF(checked_cf_cast<CFArrayRef>(CFCopyArray(cf1)));
+  auto cf4 = adoptCFNullable(CFArrayCreateMutable(kCFAllocatorDefault, 10));
   CreateCopy();
 }
 
@@ -28,6 +30,8 @@ void basic_wrong() {
   // expected-warning at -1{{Incorrect use of RetainPtr constructor. The argument is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
   auto ns2 = adoptNS([ns1.get() next]);
   // expected-warning at -1{{Incorrect use of adoptNS. The argument is +0 and results in an use-after-free [alpha.webkit.RetainPtrCtorAdoptChecker]}}
+  auto ns3 = adoptNSNullable([ns1.get() next]);
+  // expected-warning at -1{{Incorrect use of adoptNSNullable. The argument is +0 and results in an use-after-free [alpha.webkit.RetainPtrCtorAdoptChecker]}}
   RetainPtr<CFMutableArrayRef> cf1 = CFArrayCreateMutable(kCFAllocatorDefault, 10);
   // expected-warning at -1{{Incorrect use of RetainPtr constructor. The argument is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
   RetainPtr<CFMutableArrayRef> cf2 = adoptCF(provide_cf());
@@ -36,6 +40,8 @@ void basic_wrong() {
   // expected-warning at -1{{Incorrect use of RetainPtr constructor. The argument is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
   CFCopyArray(cf1);
   // expected-warning at -1{{The return value is +1 and results in a memory leak [alpha.webkit.RetainPtrCtorAdoptChecker]}}
+  RetainPtr<CFMutableArrayRef> cf4 = adoptCFNullable(provide_cf());
+  // expected-warning at -1{{Incorrect use of adoptCFNullable. The argument is +0 and results in an use-after-free [alpha.webkit.RetainPtrCtorAdoptChecker]}}
 }
 
 void basic_correct_arc() {
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
index cfc214fae33e5..add9144744db4 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -385,7 +385,9 @@ void os_ptr() {
 namespace call_with_adopt_ref {
   void foo() {
     [adoptNS(provide()).get() doWork];
+    [adoptNSNullable(provide()).get() doWork];
     CFArrayAppendValue(adoptCF(provide_cf()).get(), nullptr);
+    CFArrayAppendValue(adoptCFNullable(provide_cf()).get(), nullptr);
     consume_dispatch(adoptOSObject(provide_dispatch()).get());
   }
 }



More information about the cfe-commits mailing list