llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Ryosuke Niwa (rniwa) <details> <summary>Changes</summary> Recognize dynamic_objc_cast, checked_objc_cast, dynamic_cf_cast, and checked_cf_cast. --- Full diff: https://github.com/llvm/llvm-project/pull/132784.diff 5 Files Affected: - (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp (+4-1) - (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp (+2-5) - (modified) clang/test/Analysis/Checkers/WebKit/objc-mock-types.h (+143) - (modified) clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm (+22) - (modified) clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm (+27) ``````````diff 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..a30ce2e7e7729 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,148 @@ inline CFTypeRef bridge_cast(NSObject *object) return (__bridge CFTypeRef)object; } +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::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..589182320a1af 100644 --- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm +++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm @@ -375,6 +375,28 @@ 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)); +} + +} // 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 `````````` </details> https://github.com/llvm/llvm-project/pull/132784 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits