[clang] a285be3 - [WebKit Checkers] Recognize Objective-C and CF pointer conversion functions. (#132784)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 27 15:47:42 PDT 2025
Author: Ryosuke Niwa
Date: 2025-03-27T15:47:38-07:00
New Revision: a285be320aadad51c431eed87cc2540951b3e4da
URL: https://github.com/llvm/llvm-project/commit/a285be320aadad51c431eed87cc2540951b3e4da
DIFF: https://github.com/llvm/llvm-project/commit/a285be320aadad51c431eed87cc2540951b3e4da.diff
LOG: [WebKit Checkers] Recognize Objective-C and CF pointer conversion functions. (#132784)
Recognize dynamic_objc_cast, checked_objc_cast, dynamic_cf_cast, and
checked_cf_cast.
Added:
Modified:
clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index b4d2353a03cd2..7bc04ee565d03 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -418,7 +418,10 @@ bool isPtrConversion(const FunctionDecl *F) {
FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
FunctionName == "checkedDowncast" ||
FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast" ||
- FunctionName == "bridge_cast")
+ FunctionName == "bridge_cast" || FunctionName == "bridge_id_cast" ||
+ FunctionName == "dynamic_cf_cast" || FunctionName == "checked_cf_cast" ||
+ FunctionName == "dynamic_objc_cast" ||
+ FunctionName == "checked_objc_cast")
return true;
return false;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
index 39e9cd023d1f7..ce8f0df697b06 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
@@ -286,15 +286,12 @@ class RawPtrRefCallArgsChecker
overloadedOperatorType == OO_PipePipe)
return true;
- if (isCtorOfSafePtr(Callee))
+ if (isCtorOfSafePtr(Callee) || isPtrConversion(Callee))
return true;
auto name = safeGetName(Callee);
if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
- name == "dynamicDowncast" || name == "downcast" ||
- name == "checkedDowncast" || name == "uncheckedDowncast" ||
- name == "bitwise_cast" || name == "is" || name == "equal" ||
- name == "hash" || name == "isType" ||
+ name == "is" || name == "equal" || name == "hash" || name == "isType" ||
// FIXME: Most/all of these should be implemented via attributes.
name == "equalIgnoringASCIICase" ||
name == "equalIgnoringASCIICaseCommon" ||
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index 5bd265596a0b4..ef46a7c0a2925 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -5,6 +5,7 @@
#define CF_BRIDGED_TYPE(T) __attribute__((objc_bridge(T)))
#define CF_BRIDGED_MUTABLE_TYPE(T) __attribute__((objc_bridge_mutable(T)))
typedef CF_BRIDGED_TYPE(id) void * CFTypeRef;
+typedef unsigned long long CFTypeID;
typedef signed char BOOL;
typedef unsigned char Boolean;
typedef signed long CFIndex;
@@ -21,6 +22,8 @@ typedef struct CF_BRIDGED_MUTABLE_TYPE(CFRunLoopRef) __CFRunLoop * CFRunLoopRef;
extern const CFAllocatorRef kCFAllocatorDefault;
typedef struct _NSZone NSZone;
+CFTypeID CFGetTypeID(CFTypeRef cf);
+CFTypeID CFArrayGetTypeID();
CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity);
extern void CFArrayAppendValue(CFMutableArrayRef theArray, const void *value);
CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues);
@@ -29,6 +32,7 @@ CFIndex CFArrayGetCount(CFArrayRef theArray);
typedef const struct CF_BRIDGED_TYPE(NSDictionary) __CFDictionary * CFDictionaryRef;
typedef struct CF_BRIDGED_MUTABLE_TYPE(NSMutableDictionary) __CFDictionary * CFMutableDictionaryRef;
+CFTypeID CFDictionaryGetTypeID();
CFDictionaryRef CFDictionaryCreate(CFAllocatorRef allocator, const void **keys, const void **values, CFIndex numValues);
CFDictionaryRef CFDictionaryCreateCopy(CFAllocatorRef allocator, CFDictionaryRef theDict);
CFDictionaryRef CFDictionaryCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFDictionaryRef theDict);
@@ -135,6 +139,8 @@ __attribute__((objc_root_class))
namespace WTF {
+void WTFCrash(void);
+
template<typename T> class RetainPtr;
template<typename T> RetainPtr<T> adoptNS(T*);
template<typename T> RetainPtr<T> adoptCF(T);
@@ -273,11 +279,163 @@ inline CFTypeRef bridge_cast(NSObject *object)
return (__bridge CFTypeRef)object;
}
+inline id bridge_id_cast(CFTypeRef object)
+{
+ return (__bridge id)object;
+}
+
+inline RetainPtr<id> bridge_id_cast(RetainPtr<CFTypeRef>&& object)
+{
+#if __has_feature(objc_arc)
+ return adoptNS((__bridge_transfer id)object.leakRef());
+#else
+ return adoptNS((__bridge id)object.leakRef());
+#endif
+}
+
+template <typename ExpectedType>
+struct ObjCTypeCastTraits {
+public:
+ static bool isType(id object) { return [object isKindOfClass:[ExpectedType class]]; }
+
+ template <typename ArgType>
+ static bool isType(const ArgType *object) { return [object isKindOfClass:[ExpectedType class]]; }
+};
+
+template <typename ExpectedType, typename ArgType>
+inline bool is_objc(ArgType * source)
+{
+ return source && ObjCTypeCastTraits<ExpectedType>::isType(source);
+}
+
+template<typename T> inline T *checked_objc_cast(id object)
+{
+ if (!object)
+ return nullptr;
+
+ if (!is_objc<T>(object))
+ WTFCrash();
+
+ return reinterpret_cast<T*>(object);
+}
+
+template<typename T, typename U> inline T *checked_objc_cast(U *object)
+{
+ if (!object)
+ return nullptr;
+
+ if (!is_objc<T>(object))
+ WTFCrash();
+
+ return static_cast<T*>(object);
+}
+
+template<typename T, typename U> RetainPtr<T> dynamic_objc_cast(RetainPtr<U>&& object)
+{
+ if (!is_objc<T>(object.get()))
+ return nullptr;
+ return adoptNS(static_cast<T*>(object.leakRef()));
+}
+
+template<typename T> RetainPtr<T> dynamic_objc_cast(RetainPtr<id>&& object)
+{
+ if (!is_objc<T>(object.get()))
+ return nullptr;
+ return adoptNS(reinterpret_cast<T*>(object.leakRef()));
+}
+
+template<typename T, typename U> RetainPtr<T> dynamic_objc_cast(const RetainPtr<U>& object)
+{
+ if (!is_objc<T>(object.get()))
+ return nullptr;
+ return static_cast<T*>(object.get());
}
+template<typename T> RetainPtr<T> dynamic_objc_cast(const RetainPtr<id>& object)
+{
+ if (!is_objc<T>(object.get()))
+ return nullptr;
+ return reinterpret_cast<T*>(object.get());
+}
+
+template<typename T> T *dynamic_objc_cast(NSObject *object)
+{
+ if (!is_objc<T>(object))
+ return nullptr;
+ return static_cast<T*>(object);
+}
+
+template<typename T> T *dynamic_objc_cast(id object)
+{
+ if (!is_objc<T>(object))
+ return nullptr;
+ return reinterpret_cast<T*>(object);
+}
+
+template <typename> struct CFTypeTrait;
+
+template<typename T> T dynamic_cf_cast(CFTypeRef object)
+{
+ if (!object)
+ return nullptr;
+
+ if (CFGetTypeID(object) != CFTypeTrait<T>::typeID())
+ return nullptr;
+
+ return static_cast<T>(const_cast<CF_BRIDGED_TYPE(id) void*>(object));
+}
+
+template<typename T> T checked_cf_cast(CFTypeRef object)
+{
+ if (!object)
+ return nullptr;
+
+ if (CFGetTypeID(object) != CFTypeTrait<T>::typeID())
+ WTFCrash();
+
+ return static_cast<T>(const_cast<CF_BRIDGED_TYPE(id) void*>(object));
+}
+
+template<typename T, typename U> RetainPtr<T> dynamic_cf_cast(RetainPtr<U>&& object)
+{
+ if (!object)
+ return nullptr;
+
+ if (CFGetTypeID(object.get()) != CFTypeTrait<T>::typeID())
+ return nullptr;
+
+ return adoptCF(static_cast<T>(const_cast<CF_BRIDGED_TYPE(id) void*>(object.leakRef())));
+}
+
+} // namespace WTF
+
+#define WTF_DECLARE_CF_TYPE_TRAIT(ClassName) \
+template <> \
+struct WTF::CFTypeTrait<ClassName##Ref> { \
+ static inline CFTypeID typeID(void) { return ClassName##GetTypeID(); } \
+};
+
+WTF_DECLARE_CF_TYPE_TRAIT(CFArray);
+WTF_DECLARE_CF_TYPE_TRAIT(CFDictionary);
+
+#define WTF_DECLARE_CF_MUTABLE_TYPE_TRAIT(ClassName, MutableClassName) \
+template <> \
+struct WTF::CFTypeTrait<MutableClassName##Ref> { \
+ static inline CFTypeID typeID(void) { return ClassName##GetTypeID(); } \
+};
+
+WTF_DECLARE_CF_MUTABLE_TYPE_TRAIT(CFArray, CFMutableArray);
+WTF_DECLARE_CF_MUTABLE_TYPE_TRAIT(CFDictionary, CFMutableDictionary);
+
using WTF::RetainPtr;
using WTF::adoptNS;
using WTF::adoptCF;
using WTF::retainPtr;
using WTF::downcast;
using WTF::bridge_cast;
+using WTF::bridge_id_cast;
+using WTF::is_objc;
+using WTF::checked_objc_cast;
+using WTF::dynamic_objc_cast;
+using WTF::checked_cf_cast;
+using WTF::dynamic_cf_cast;
\ No newline at end of file
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
index 3ea701d23b518..55e795ee9a598 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -375,6 +375,33 @@ bool baz(NSObject *obj) {
}
}
+namespace ptr_conversion {
+
+SomeObj *provide_obj();
+
+void dobjc(SomeObj* obj) {
+ [dynamic_objc_cast<OtherObj>(obj) doMoreWork:nil];
+}
+
+void cobjc(SomeObj* obj) {
+ [checked_objc_cast<OtherObj>(obj) doMoreWork:nil];
+}
+
+unsigned dcf(CFTypeRef obj) {
+ return CFArrayGetCount(dynamic_cf_cast<CFArrayRef>(obj));
+}
+
+unsigned ccf(CFTypeRef obj) {
+ return CFArrayGetCount(checked_cf_cast<CFArrayRef>(obj));
+}
+
+void some_function(id);
+void idcf(CFTypeRef obj) {
+ some_function(bridge_id_cast(obj));
+}
+
+} // ptr_conversion
+
@interface TestObject : NSObject
- (void)doWork:(NSString *)msg, ...;
- (void)doWorkOnSelf;
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm
index 21ef6a5dca519..0a3d9e54fa024 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm
@@ -359,6 +359,33 @@ void bar() {
}
}
+namespace ptr_conversion {
+
+SomeObj *provide_obj();
+
+void dobjc(SomeObj* obj) {
+ if (auto *otherObj = dynamic_objc_cast<OtherObj>(obj))
+ [otherObj doMoreWork:nil];
+}
+
+void cobjc(SomeObj* obj) {
+ auto *otherObj = checked_objc_cast<OtherObj>(obj);
+ [otherObj doMoreWork:nil];
+}
+
+unsigned dcf(CFTypeRef obj) {
+ if (CFArrayRef array = dynamic_cf_cast<CFArrayRef>(obj))
+ return CFArrayGetCount(array);
+ return 0;
+}
+
+unsigned ccf(CFTypeRef obj) {
+ CFArrayRef array = checked_cf_cast<CFArrayRef>(obj);
+ return CFArrayGetCount(array);
+}
+
+} // ptr_conversion
+
bool doMoreWorkOpaque(OtherObj*);
@implementation OtherObj
More information about the cfe-commits
mailing list