[clang] [WebKit checkers] Add the support for OSObjectPtr (PR #159484)

via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 17 17:02:23 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Ryosuke Niwa (rniwa)

<details>
<summary>Changes</summary>

Add the support for OSObjectPtr, which behaves like RetainPtr.

---

Patch is 51.33 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/159484.diff


15 Files Affected:

- (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp (+4-2) 
- (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp (+6-5) 
- (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp (+2-1) 
- (modified) clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp (-14) 
- (modified) clang/test/Analysis/Checkers/WebKit/mock-types.h (+10) 
- (modified) clang/test/Analysis/Checkers/WebKit/objc-mock-types.h (+156) 
- (modified) clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp (-3) 
- (modified) clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm (+12-3) 
- (modified) clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm (+81-14) 
- (modified) clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-arc.mm (+46-3) 
- (modified) clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm (+64-3) 
- (modified) clang/test/Analysis/Checkers/WebKit/unretained-local-vars-arc.mm (+16-3) 
- (modified) clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm (+120-8) 
- (modified) clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm (+18-6) 
- (modified) clang/test/Analysis/Checkers/WebKit/unretained-members.mm (+35-13) 


``````````diff
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 90b2343b4be77..44a0cc52662d8 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -130,7 +130,8 @@ bool isRefType(const std::string &Name) {
 }
 
 bool isRetainPtr(const std::string &Name) {
-  return Name == "RetainPtr" || Name == "RetainPtrArc";
+  return Name == "RetainPtr" || Name == "RetainPtrArc" ||
+         Name == "OSObjectPtr" || Name == "OSObjectPtrArc";
 }
 
 bool isCheckedPtr(const std::string &Name) {
@@ -170,7 +171,8 @@ bool isCtorOfRetainPtr(const clang::FunctionDecl *F) {
   const std::string &FunctionName = safeGetName(F);
   return FunctionName == "RetainPtr" || FunctionName == "adoptNS" ||
          FunctionName == "adoptCF" || FunctionName == "retainPtr" ||
-         FunctionName == "RetainPtrArc" || FunctionName == "adoptNSArc";
+         FunctionName == "RetainPtrArc" || FunctionName == "adoptNSArc" ||
+         FunctionName == "adoptOSObject" || FunctionName == "adoptOSObjectArc";
 }
 
 bool isCtorOfSafePtr(const clang::FunctionDecl *F) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
index 8faf6a219450a..a776aa575fc94 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp
@@ -119,10 +119,11 @@ class RawPtrRefMemberChecker
     auto *Desugared = PointeeType->getUnqualifiedDesugaredType();
     if (!Desugared)
       return nullptr;
-    auto *ObjCType = dyn_cast<ObjCInterfaceType>(Desugared);
-    if (!ObjCType)
-      return nullptr;
-    return ObjCType->getDecl();
+    if (auto *ObjCType = dyn_cast<ObjCInterfaceType>(Desugared))
+      return ObjCType->getDecl();
+    if (auto* ObjCType = dyn_cast<ObjCObjectType>(Desugared))
+      return ObjCType->getInterface();
+    return nullptr;
   }
 
   void visitObjCDecl(const ObjCContainerDecl *CD) const {
@@ -369,7 +370,7 @@ class NoUnretainedMemberChecker final : public RawPtrRefMemberChecker {
   const char *typeName() const final { return "retainable type"; }
 
   const char *invariant() const final {
-    return "member variables must be a RetainPtr";
+    return "member variables must be a RetainPtr or OSObjectPtr";
   }
 
   PrintDeclKind printPointer(llvm::raw_svector_ostream &Os,
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
index 5c1b2d7cce45d..572cbc6bb40a9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
@@ -121,7 +121,8 @@ class RetainPtrCtorAdoptChecker
   }
 
   bool isAdoptFnName(const std::string &Name) const {
-    return isAdoptNS(Name) || Name == "adoptCF" || Name == "adoptCFArc";
+    return isAdoptNS(Name) || Name == "adoptCF" || Name == "adoptCFArc" ||
+           Name == "adoptOSObject" || Name == "adoptOSObjectArc";
   }
 
   bool isAdoptNS(const std::string &Name) const {
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp
index b2fb042e7deff..e9f01c8ed7540 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp
@@ -2,20 +2,6 @@
 
 #include "mock-types.h"
 
-namespace std {
-
-template <typename T> struct remove_reference {
-  typedef T type;
-};
-
-template <typename T> struct remove_reference<T&> {
-  typedef T type;
-};
-
-template<typename T> typename remove_reference<T>::type&& move(T&& t);
-
-} // namespace std
-
 RefCountableAndCheckable* makeObj();
 CheckedRef<RefCountableAndCheckable> makeObjChecked();
 void someFunction(RefCountableAndCheckable*);
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index a03d31870ee0d..08b8ad0675946 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -3,6 +3,16 @@
 
 namespace std {
 
+template <typename T> struct remove_reference {
+typedef T type;
+};
+
+template <typename T> struct remove_reference<T&> {
+typedef T type;
+};
+
+template<typename T> typename remove_reference<T>::type&& move(T&& t);
+
 template <typename T>
 class unique_ptr {
 private:
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index 09b303961fd6a..5bdd14c6afd1f 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -157,6 +157,49 @@ __attribute__((objc_root_class))
 - (void)doMoreWork:(OtherObj *)other;
 @end
 
+ at protocol OS_dispatch_queue
+ at end
+
+typedef NSObject<OS_dispatch_queue> *dispatch_queue_t;
+
+ at protocol OS_dispatch_queue_attr
+ at end
+
+typedef NSObject<OS_dispatch_queue_attr> *dispatch_queue_attr_t;
+
+NS_RETURNS_RETAINED dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
+const char *dispatch_queue_get_label(dispatch_queue_t queue);
+
+namespace std {
+
+template <typename T> struct remove_reference {
+typedef T type;
+};
+
+template <typename T> struct remove_reference<T&> {
+typedef T type;
+};
+
+template<typename T> typename remove_reference<T>::type&& move(T&& t);
+
+template <typename StorageType>
+void swap(StorageType& a, StorageType& b)
+{
+  StorageType temp = static_cast<StorageType&&>(a);
+  a = static_cast<StorageType&&>(b);
+  b = static_cast<StorageType&&>(temp);
+}
+
+template <typename StorageType, typename ValueType>
+StorageType exchange(StorageType& obj, ValueType& value)
+{
+  StorageType returnValue = static_cast<StorageType&&>(obj);
+  obj = static_cast<StorageType&&>(value);
+  return returnValue;
+}
+
+}
+
 namespace WTF {
 
 void WTFCrash(void);
@@ -293,6 +336,117 @@ template<typename T> inline RetainPtr<T> retainPtr(T* ptr)
   return ptr;
 }
 
+template<typename> class OSObjectPtr;
+template<typename T> OSObjectPtr<T> adoptOSObject(T);
+
+template<typename T> static inline void retainOSObject(T ptr)
+{
+#if !__has_feature(objc_arc)
+    [ptr retain];
+#endif
+}
+
+template<typename T> static inline void releaseOSObject(T ptr)
+{
+#if !__has_feature(objc_arc)
+    [ptr release];
+#endif
+}
+
+template<typename T> class OSObjectPtr {
+public:
+    OSObjectPtr()
+        : m_ptr(nullptr)
+    {
+    }
+
+    ~OSObjectPtr()
+    {
+        if (m_ptr)
+            releaseOSObject(m_ptr);
+    }
+
+    T get() const { return m_ptr; }
+
+    explicit operator bool() const { return m_ptr; }
+    bool operator!() const { return !m_ptr; }
+
+    OSObjectPtr(const OSObjectPtr& other)
+        : m_ptr(other.m_ptr)
+    {
+        if (m_ptr)
+            retainOSObject(m_ptr);
+    }
+
+    OSObjectPtr(OSObjectPtr&& other)
+        : m_ptr(std::move(other.m_ptr))
+    {
+        other.m_ptr = nullptr;
+    }
+
+    OSObjectPtr(T ptr)
+        : m_ptr(std::move(ptr))
+    {
+        if (m_ptr)
+            retainOSObject(m_ptr);
+    }
+
+    OSObjectPtr& operator=(const OSObjectPtr& other)
+    {
+        OSObjectPtr ptr = other;
+        swap(ptr);
+        return *this;
+    }
+
+    OSObjectPtr& operator=(OSObjectPtr&& other)
+    {
+        OSObjectPtr ptr = std::move(other);
+        swap(ptr);
+        return *this;
+    }
+
+    OSObjectPtr& operator=(decltype(nullptr))
+    {
+        if (m_ptr)
+            releaseOSObject(m_ptr);
+        m_ptr = nullptr;
+        return *this;
+    }
+
+    OSObjectPtr& operator=(T other)
+    {
+        OSObjectPtr ptr = std::move(other);
+        swap(ptr);
+        return *this;
+    }
+
+    void swap(OSObjectPtr& other)
+    {
+        std::swap(m_ptr, other.m_ptr);
+    }
+
+    T leakRef()
+    {
+        return std::exchange(m_ptr, nullptr);
+    }
+
+    friend OSObjectPtr adoptOSObject<T>(T);
+
+private:
+    struct AdoptOSObject { };
+    OSObjectPtr(AdoptOSObject, T ptr)
+        : m_ptr(std::move(ptr))
+    {
+    }
+
+    T m_ptr;
+};
+
+template<typename T> inline OSObjectPtr<T> adoptOSObject(T ptr)
+{
+    return OSObjectPtr<T> { typename OSObjectPtr<T>::AdoptOSObject { }, std::move(ptr) };
+}
+
 inline NSObject *bridge_cast(CFTypeRef object)
 {
   return (__bridge NSObject *)object;
@@ -455,6 +609,8 @@ using WTF::RetainPtr;
 using WTF::adoptNS;
 using WTF::adoptCF;
 using WTF::retainPtr;
+using WTF::OSObjectPtr;
+using WTF::adoptOSObject;
 using WTF::downcast;
 using WTF::bridge_cast;
 using WTF::bridge_id_cast;
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
index 2c6ccb55e2ce8..a9cd77c066f6f 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -74,9 +74,6 @@ T* addressof(T& arg);
 template<typename T>
 T&& forward(T& arg);
 
-template<typename T>
-T&& move( T&& t );
-
 template<typename ToType, typename FromType>
 ToType bit_cast(FromType from);
 
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
index fa866258a2f6d..2763d8a188d8a 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args-arc.mm
@@ -4,6 +4,7 @@
 
 SomeObj *provide();
 CFMutableArrayRef provide_cf();
+dispatch_queue_t provide_os();
 void someFunction();
 CGImageRef provideImage();
 NSString *stringForImage(CGImageRef);
@@ -14,6 +15,7 @@ void foo() {
   [provide() doWork];
   CFArrayAppendValue(provide_cf(), nullptr);
   // expected-warning at -1{{Call argument for parameter 'theArray' is unretained and unsafe [alpha.webkit.UnretainedCallArgsChecker]}}
+  dispatch_queue_get_label(provide_os());
 }
 
 } // namespace raw_ptr
@@ -22,15 +24,17 @@ void foo() {
 
 extern NSString * const SomeConstant;
 extern CFDictionaryRef const SomeDictionary;
-void doWork(NSString *str, CFDictionaryRef dict);
+extern dispatch_queue_t const SomeDispatch;
+void doWork(NSString *str, CFDictionaryRef dict, dispatch_queue_t dispatch);
 void use_const_global() {
-  doWork(SomeConstant, SomeDictionary);
+  doWork(SomeConstant, SomeDictionary, SomeDispatch);
 }
 
 NSString *provide_str();
 CFDictionaryRef provide_dict();
+dispatch_queue_t provide_dispatch();
 void use_const_local() {
-  doWork(provide_str(), provide_dict());
+  doWork(provide_str(), provide_dict(), provide_dispatch());
   // expected-warning at -1{{Call argument for parameter 'dict' is unretained and unsafe}}
 }
 
@@ -65,4 +69,9 @@ - (NSString *)convertImage {
   RetainPtr<CGImageRef> image = [self createImage];
   return stringForImage(image.get());
 }
+
+- (const char *)dispatchLabel {
+  OSObjectPtr obj = provide_os();
+  return dispatch_queue_get_label(obj.get());
+}
 @end
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
index 75eead070fdf9..343e37d30e054 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -9,6 +9,9 @@
 CFMutableArrayRef provide_cf();
 void consume_cf(CFMutableArrayRef);
 
+dispatch_queue_t provide_dispatch();
+void consume_dispatch(dispatch_queue_t);
+
 CGImageRef provideImage();
 NSString *stringForImage(CGImageRef);
 
@@ -20,6 +23,8 @@ void foo() {
     // expected-warning at -1{{Call argument is unretained and unsafe}}
     consume_cf(provide_cf());
     // expected-warning at -1{{Call argument is unretained and unsafe}}
+    consume_dispatch(provide_dispatch());
+    // expected-warning at -1{{Call argument is unretained and unsafe}}
   }
 
   // Test that the checker works with [[clang::suppress]].
@@ -31,32 +36,32 @@ void foo_suppressed() {
 }
 
 namespace multi_arg {
-  void consume_retainable(int, SomeObj* foo, CFMutableArrayRef bar, bool);
+  void consume_retainable(int, SomeObj* foo, CFMutableArrayRef bar, dispatch_queue_t baz, bool);
   void foo() {
-    consume_retainable(42, provide(), provide_cf(), true);
+    consume_retainable(42, provide(), provide_cf(), provide_dispatch(), true);
     // expected-warning at -1{{Call argument for parameter 'foo' is unretained and unsafe}}
     // expected-warning at -2{{Call argument for parameter 'bar' is unretained and unsafe}}
+    // expected-warning at -3{{Call argument for parameter 'baz' is unretained and unsafe}}
   }
 
   void consume_retainable(SomeObj* foo, ...);
   void bar() {
-    consume_retainable(provide(), 1, provide_cf(), RetainPtr<CFMutableArrayRef> { provide_cf() }.get());
+    consume_retainable(provide(), 1, provide_cf(), RetainPtr<CFMutableArrayRef> { provide_cf() }.get(), provide_dispatch());
     // expected-warning at -1{{Call argument for parameter 'foo' is unretained and unsafe}}
     // expected-warning at -2{{Call argument is unretained and unsafe}}
+    // expected-warning at -3{{Call argument is unretained and unsafe}}
      consume_retainable(RetainPtr<SomeObj> { provide() }.get(), 1, RetainPtr<CFMutableArrayRef> { provide_cf() }.get());
   }
 }
 
 namespace retained {
-  RetainPtr<SomeObj> provide_obj() { return RetainPtr<SomeObj>{}; }
-  void consume_obj(RetainPtr<SomeObj>) {}
-
-  RetainPtr<CFMutableArrayRef> provide_cf() { return CFMutableArrayRef{}; }
-  void consume_cf(RetainPtr<CFMutableArrayRef>) {}
-
+  RetainPtr<SomeObj> provide_obj();
+  RetainPtr<CFMutableArrayRef> provide_cf();
+  OSObjectPtr<dispatch_queue_t> provide_dispatch();
   void foo() {
     consume_obj(provide_obj().get()); // no warning
     consume_cf(provide_cf().get()); // no warning
+    consume_dispatch(provide_dispatch().get()); // no warning
   }
 }
 
@@ -64,6 +69,7 @@ void foo() {
   struct Consumer {
     void consume_obj(SomeObj* ptr);
     void consume_cf(CFMutableArrayRef ref);
+    void consume_dispatch(dispatch_queue_t ptr);
   };
 
   void foo() {
@@ -73,6 +79,8 @@ void foo() {
     // expected-warning at -1{{Call argument for parameter 'ptr' is unretained and unsafe}}
     c.consume_cf(provide_cf());
     // expected-warning at -1{{Call argument for parameter 'ref' is unretained and unsafe}}
+    c.consume_dispatch(provide_dispatch());
+    // expected-warning at -1{{Call argument for parameter 'ptr' is unretained and unsafe}}
   }
 
   void foo2() {
@@ -88,6 +96,12 @@ void something() {
         consume_cf(provide_cf());
         // expected-warning at -1{{Call argument is unretained and unsafe}}
       }
+
+      void consume_dispatch(dispatch_queue_t) { some_function(); }
+      void anything() {
+        consume_dispatch(provide_dispatch());
+        // expected-warning at -1{{Call argument is unretained and unsafe}}
+      }
     };
   }
 
@@ -104,6 +118,12 @@ void something() {
         this->consume_cf(provide_cf());
         // expected-warning at -1{{Call argument is unretained and unsafe}}
       }
+
+      void consume_dispatch(dispatch_queue_t) { some_function(); }
+      void anything() {
+        this->consume_dispatch(provide_dispatch());
+        // expected-warning at -1{{Call argument is unretained and unsafe}}
+      }
     };
   }
 
@@ -131,6 +151,8 @@ void foo_ref() {
     consume_obj(0);
     consume_cf(nullptr);
     consume_cf(0);
+    consume_dispatch(nullptr);
+    consume_dispatch(0);
   }
 }
 
@@ -161,6 +183,7 @@ void bar() {
 namespace param_formarding_function {
   void consume_more_obj(OtherObj*);
   void consume_more_cf(CFMutableArrayRef);
+  void consume_more_dispatch(dispatch_queue_t);
 
   namespace objc {
     void foo(SomeObj* param) {
@@ -178,6 +201,7 @@ void foo(CFMutableArrayRef param) {
 namespace param_formarding_lambda {
   auto consume_more_obj = [](OtherObj*) { some_function(); };
   auto consume_more_cf = [](CFMutableArrayRef) { some_function(); };
+  auto consume_more_dispatch = [](dispatch_queue_t) { some_function(); };
 
   namespace objc {
     void foo(SomeObj* param) {
@@ -190,6 +214,12 @@ void foo(CFMutableArrayRef param) {
       consume_more_cf(param);
     }
   }
+  
+  namespace os_obj {
+    void foo(dispatch_queue_t param) {
+      consume_more_dispatch(param);
+    }
+  }
 }
 
 namespace param_forwarding_method {
@@ -198,6 +228,8 @@ void foo(CFMutableArrayRef param) {
     static void consume_obj_s(SomeObj*);
     void consume_cf(CFMutableArrayRef);
     static void consume_cf_s(CFMutableArrayRef);
+    void consume_dispatch(dispatch_queue_t);
+    static void consume_dispatch_s(dispatch_queue_t);
   };
 
   void bar(Consumer* consumer, SomeObj* param) {
@@ -212,12 +244,18 @@ void baz(Consumer* consumer, CFMutableArrayRef param) {
     consumer->consume_cf(param);
     Consumer::consume_cf_s(param);
   }
+
+  void baz(Consumer* consumer, dispatch_queue_t param) {
+    consumer->consume_dispatch(param);
+    Consumer::consume_dispatch_s(param);
+  }
 }
 
 
 namespace default_arg {
   SomeObj* global;
   CFMutableArrayRef global_cf;
+  dispatch_queue_t global_dispatch;
 
   void function_with_default_arg1(SomeObj* param = global);
   // expected-warning at -1{{Call argument for parameter 'param' is unretained and unsafe}}
@@ -225,9 +263,13 @@ void baz(Consumer* consumer, CFMutableArrayRef param) {
   void function_with_default_arg2(CFMutableArrayRef param = global_cf);
   // expected-warning at -1{{Call argument for parameter 'param' is unretained and unsafe}}
 
+  void function_with_default_arg3(dispatch_queue_t param = global_dispatch);
+  // expected-warning at -1{{Call argument for parameter 'param' is unretained and unsafe}}
+
   void foo() {
     function_with_default_arg1();
     function_with_default_arg2();
+    function_with_default_arg3();
   }
 }
 
@@ -259,9 +301,11 @@ void bar() {
     Foo& operator+(SomeObj* bad);
     friend Foo& operator-(Foo& lhs, SomeObj* bad);
     void operator()(SomeObj* bad);
+    Foo& operator<<(dispatch_queue_t bad);
   };
 
   SomeObj* global;
+  dispatch_queue_t global_dispatch;
 
   void foo() {
     Foo f;
@@ -271,6 +315,8 @@ void foo() {
     // expected-warning at -1{{Call argument for parameter 'bad' is unretained and unsafe}}
     f(global);
     // expected-warning at -1{{Call argument for parameter 'bad' is unretained and unsafe}}
+    f << global_dispatch;
+    // expected-warning at -1{{Call argument for parameter 'bad' is unretained and unsafe}}
   }
 }
 
@@ -280,6 +326,8 @@ void foo() {
   void foo() {
     RetainPtr<SomeObj> ptr;
     ptr = provide();
+    OSObjectPtr<dispatch_queue_t> objPtr;
+    objPtr = provide_dispatch();
   }
 
 }
@@ -287,8 +335,10 @@ void foo() {
 namespace call_with_ptr_on_ref {
   RetainPtr<SomeObj> provideProtected();
   RetainPtr<CFMutableArrayRef> provideProtectedCF();
+  OSObjectPtr<dispatch_queue_t> provideProtectedDispatch();
   void bar(SomeObj* bad);
   void bar_cf(CFMutableArrayRef bad);
+  void bar_dispatch(dispatch_queue_t);
   bool baz();
   void foo(bool v) {
     bar(v ? nullptr : provideProtected().get());
@@ -304,6 +354,13 @@ void foo(bool v) {
     // expected-warning at -1{{Call argument for parameter 'bad' is unretained and unsafe}}
     bar_cf(v ? provideProtectedCF().get() : provide_cf());
     // expected-warning at -1{{Call argument for parameter 'bad' is unretained and unsafe}}
+
+    bar_dispatch(v ? nullptr : provideProtectedDispatch().get());
+    bar_dispatch(baz() ? provideProtectedDispatch().get() : nullptr);
+    bar_dispatch(v ? provide_dispatch() : provideProtectedDispatch().get());
+    // expected-warning at -1{{Call argument is unretained and unsafe}}
+    bar_dispatch(v ? provideProtectedDispatch().get() : provide_dispatch());
+    // expected-warning at -1{{Call argument is unretained and unsafe}}
   }
 }
 
@@ -320,12 +377,16 @@ void bar() {
   void baz() {
     bar<int>();
   }
+  void os_ptr() {
+    consume_dispatch(OSObjectPtr { provide_dispatch() }.get());
+  }
 }
 
 namespace call_with_adopt_ref {
   void foo() {
     [adoptNS(provide()).get() doWork];
     CFArrayAppendValue(adoptCF(provide_cf()).get(), nullptr);
+    consume_dispatch(adoptOSObject(provide_dispatch()).get());
   }
 }
 
@@ -423,17 +484,19 @@ void idcf(CFTypeRef obj) {
 
 extern NSString * const SomeConstant;
 extern CFDictionaryRef const SomeDictionary;
-void doWork(NSString *str, CFDictiona...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/159484


More information about the cfe-commits mailing list