https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/159484
Add the support for OSObjectPtr, which behaves like RetainPtr. >From 126317069f2e9332849985bcfced95aba280e1fa Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa <rn...@webkit.org> Date: Wed, 17 Sep 2025 16:55:50 -0700 Subject: [PATCH] [WebKit checkers] Add the support for OSObjectPtr Add the support for OSObjectPtr, which behaves like RetainPtr. --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 6 +- .../WebKit/RawPtrRefMemberChecker.cpp | 11 +- .../WebKit/RetainPtrCtorAdoptChecker.cpp | 3 +- .../Checkers/WebKit/call-args-checked.cpp | 14 -- .../Analysis/Checkers/WebKit/mock-types.h | 10 ++ .../Checkers/WebKit/objc-mock-types.h | 156 ++++++++++++++++++ .../Checkers/WebKit/uncounted-obj-arg.cpp | 3 - .../WebKit/unretained-call-args-arc.mm | 15 +- .../Checkers/WebKit/unretained-call-args.mm | 95 +++++++++-- .../WebKit/unretained-lambda-captures-arc.mm | 49 +++++- .../WebKit/unretained-lambda-captures.mm | 67 +++++++- .../WebKit/unretained-local-vars-arc.mm | 19 ++- .../Checkers/WebKit/unretained-local-vars.mm | 128 +++++++++++++- .../Checkers/WebKit/unretained-members-arc.mm | 24 ++- .../Checkers/WebKit/unretained-members.mm | 48 ++++-- 15 files changed, 570 insertions(+), 78 deletions(-) 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 +@protocol OS_dispatch_queue +@end + +typedef NSObject<OS_dispatch_queue> *dispatch_queue_t; + +@protocol OS_dispatch_queue_attr +@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@-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@-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@-1{{Call argument is unretained and unsafe}} consume_cf(provide_cf()); // expected-warning@-1{{Call argument is unretained and unsafe}} + consume_dispatch(provide_dispatch()); + // expected-warning@-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@-1{{Call argument for parameter 'foo' is unretained and unsafe}} // expected-warning@-2{{Call argument for parameter 'bar' is unretained and unsafe}} + // expected-warning@-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@-1{{Call argument for parameter 'foo' is unretained and unsafe}} // expected-warning@-2{{Call argument is unretained and unsafe}} + // expected-warning@-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@-1{{Call argument for parameter 'ptr' is unretained and unsafe}} c.consume_cf(provide_cf()); // expected-warning@-1{{Call argument for parameter 'ref' is unretained and unsafe}} + c.consume_dispatch(provide_dispatch()); + // expected-warning@-1{{Call argument for parameter 'ptr' is unretained and unsafe}} } void foo2() { @@ -88,6 +96,12 @@ void something() { consume_cf(provide_cf()); // expected-warning@-1{{Call argument is unretained and unsafe}} } + + void consume_dispatch(dispatch_queue_t) { some_function(); } + void anything() { + consume_dispatch(provide_dispatch()); + // expected-warning@-1{{Call argument is unretained and unsafe}} + } }; } @@ -104,6 +118,12 @@ void something() { this->consume_cf(provide_cf()); // expected-warning@-1{{Call argument is unretained and unsafe}} } + + void consume_dispatch(dispatch_queue_t) { some_function(); } + void anything() { + this->consume_dispatch(provide_dispatch()); + // expected-warning@-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@-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@-1{{Call argument for parameter 'param' is unretained and unsafe}} + void function_with_default_arg3(dispatch_queue_t param = global_dispatch); + // expected-warning@-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@-1{{Call argument for parameter 'bad' is unretained and unsafe}} f(global); // expected-warning@-1{{Call argument for parameter 'bad' is unretained and unsafe}} + f << global_dispatch; + // expected-warning@-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@-1{{Call argument for parameter 'bad' is unretained and unsafe}} bar_cf(v ? provideProtectedCF().get() : provide_cf()); // expected-warning@-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@-1{{Call argument is unretained and unsafe}} + bar_dispatch(v ? provideProtectedDispatch().get() : provide_dispatch()); + // expected-warning@-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, 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(); void use_const_local() { - doWork(provide_str(), provide_dict()); + doWork(provide_str(), provide_dict(), provide_dispatch()); // expected-warning@-1{{Call argument for parameter 'str' is unretained and unsafe}} // expected-warning@-2{{Call argument for parameter 'dict' is unretained and unsafe}} + // expected-warning@-3{{Call argument for parameter 'dispatch' is unretained and unsafe}} } } // namespace const_global @@ -470,12 +533,15 @@ bool baz(NSObject *obj) { NSString *provideNS() NS_RETURNS_RETAINED; CFDictionaryRef provideCF() CF_RETURNS_RETAINED; +dispatch_queue_t provideDispatch() NS_RETURNS_RETAINED; void consumeNS(NSString *); void consumeCF(CFDictionaryRef); +void consumeDispatch(dispatch_queue_t); void foo() { consumeNS(provideNS()); consumeCF(provideCF()); + consumeDispatch(provideDispatch()); } struct Base { @@ -506,10 +572,11 @@ - (void)doWork:(NSString *)msg, ... { - (void)doWorkOnSelf { [self doWork:nil]; - [self doWork:@"hello", provide(), provide_cf()]; + [self doWork:@"hello", provide(), provide_cf(), provide_dispatch()]; // expected-warning@-1{{Call argument is unretained and unsafe}} // expected-warning@-2{{Call argument is unretained and unsafe}} - [self doWork:@"hello", RetainPtr<SomeObj> { provide() }.get(), RetainPtr<CFMutableArrayRef> { provide_cf() }.get()]; + // expected-warning@-3{{Call argument is unretained and unsafe}} + [self doWork:@"hello", RetainPtr<SomeObj> { provide() }.get(), RetainPtr<CFMutableArrayRef> { provide_cf() }.get(), OSObjectPtr { provide_dispatch() }.get()]; [self doWork:__null]; [self doWork:nil]; } diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-arc.mm index 63526a5047157..4e024dea7037a 100644 --- a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-arc.mm +++ b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-arc.mm @@ -112,6 +112,7 @@ explicit CallableWrapper(CallableType& callable) SomeObj* make_obj(); CFMutableArrayRef make_cf(); +dispatch_queue_t make_os(); void someFunction(); template <typename Callback> void call(Callback callback) { @@ -125,8 +126,6 @@ void raw_ptr() { auto foo1 = [obj](){ [obj doWork]; }; - call(foo1); - auto foo2 = [&obj](){ [obj doWork]; }; @@ -157,6 +156,21 @@ void raw_ptr() { // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }; + auto os = make_os(); + auto baz1 = [os](){ + dispatch_queue_get_label(os); + }; + auto baz2 = [&os](){ + dispatch_queue_get_label(os); + }; + auto baz3 = [&](){ + dispatch_queue_get_label(os); + os = nullptr; + }; + auto baz4 = [=](){ + dispatch_queue_get_label(os); + }; + call(foo1); call(foo2); call(foo3); @@ -166,6 +180,11 @@ void raw_ptr() { call(bar2); call(bar3); call(bar4); + + call(baz1); + call(baz2); + call(baz3); + call(baz4); } void quiet() { @@ -208,6 +227,14 @@ void get_count_cf(CFArrayRef array, [[clang::noescape]] Callback1&& callback1, C callback2(count); } +template <typename Callback1, typename Callback2> +void get_count_os(dispatch_queue_t queue, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2) +{ + auto* label = dispatch_queue_get_label(queue); + callback1(label); + callback2(label); +} + void noescape_lambda() { SomeObj* someObj = make_obj(); SomeObj* otherObj = make_obj(); @@ -230,6 +257,13 @@ void noescape_lambda() { CFArrayAppendValue(someCF, nullptr); // expected-warning@-1{{Implicitly captured reference 'someCF' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); + + dispatch_queue_t someOS = make_os(); + get_count_os(make_os(), [&](const char* label) { + dispatch_queue_get_label(someOS); + }, [&](const char* label) { + dispatch_queue_get_label(someOS); + }); } void callFunctionOpaque(WTF::Function<void()>&&); @@ -238,22 +272,25 @@ void callFunction(WTF::Function<void()>&& function) { function(); } -void lambda_converted_to_function(SomeObj* obj, CFMutableArrayRef cf) +void lambda_converted_to_function(SomeObj* obj, CFMutableArrayRef cf, dispatch_queue_t os) { callFunction([&]() { [obj doWork]; CFArrayAppendValue(cf, nullptr); // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + dispatch_queue_get_label(os); }); callFunctionOpaque([&]() { [obj doWork]; CFArrayAppendValue(cf, nullptr); // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + dispatch_queue_get_label(os); }); } @interface ObjWithSelf : NSObject { RetainPtr<id> delegate; + OSObjectPtr<dispatch_queue_t> queue; } -(void)doWork; -(void)doMoreWork; @@ -274,9 +311,15 @@ -(void)doWork { someFunction(); [delegate doWork]; }; + auto* queuePtr = queue.get(); + auto doAdditionalWork = [&] { + someFunction(); + dispatch_queue_get_label(queuePtr); + }; callFunctionOpaque(doWork); callFunctionOpaque(doMoreWork); callFunctionOpaque(doExtraWork); + callFunctionOpaque(doAdditionalWork); } -(void)doMoreWork { diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm index dcc6672261d8c..76a1da7707a2a 100644 --- a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm +++ b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm @@ -112,6 +112,7 @@ explicit CallableWrapper(CallableType& callable) SomeObj* make_obj(); CFMutableArrayRef make_cf(); +dispatch_queue_t make_os(); void someFunction(); template <typename Callback> void call(Callback callback) { @@ -161,6 +162,25 @@ void raw_ptr() { // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }; + auto os = make_os(); + auto baz1 = [os](){ + // expected-warning@-1{{Captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + dispatch_queue_get_label(os); + }; + auto baz2 = [&os](){ + // expected-warning@-1{{Captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + dispatch_queue_get_label(os); + }; + auto baz3 = [&](){ + dispatch_queue_get_label(os); + // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + os = nullptr; + }; + auto baz4 = [=](){ + dispatch_queue_get_label(os); + // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }; + call(foo1); call(foo2); call(foo3); @@ -171,8 +191,13 @@ void raw_ptr() { call(bar3); call(bar4); + call(baz1); + call(baz2); + call(baz3); + call(baz4); + // Confirm that the checker respects [[clang::suppress]]. - SomeObj* suppressed_obj = nullptr; + SomeObj* suppressed_obj = make_obj(); [[clang::suppress]] auto foo5 = [suppressed_obj](){ [suppressed_obj doWork]; }; @@ -180,12 +205,20 @@ void raw_ptr() { call(foo5); // Confirm that the checker respects [[clang::suppress]]. - CFMutableArrayRef suppressed_cf = nullptr; + CFMutableArrayRef suppressed_cf = make_cf(); [[clang::suppress]] auto bar5 = [suppressed_cf](){ CFArrayAppendValue(suppressed_cf, nullptr); }; // no warning. call(bar5); + + // Confirm that the checker respects [[clang::suppress]]. + dispatch_queue_t suppressed_os = make_os(); + [[clang::suppress]] auto baz5 = [suppressed_os](){ + dispatch_queue_get_label(suppressed_os); + }; + // no warning. + call(baz5); } void quiet() { @@ -228,6 +261,14 @@ void get_count_cf(CFArrayRef array, [[clang::noescape]] Callback1&& callback1, C callback2(count); } +template <typename Callback1, typename Callback2> +void get_count_os(dispatch_queue_t queue, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2) +{ + auto* label = dispatch_queue_get_label(queue); + callback1(label); + callback2(label); +} + void noescape_lambda() { SomeObj* someObj = make_obj(); SomeObj* otherObj = make_obj(); @@ -251,6 +292,14 @@ void noescape_lambda() { CFArrayAppendValue(someCF, nullptr); // expected-warning@-1{{Implicitly captured reference 'someCF' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); + + dispatch_queue_t someOS = make_os(); + get_count_os(make_os(), [&](const char* label) { + dispatch_queue_get_label(someOS); + }, [&](const char* label) { + dispatch_queue_get_label(someOS); + // expected-warning@-1{{Implicitly captured reference 'someOS' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }); } void callFunctionOpaque(WTF::Function<void()>&&); @@ -259,24 +308,29 @@ void callFunction(WTF::Function<void()>&& function) { function(); } -void lambda_converted_to_function(SomeObj* obj, CFMutableArrayRef cf) +void lambda_converted_to_function(SomeObj* obj, CFMutableArrayRef cf, dispatch_queue_t os) { callFunction([&]() { [obj doWork]; // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} CFArrayAppendValue(cf, nullptr); // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + dispatch_queue_get_label(os); + // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); callFunctionOpaque([&]() { [obj doWork]; // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} CFArrayAppendValue(cf, nullptr); // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + dispatch_queue_get_label(os); + // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); } @interface ObjWithSelf : NSObject { RetainPtr<id> delegate; + OSObjectPtr<dispatch_queue_t> queue; } -(void)doWork; -(void)doMoreWork; @@ -299,9 +353,16 @@ -(void)doWork { someFunction(); [delegate doWork]; }; + auto* queuePtr = queue.get(); + auto doAdditionalWork = [&] { + someFunction(); + dispatch_queue_get_label(queuePtr); + // expected-warning@-1{{Implicitly captured raw-pointer 'queuePtr' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + }; callFunctionOpaque(doWork); callFunctionOpaque(doMoreWork); callFunctionOpaque(doExtraWork); + callFunctionOpaque(doAdditionalWork); } -(void)doMoreWork { diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-arc.mm index a84bee8529645..3d256f1599f04 100644 --- a/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-arc.mm +++ b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars-arc.mm @@ -23,24 +23,32 @@ void bar() { CFArrayAppendValue(array, nullptr); } +void baz() { + auto queue = dispatch_queue_create("some queue", nullptr); + dispatch_queue_get_label(queue); +} + } // namespace raw_ptr namespace const_global { extern NSString * const SomeConstant; extern CFDictionaryRef const SomeDictionary; -void doWork(NSString *, CFDictionaryRef); +extern dispatch_queue_t const SomeDispatch; +void doWork(NSString *, CFDictionaryRef, dispatch_queue_t); 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() { NSString * const str = provide_str(); CFDictionaryRef dict = provide_dict(); // expected-warning@-1{{Local variable 'dict' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} - doWork(str, dict); + auto dispatch = provide_dispatch(); + doWork(str, dict, dispatch); } } // namespace const_global @@ -63,4 +71,9 @@ - (void)bar { // expected-warning@-1{{Local variable 'array' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} CFArrayAppendValue(array, nullptr); } + +- (void)baz { + auto queue = dispatch_queue_create("some queue", nullptr); + dispatch_queue_get_label(queue); +} @end diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm index 0ad8f707e254c..307a4d03fe101 100644 --- a/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm +++ b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm @@ -30,18 +30,27 @@ void cf_ptr() { // expected-warning@-1{{Local variable 'array' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} CFArrayAppendValue(array, nullptr); } + +dispatch_queue_t provide_os(); +void os_ptr() { + auto queue = provide_os(); + // expected-warning@-1{{Local variable 'queue' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} + dispatch_queue_get_label(queue); +} + } // namespace pointer namespace guardian_scopes { +SomeObj *provide(); void foo1() { - RetainPtr<SomeObj> foo; + RetainPtr<SomeObj> foo = provide(); { SomeObj *bar = foo.get(); } } void foo2() { - RetainPtr<SomeObj> foo; + RetainPtr<SomeObj> foo = provide(); // missing embedded scope here SomeObj *bar = foo.get(); // expected-warning@-1{{Local variable 'bar' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} @@ -49,7 +58,7 @@ void foo2() { } void foo3() { - RetainPtr<SomeObj> foo; + RetainPtr<SomeObj> foo = provide(); { { SomeObj *bar = foo.get(); } } @@ -57,11 +66,35 @@ void foo3() { void foo4() { { - RetainPtr<SomeObj> foo; + RetainPtr<SomeObj> foo = provide(); { SomeObj *bar = foo.get(); } } } +CFMutableArrayRef provide_cf(); +void foo5() { + RetainPtr<CFMutableArrayRef> foo = provide_cf(); + { + CFMutableArrayRef bar = foo.get(); + CFArrayAppendValue(bar, nullptr); + } + CFMutableArrayRef baz = foo.get(); + // expected-warning@-1{{Local variable 'baz' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} + CFArrayAppendValue(baz, nullptr); +} + +dispatch_queue_t provide_os(); +void foo6() { + OSObjectPtr<dispatch_queue_t> queue = provide_os(); + { + dispatch_queue_t bar = queue.get(); + dispatch_queue_get_label(bar); + } + dispatch_queue_t baz = queue.get(); + // expected-warning@-1{{Local variable 'baz' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} + dispatch_queue_get_label(baz); +} + struct SelfReferencingStruct { SelfReferencingStruct* ptr; SomeObj* obj { nullptr }; @@ -171,13 +204,35 @@ void foo10() { } } +bool trivialFunction(dispatch_queue_t queue) { return !!queue; } +void foo11() { + OSObjectPtr<dispatch_queue_t> queue = adoptOSObject(dispatch_queue_create("some queue", nullptr)); + { + dispatch_queue_t queuePtr = queue.get(); + dispatch_queue_get_label(queuePtr); + } + { + dispatch_queue_t queuePtr = queue.get(); + // expected-warning@-1{{Local variable 'queuePtr' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} + queue = nullptr; + dispatch_queue_get_label(queuePtr); + } + { + dispatch_queue_t queuePtr = queue.get(); + if (trivialFunction(queuePtr)) + queuePtr = nullptr; + } +} + } // namespace guardian_scopes namespace auto_keyword { class Foo { SomeObj *provide_obj(); CFMutableArrayRef provide_cf_array(); + dispatch_queue_t provide_queue(); void doWork(CFMutableArrayRef); + void doWork(dispatch_queue_t); void evil_func() { SomeObj *bar = provide_obj(); @@ -204,6 +259,14 @@ void bar() { doWork(baz); } + void baz() { + auto value1 = provide_queue(); + // expected-warning@-1{{Local variable 'value1' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} + doWork(value1); + [[clang::suppress]] auto value2 = provide_queue(); // no-warning + doWork(value2); + } + }; } // namespace auto_keyword @@ -223,6 +286,14 @@ void foo2() { someFunction(); } } + +void foo3() { + OSObjectPtr<dispatch_queue_t> foo; + { + dispatch_queue_t bar = foo.get(); + someFunction(); + } +} } // namespace guardian_casts namespace conditional_op { @@ -292,6 +363,15 @@ void bar(CFMutableArrayRef a) { doWork(a); } +dispatch_queue_t provide_queue(); +void doWork(dispatch_queue_t); + +void baz(dispatch_queue_t a) { + a = provide_queue(); + // expected-warning@-1{{Assignment to an unretained parameter 'a' is unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} + doWork(a); +} + } // namespace local_assignment_to_parameter namespace local_assignment_to_static_local { @@ -317,6 +397,16 @@ void bar() { doWork(a); } +dispatch_queue_t provide_queue(); +void doWork(dispatch_queue_t); + +void baz() { + static dispatch_queue_t a = nullptr; + // expected-warning@-1{{Static local variable 'a' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} + a = provide_queue(); + doWork(a); +} + } // namespace local_assignment_to_static_local namespace local_assignment_to_global { @@ -344,6 +434,16 @@ void bar() { doWork(g_b); } +dispatch_queue_t provide_queue(); +void doWork(dispatch_queue_t); +dispatch_queue_t g_c = nullptr; +// expected-warning@-1{{Global variable 'local_assignment_to_global::g_c' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} + +void baz() { + g_c = provide_queue(); + doWork(g_c); +} + } // namespace local_assignment_to_global namespace local_var_for_singleton { @@ -358,6 +458,11 @@ void foo() { void bar() { CFMutableArrayRef cf = cfSingleton(); } + + dispatch_queue_t osSingleton(); + void baz() { + dispatch_queue_t os = osSingleton(); + } } namespace ptr_conversion { @@ -391,19 +496,23 @@ unsigned ccf(CFTypeRef obj) { extern NSString * const SomeConstant; extern CFDictionaryRef const SomeDictionary; -void doWork(NSString *, CFDictionaryRef); +extern dispatch_queue_t const SomeQueue; +void doWork(NSString *, CFDictionaryRef, dispatch_queue_t); void use_const_global() { - doWork(SomeConstant, SomeDictionary); + doWork(SomeConstant, SomeDictionary, SomeQueue); } NSString *provide_str(); CFDictionaryRef provide_dict(); +dispatch_queue_t provide_queue(); void use_const_local() { NSString * const str = provide_str(); // expected-warning@-1{{Local variable 'str' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} CFDictionaryRef dict = provide_dict(); // expected-warning@-1{{Local variable 'dict' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} - doWork(str, dict); + dispatch_queue_t queue = provide_queue(); + // expected-warning@-1{{Local variable 'queue' is unretained and unsafe [alpha.webkit.UnretainedLocalVarsChecker]}} + doWork(str, dict, queue); } } // namespace const_global @@ -412,13 +521,16 @@ void use_const_local() { NSString *provideNS() NS_RETURNS_RETAINED; CFDictionaryRef provideCF() CF_RETURNS_RETAINED; +dispatch_queue_t provideOS() CF_RETURNS_RETAINED; void consumeNS(NSString *); void consumeCF(CFDictionaryRef); +int consumeOS(dispatch_queue_t); unsigned foo() { auto *string = provideNS(); auto *dictionary = provideCF(); - return string.length + CFDictionaryGetCount(dictionary); + auto* queue = provideOS(); + return string.length + CFDictionaryGetCount(dictionary) + consumeOS(queue); } } // namespace ns_retained_return_value diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm b/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm index 00e6e6ec1dcfa..19c54c4dc07ba 100644 --- a/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm +++ b/clang/test/Analysis/Checkers/WebKit/unretained-members-arc.mm @@ -20,22 +20,26 @@ CFMutableArrayRef e = nullptr; // expected-warning@-1{{Member variable 'e' in 'members::Foo' is a retainable type 'CFMutableArrayRef'}} + + dispatch_queue_t f = nullptr; }; union FooUnion { SomeObj* a; CFMutableArrayRef b; // expected-warning@-1{{Member variable 'b' in 'members::FooUnion' is a retainable type 'CFMutableArrayRef'}} + dispatch_queue_t c; }; - template<class T, class S> + template<class T, class S, class R> struct FooTmpl { T* x; S y; -// expected-warning@-1{{Member variable 'y' in 'members::FooTmpl<SomeObj, __CFArray *>' is a raw pointer to retainable type}} +// expected-warning@-1{{Member variable 'y' in 'members::FooTmpl<SomeObj, __CFArray *, NSObject<OS_dispatch_queue> *>' is a raw pointer to retainable type}} + R z; }; - void forceTmplToInstantiate(FooTmpl<SomeObj, CFMutableArrayRef>) {} + void forceTmplToInstantiate(FooTmpl<SomeObj, CFMutableArrayRef, dispatch_queue_t>) {} struct [[clang::suppress]] FooSuppressed { private: @@ -51,16 +55,19 @@ void forceTmplToInstantiate(FooTmpl<SomeObj, CFMutableArrayRef>) {} // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::List' contains a retainable type 'CFMutableArrayRef'}} }; - template <typename T, typename S> + template <typename T, typename S, typename R> struct TemplateList { + T* elements1; S* elements2; - // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::TemplateList<SomeObj, __CFArray *>' contains a raw pointer to retainable type '__CFArray'}} + // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::TemplateList<SomeObj, __CFArray *, NSObject<OS_dispatch_queue> *>' contains a raw pointer to retainable type '__CFArray'}} + R* elements3; }; - TemplateList<SomeObj, CFMutableArrayRef> list; + TemplateList<SomeObj, CFMutableArrayRef, dispatch_queue_t> list; struct SafeList { RetainPtr<SomeObj>* elements1; RetainPtr<CFMutableArrayRef>* elements2; + OSObjectPtr<dispatch_queue_t> elements3; }; } // namespace ptr_to_ptr_to_retained @@ -69,6 +76,7 @@ @interface AnotherObject : NSObject { NSString *ns_string; CFStringRef cf_string; // expected-warning@-1{{Instance variable 'cf_string' in 'AnotherObject' is a retainable type 'CFStringRef'; member variables must be a RetainPtr}} + dispatch_queue_t queue; } @property(nonatomic, strong) NSString *prop_string1; @property(nonatomic, assign) NSString *prop_string2; @@ -76,6 +84,7 @@ @interface AnotherObject : NSObject { @property(nonatomic, unsafe_unretained) NSString *prop_string3; // expected-warning@-1{{Property 'prop_string3' in 'AnotherObject' is a raw pointer to retainable type 'NSString'; member variables must be a RetainPtr}} @property(nonatomic, readonly) NSString *prop_string4; +@property(nonatomic, readonly) dispatch_queue_t prop_string5; @end NS_REQUIRES_PROPERTY_DEFINITIONS @@ -90,6 +99,8 @@ @interface NoSynthObject : NSObject { // expected-warning@-1{{Property 'prop_string3' in 'NoSynthObject' is a raw pointer to retainable type 'NSString'; member variables must be a RetainPtr}} @property(nonatomic, unsafe_unretained) NSString *prop_string4; // expected-warning@-1{{Property 'prop_string4' in 'NoSynthObject' is a raw pointer to retainable type 'NSString'; member variables must be a RetainPtr}} +@property(nonatomic, unsafe_unretained) dispatch_queue_t prop_string5; +// expected-warning@-1{{Property 'prop_string5' in 'NoSynthObject' is a retainable type 'dispatch_queue_t'}} @end @implementation NoSynthObject @@ -99,4 +110,5 @@ - (NSString *)prop_string1 { @synthesize prop_string2; @synthesize prop_string3; @synthesize prop_string4; +@synthesize prop_string5; @end diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-members.mm b/clang/test/Analysis/Checkers/WebKit/unretained-members.mm index 46f65dfa603ad..155848f9834af 100644 --- a/clang/test/Analysis/Checkers/WebKit/unretained-members.mm +++ b/clang/test/Analysis/Checkers/WebKit/unretained-members.mm @@ -11,6 +11,8 @@ private: SomeObj* a = nullptr; // expected-warning@-1{{Member variable 'a' in 'members::Foo' is a raw pointer to retainable type}} + dispatch_queue_t a2 = nullptr; +// expected-warning@-1{{Member variable 'a2' in 'members::Foo' is a retainable type 'dispatch_queue_t'}} [[clang::suppress]] SomeObj* a_suppressed = nullptr; @@ -19,25 +21,31 @@ protected: RetainPtr<SomeObj> b; // No warning. + OSObjectPtr<dispatch_queue_t> b2; +// No warning. public: SomeObj* c = nullptr; // expected-warning@-1{{Member variable 'c' in 'members::Foo' is a raw pointer to retainable type}} RetainPtr<SomeObj> d; + OSObjectPtr<dispatch_queue_t> d2; CFMutableArrayRef e = nullptr; // expected-warning@-1{{Member variable 'e' in 'members::Foo' is a retainable type 'CFMutableArrayRef'}} + }; - template<class T, class S> + template<class T, class S, class R> struct FooTmpl { T* a; -// expected-warning@-1{{Member variable 'a' in 'members::FooTmpl<SomeObj, __CFArray *>' is a raw pointer to retainable type}} +// expected-warning@-1{{Member variable 'a' in 'members::FooTmpl<SomeObj, __CFArray *, NSObject<OS_dispatch_queue> *>' is a raw pointer to retainable type}} S b; -// expected-warning@-1{{Member variable 'b' in 'members::FooTmpl<SomeObj, __CFArray *>' is a raw pointer to retainable type}} +// expected-warning@-1{{Member variable 'b' in 'members::FooTmpl<SomeObj, __CFArray *, NSObject<OS_dispatch_queue> *>' is a raw pointer to retainable type}} + R c; +// expected-warning@-1{{Member variable 'c' in 'members::FooTmpl<SomeObj, __CFArray *, NSObject<OS_dispatch_queue> *>' is a raw pointer to retainable type}} }; - void forceTmplToInstantiate(FooTmpl<SomeObj, CFMutableArrayRef>) {} + void forceTmplToInstantiate(FooTmpl<SomeObj, CFMutableArrayRef, dispatch_queue_t>) {} struct [[clang::suppress]] FooSuppressed { private: @@ -54,6 +62,8 @@ void forceTmplToInstantiate(FooTmpl<SomeObj, CFMutableArrayRef>) {} RetainPtr<SomeObj> b; CFMutableArrayRef c; // expected-warning@-1{{Member variable 'c' in 'unions::Foo' is a retainable type 'CFMutableArrayRef'}} + dispatch_queue_t d; + // expected-warning@-1{{Member variable 'd' in 'unions::Foo' is a retainable type 'dispatch_queue_t'}} }; template<class T> @@ -72,16 +82,20 @@ void forceTmplToInstantiate(FooTempl<SomeObj>) {} // expected-warning@-1{{Member variable 'elements1' in 'ptr_to_ptr_to_retained::List' contains a raw pointer to retainable type 'SomeObj'}} CFMutableArrayRef* elements2; // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::List' contains a retainable type 'CFMutableArrayRef'}} + dispatch_queue_t* elements3; + // expected-warning@-1{{Member variable 'elements3' in 'ptr_to_ptr_to_retained::List' contains a retainable type 'dispatch_queue_t'}} }; - template <typename T, typename S> + template <typename T, typename S, typename R> struct TemplateList { T** elements1; - // expected-warning@-1{{Member variable 'elements1' in 'ptr_to_ptr_to_retained::TemplateList<SomeObj, __CFArray *>' contains a raw pointer to retainable type 'SomeObj'}} + // expected-warning@-1{{Member variable 'elements1' in 'ptr_to_ptr_to_retained::TemplateList<SomeObj, __CFArray *, NSObject<OS_dispatch_queue> *>' contains a raw pointer to retainable type 'SomeObj'}} S* elements2; - // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::TemplateList<SomeObj, __CFArray *>' contains a raw pointer to retainable type '__CFArray'}} + // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::TemplateList<SomeObj, __CFArray *, NSObject<OS_dispatch_queue> *>' contains a raw pointer to retainable type '__CFArray'}} + R* elements3; + // expected-warning@-1{{Member variable 'elements3' in 'ptr_to_ptr_to_retained::TemplateList<SomeObj, __CFArray *, NSObject<OS_dispatch_queue> *>' contains a raw pointer to retainable type 'NSObject'}} }; - TemplateList<SomeObj, CFMutableArrayRef> list; + TemplateList<SomeObj, CFMutableArrayRef, dispatch_queue_t> list; struct SafeList { RetainPtr<SomeObj>* elements1; @@ -92,20 +106,24 @@ void forceTmplToInstantiate(FooTempl<SomeObj>) {} @interface AnotherObject : NSObject { NSString *ns_string; - // expected-warning@-1{{Instance variable 'ns_string' in 'AnotherObject' is a raw pointer to retainable type 'NSString'; member variables must be a RetainPtr}} + // expected-warning@-1{{Instance variable 'ns_string' in 'AnotherObject' is a raw pointer to retainable type 'NSString'}} CFStringRef cf_string; - // expected-warning@-1{{Instance variable 'cf_string' in 'AnotherObject' is a retainable type 'CFStringRef'; member variables must be a RetainPtr}} + // expected-warning@-1{{Instance variable 'cf_string' in 'AnotherObject' is a retainable type 'CFStringRef'}} + dispatch_queue_t dispatch; + // expected-warning@-1{{Instance variable 'dispatch' in 'AnotherObject' is a retainable type 'dispatch_queue_t'}} } @property(nonatomic, strong) NSString *prop_string; -// expected-warning@-1{{Property 'prop_string' in 'AnotherObject' is a raw pointer to retainable type 'NSString'; member variables must be a RetainPtr}} +// expected-warning@-1{{Property 'prop_string' in 'AnotherObject' is a raw pointer to retainable type 'NSString'}} @end NS_REQUIRES_PROPERTY_DEFINITIONS @interface NoSynthObject : NSObject { NSString *ns_string; - // expected-warning@-1{{Instance variable 'ns_string' in 'NoSynthObject' is a raw pointer to retainable type 'NSString'; member variables must be a RetainPtr}} + // expected-warning@-1{{Instance variable 'ns_string' in 'NoSynthObject' is a raw pointer to retainable type 'NSString'}} CFStringRef cf_string; - // expected-warning@-1{{Instance variable 'cf_string' in 'NoSynthObject' is a retainable type 'CFStringRef'; member variables must be a RetainPtr}} + // expected-warning@-1{{Instance variable 'cf_string' in 'NoSynthObject' is a retainable type 'CFStringRef'}} + dispatch_queue_t dispatch; + // expected-warning@-1{{Instance variable 'dispatch' in 'NoSynthObject' is a retainable type 'dispatch_queue_t'}} } @property(nonatomic, readonly, strong) NSString *prop_string1; @property(nonatomic, readonly, strong) NSString *prop_string2; @@ -114,6 +132,7 @@ @interface NoSynthObject : NSObject { // expected-warning@-1{{Property 'prop_string3' in 'NoSynthObject' is a raw pointer to retainable type 'NSString'; member variables must be a RetainPtr}} @property(nonatomic, unsafe_unretained) NSString *prop_string4; // expected-warning@-1{{Property 'prop_string4' in 'NoSynthObject' is a raw pointer to retainable type 'NSString'; member variables must be a RetainPtr}} +@property(nonatomic, readonly, strong) NSString *dispatch; @end @implementation NoSynthObject @@ -123,4 +142,7 @@ - (NSString *)prop_string1 { @synthesize prop_string2; @synthesize prop_string3; @synthesize prop_string4; +- (dispatch_queue_t)dispatch { + return nil; +} @end _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits