-
Notifications
You must be signed in to change notification settings - Fork 13.3k
[WebKit Checkers] Recognize Objective-C and CF pointer conversion functions. #132784
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…ctions. Recognize dynamic_objc_cast, checked_objc_cast, dynamic_cf_cast, and checked_cf_cast.
@llvm/pr-subscribers-clang-static-analyzer-1 @llvm/pr-subscribers-clang Author: Ryosuke Niwa (rniwa) ChangesRecognize 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:
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
|
using WTF::checked_objc_cast; | ||
using WTF::dynamic_objc_cast; | ||
using WTF::checked_cf_cast; | ||
using WTF::dynamic_cf_cast; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this missing bridge_id_cast
? I also don't seem to find any test case using bridge_id_cast
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh yeah, indeed, I'm missing bridge_id_cast
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a new test case for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Thanks for the review! |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/144/builds/21412 Here is the relevant piece of the build log for the reference
|
Oops, fixing this in #133341. |
…ctions. (llvm#132784) Recognize dynamic_objc_cast, checked_objc_cast, dynamic_cf_cast, and checked_cf_cast.
…ard-decl-checker.mm after llvm/llvm-project#132784. (#133341)
…ctions. (llvm#132784) Recognize dynamic_objc_cast, checked_objc_cast, dynamic_cf_cast, and checked_cf_cast.
Recognize dynamic_objc_cast, checked_objc_cast, dynamic_cf_cast, and checked_cf_cast.