Skip to content

Commit a285be3

Browse files
authored
[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.
1 parent c676eb7 commit a285be3

File tree

5 files changed

+218
-6
lines changed

5 files changed

+218
-6
lines changed

clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,10 @@ bool isPtrConversion(const FunctionDecl *F) {
418418
FunctionName == "dynamicDowncast" || FunctionName == "downcast" ||
419419
FunctionName == "checkedDowncast" ||
420420
FunctionName == "uncheckedDowncast" || FunctionName == "bitwise_cast" ||
421-
FunctionName == "bridge_cast")
421+
FunctionName == "bridge_cast" || FunctionName == "bridge_id_cast" ||
422+
FunctionName == "dynamic_cf_cast" || FunctionName == "checked_cf_cast" ||
423+
FunctionName == "dynamic_objc_cast" ||
424+
FunctionName == "checked_objc_cast")
422425
return true;
423426

424427
return false;

clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -286,15 +286,12 @@ class RawPtrRefCallArgsChecker
286286
overloadedOperatorType == OO_PipePipe)
287287
return true;
288288

289-
if (isCtorOfSafePtr(Callee))
289+
if (isCtorOfSafePtr(Callee) || isPtrConversion(Callee))
290290
return true;
291291

292292
auto name = safeGetName(Callee);
293293
if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
294-
name == "dynamicDowncast" || name == "downcast" ||
295-
name == "checkedDowncast" || name == "uncheckedDowncast" ||
296-
name == "bitwise_cast" || name == "is" || name == "equal" ||
297-
name == "hash" || name == "isType" ||
294+
name == "is" || name == "equal" || name == "hash" || name == "isType" ||
298295
// FIXME: Most/all of these should be implemented via attributes.
299296
name == "equalIgnoringASCIICase" ||
300297
name == "equalIgnoringASCIICaseCommon" ||

clang/test/Analysis/Checkers/WebKit/objc-mock-types.h

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#define CF_BRIDGED_TYPE(T) __attribute__((objc_bridge(T)))
66
#define CF_BRIDGED_MUTABLE_TYPE(T) __attribute__((objc_bridge_mutable(T)))
77
typedef CF_BRIDGED_TYPE(id) void * CFTypeRef;
8+
typedef unsigned long long CFTypeID;
89
typedef signed char BOOL;
910
typedef unsigned char Boolean;
1011
typedef signed long CFIndex;
@@ -21,6 +22,8 @@ typedef struct CF_BRIDGED_MUTABLE_TYPE(CFRunLoopRef) __CFRunLoop * CFRunLoopRef;
2122

2223
extern const CFAllocatorRef kCFAllocatorDefault;
2324
typedef struct _NSZone NSZone;
25+
CFTypeID CFGetTypeID(CFTypeRef cf);
26+
CFTypeID CFArrayGetTypeID();
2427
CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity);
2528
extern void CFArrayAppendValue(CFMutableArrayRef theArray, const void *value);
2629
CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues);
@@ -29,6 +32,7 @@ CFIndex CFArrayGetCount(CFArrayRef theArray);
2932
typedef const struct CF_BRIDGED_TYPE(NSDictionary) __CFDictionary * CFDictionaryRef;
3033
typedef struct CF_BRIDGED_MUTABLE_TYPE(NSMutableDictionary) __CFDictionary * CFMutableDictionaryRef;
3134

35+
CFTypeID CFDictionaryGetTypeID();
3236
CFDictionaryRef CFDictionaryCreate(CFAllocatorRef allocator, const void **keys, const void **values, CFIndex numValues);
3337
CFDictionaryRef CFDictionaryCreateCopy(CFAllocatorRef allocator, CFDictionaryRef theDict);
3438
CFDictionaryRef CFDictionaryCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFDictionaryRef theDict);
@@ -135,6 +139,8 @@ __attribute__((objc_root_class))
135139

136140
namespace WTF {
137141

142+
void WTFCrash(void);
143+
138144
template<typename T> class RetainPtr;
139145
template<typename T> RetainPtr<T> adoptNS(T*);
140146
template<typename T> RetainPtr<T> adoptCF(T);
@@ -273,11 +279,163 @@ inline CFTypeRef bridge_cast(NSObject *object)
273279
return (__bridge CFTypeRef)object;
274280
}
275281

282+
inline id bridge_id_cast(CFTypeRef object)
283+
{
284+
return (__bridge id)object;
285+
}
286+
287+
inline RetainPtr<id> bridge_id_cast(RetainPtr<CFTypeRef>&& object)
288+
{
289+
#if __has_feature(objc_arc)
290+
return adoptNS((__bridge_transfer id)object.leakRef());
291+
#else
292+
return adoptNS((__bridge id)object.leakRef());
293+
#endif
294+
}
295+
296+
template <typename ExpectedType>
297+
struct ObjCTypeCastTraits {
298+
public:
299+
static bool isType(id object) { return [object isKindOfClass:[ExpectedType class]]; }
300+
301+
template <typename ArgType>
302+
static bool isType(const ArgType *object) { return [object isKindOfClass:[ExpectedType class]]; }
303+
};
304+
305+
template <typename ExpectedType, typename ArgType>
306+
inline bool is_objc(ArgType * source)
307+
{
308+
return source && ObjCTypeCastTraits<ExpectedType>::isType(source);
309+
}
310+
311+
template<typename T> inline T *checked_objc_cast(id object)
312+
{
313+
if (!object)
314+
return nullptr;
315+
316+
if (!is_objc<T>(object))
317+
WTFCrash();
318+
319+
return reinterpret_cast<T*>(object);
320+
}
321+
322+
template<typename T, typename U> inline T *checked_objc_cast(U *object)
323+
{
324+
if (!object)
325+
return nullptr;
326+
327+
if (!is_objc<T>(object))
328+
WTFCrash();
329+
330+
return static_cast<T*>(object);
331+
}
332+
333+
template<typename T, typename U> RetainPtr<T> dynamic_objc_cast(RetainPtr<U>&& object)
334+
{
335+
if (!is_objc<T>(object.get()))
336+
return nullptr;
337+
return adoptNS(static_cast<T*>(object.leakRef()));
338+
}
339+
340+
template<typename T> RetainPtr<T> dynamic_objc_cast(RetainPtr<id>&& object)
341+
{
342+
if (!is_objc<T>(object.get()))
343+
return nullptr;
344+
return adoptNS(reinterpret_cast<T*>(object.leakRef()));
345+
}
346+
347+
template<typename T, typename U> RetainPtr<T> dynamic_objc_cast(const RetainPtr<U>& object)
348+
{
349+
if (!is_objc<T>(object.get()))
350+
return nullptr;
351+
return static_cast<T*>(object.get());
276352
}
277353

354+
template<typename T> RetainPtr<T> dynamic_objc_cast(const RetainPtr<id>& object)
355+
{
356+
if (!is_objc<T>(object.get()))
357+
return nullptr;
358+
return reinterpret_cast<T*>(object.get());
359+
}
360+
361+
template<typename T> T *dynamic_objc_cast(NSObject *object)
362+
{
363+
if (!is_objc<T>(object))
364+
return nullptr;
365+
return static_cast<T*>(object);
366+
}
367+
368+
template<typename T> T *dynamic_objc_cast(id object)
369+
{
370+
if (!is_objc<T>(object))
371+
return nullptr;
372+
return reinterpret_cast<T*>(object);
373+
}
374+
375+
template <typename> struct CFTypeTrait;
376+
377+
template<typename T> T dynamic_cf_cast(CFTypeRef object)
378+
{
379+
if (!object)
380+
return nullptr;
381+
382+
if (CFGetTypeID(object) != CFTypeTrait<T>::typeID())
383+
return nullptr;
384+
385+
return static_cast<T>(const_cast<CF_BRIDGED_TYPE(id) void*>(object));
386+
}
387+
388+
template<typename T> T checked_cf_cast(CFTypeRef object)
389+
{
390+
if (!object)
391+
return nullptr;
392+
393+
if (CFGetTypeID(object) != CFTypeTrait<T>::typeID())
394+
WTFCrash();
395+
396+
return static_cast<T>(const_cast<CF_BRIDGED_TYPE(id) void*>(object));
397+
}
398+
399+
template<typename T, typename U> RetainPtr<T> dynamic_cf_cast(RetainPtr<U>&& object)
400+
{
401+
if (!object)
402+
return nullptr;
403+
404+
if (CFGetTypeID(object.get()) != CFTypeTrait<T>::typeID())
405+
return nullptr;
406+
407+
return adoptCF(static_cast<T>(const_cast<CF_BRIDGED_TYPE(id) void*>(object.leakRef())));
408+
}
409+
410+
} // namespace WTF
411+
412+
#define WTF_DECLARE_CF_TYPE_TRAIT(ClassName) \
413+
template <> \
414+
struct WTF::CFTypeTrait<ClassName##Ref> { \
415+
static inline CFTypeID typeID(void) { return ClassName##GetTypeID(); } \
416+
};
417+
418+
WTF_DECLARE_CF_TYPE_TRAIT(CFArray);
419+
WTF_DECLARE_CF_TYPE_TRAIT(CFDictionary);
420+
421+
#define WTF_DECLARE_CF_MUTABLE_TYPE_TRAIT(ClassName, MutableClassName) \
422+
template <> \
423+
struct WTF::CFTypeTrait<MutableClassName##Ref> { \
424+
static inline CFTypeID typeID(void) { return ClassName##GetTypeID(); } \
425+
};
426+
427+
WTF_DECLARE_CF_MUTABLE_TYPE_TRAIT(CFArray, CFMutableArray);
428+
WTF_DECLARE_CF_MUTABLE_TYPE_TRAIT(CFDictionary, CFMutableDictionary);
429+
278430
using WTF::RetainPtr;
279431
using WTF::adoptNS;
280432
using WTF::adoptCF;
281433
using WTF::retainPtr;
282434
using WTF::downcast;
283435
using WTF::bridge_cast;
436+
using WTF::bridge_id_cast;
437+
using WTF::is_objc;
438+
using WTF::checked_objc_cast;
439+
using WTF::dynamic_objc_cast;
440+
using WTF::checked_cf_cast;
441+
using WTF::dynamic_cf_cast;

clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,33 @@ bool baz(NSObject *obj) {
375375
}
376376
}
377377

378+
namespace ptr_conversion {
379+
380+
SomeObj *provide_obj();
381+
382+
void dobjc(SomeObj* obj) {
383+
[dynamic_objc_cast<OtherObj>(obj) doMoreWork:nil];
384+
}
385+
386+
void cobjc(SomeObj* obj) {
387+
[checked_objc_cast<OtherObj>(obj) doMoreWork:nil];
388+
}
389+
390+
unsigned dcf(CFTypeRef obj) {
391+
return CFArrayGetCount(dynamic_cf_cast<CFArrayRef>(obj));
392+
}
393+
394+
unsigned ccf(CFTypeRef obj) {
395+
return CFArrayGetCount(checked_cf_cast<CFArrayRef>(obj));
396+
}
397+
398+
void some_function(id);
399+
void idcf(CFTypeRef obj) {
400+
some_function(bridge_id_cast(obj));
401+
}
402+
403+
} // ptr_conversion
404+
378405
@interface TestObject : NSObject
379406
- (void)doWork:(NSString *)msg, ...;
380407
- (void)doWorkOnSelf;

clang/test/Analysis/Checkers/WebKit/unretained-local-vars.mm

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,33 @@ void bar() {
359359
}
360360
}
361361

362+
namespace ptr_conversion {
363+
364+
SomeObj *provide_obj();
365+
366+
void dobjc(SomeObj* obj) {
367+
if (auto *otherObj = dynamic_objc_cast<OtherObj>(obj))
368+
[otherObj doMoreWork:nil];
369+
}
370+
371+
void cobjc(SomeObj* obj) {
372+
auto *otherObj = checked_objc_cast<OtherObj>(obj);
373+
[otherObj doMoreWork:nil];
374+
}
375+
376+
unsigned dcf(CFTypeRef obj) {
377+
if (CFArrayRef array = dynamic_cf_cast<CFArrayRef>(obj))
378+
return CFArrayGetCount(array);
379+
return 0;
380+
}
381+
382+
unsigned ccf(CFTypeRef obj) {
383+
CFArrayRef array = checked_cf_cast<CFArrayRef>(obj);
384+
return CFArrayGetCount(array);
385+
}
386+
387+
} // ptr_conversion
388+
362389
bool doMoreWorkOpaque(OtherObj*);
363390

364391
@implementation OtherObj

0 commit comments

Comments
 (0)