diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index 15d7557ae6af9..d942578dd7596 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -3633,12 +3633,13 @@ See `WebKit Guidelines for Safer C++ Programming , Documentation; def RetainPtrCtorAdoptChecker : Checker<"RetainPtrCtorAdoptChecker">, - HelpText<"Check for correct use of RetainPtr constructor, adoptNS, and adoptCF">, + HelpText<"Check for correct use of RetainPtr/OSObjectPtr constructor, adoptNS, adoptCF, and adoptOSObject">, Documentation; } // end alpha.webkit diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp index b629de3254ed3..00a1b8b6e7e89 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp @@ -115,7 +115,7 @@ bool tryToFindPtrOrigin( if (auto *Callee = operatorCall->getDirectCallee()) { auto ClsName = safeGetName(Callee->getParent()); if (isRefType(ClsName) || isCheckedPtr(ClsName) || - isRetainPtr(ClsName) || ClsName == "unique_ptr" || + isRetainPtrOrOSPtr(ClsName) || ClsName == "unique_ptr" || ClsName == "UniqueRef" || ClsName == "WeakPtr" || ClsName == "WeakRef") { if (operatorCall->getNumArgs() == 1) { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp index 9deb1845a2f1a..d8539eaaac49f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp @@ -162,7 +162,7 @@ class ForwardDeclChecker : public Checker> { // Ref-counted smartpointers actually have raw-pointer to uncounted type as // a member but we trust them to handle it correctly. auto R = llvm::dyn_cast_or_null(RD); - if (!R || isRefCounted(R) || isCheckedPtr(R) || isRetainPtr(R)) + if (!R || isRefCounted(R) || isCheckedPtr(R) || isRetainPtrOrOSPtr(R)) return; for (auto *Member : RD->fields()) { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 90b2343b4be77..e5c74bbaf3d6b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -129,8 +129,9 @@ bool isRefType(const std::string &Name) { Name == "RefPtr" || Name == "RefPtrAllowingPartiallyDestroyed"; } -bool isRetainPtr(const std::string &Name) { - return Name == "RetainPtr" || Name == "RetainPtrArc"; +bool isRetainPtrOrOSPtr(const std::string &Name) { + return Name == "RetainPtr" || Name == "RetainPtrArc" || + Name == "OSObjectPtr" || Name == "OSObjectPtrArc"; } bool isCheckedPtr(const std::string &Name) { @@ -138,7 +139,7 @@ bool isCheckedPtr(const std::string &Name) { } bool isSmartPtrClass(const std::string &Name) { - return isRefType(Name) || isCheckedPtr(Name) || isRetainPtr(Name) || + return isRefType(Name) || isCheckedPtr(Name) || isRetainPtrOrOSPtr(Name) || Name == "WeakPtr" || Name == "WeakPtrFactory" || Name == "WeakPtrFactoryWithBitField" || Name == "WeakPtrImplBase" || Name == "WeakPtrImplBaseSingleThread" || Name == "ThreadSafeWeakPtr" || @@ -166,15 +167,17 @@ bool isCtorOfCheckedPtr(const clang::FunctionDecl *F) { return isCheckedPtr(safeGetName(F)); } -bool isCtorOfRetainPtr(const clang::FunctionDecl *F) { +bool isCtorOfRetainPtrOrOSPtr(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) { - return isCtorOfRefCounted(F) || isCtorOfCheckedPtr(F) || isCtorOfRetainPtr(F); + return isCtorOfRefCounted(F) || isCtorOfCheckedPtr(F) || + isCtorOfRetainPtrOrOSPtr(F); } template @@ -198,8 +201,8 @@ bool isRefOrCheckedPtrType(const clang::QualType T) { T, [](auto Name) { return isRefType(Name) || isCheckedPtr(Name); }); } -bool isRetainPtrType(const clang::QualType T) { - return isPtrOfType(T, [](auto Name) { return isRetainPtr(Name); }); +bool isRetainPtrOrOSPtrType(const clang::QualType T) { + return isPtrOfType(T, [](auto Name) { return isRetainPtrOrOSPtr(Name); }); } bool isOwnerPtrType(const clang::QualType T) { @@ -273,7 +276,7 @@ bool RetainTypeChecker::isUnretained(const QualType QT, bool ignoreARC) { std::optional isUnretained(const QualType T, bool IsARCEnabled) { if (auto *Subst = dyn_cast(T)) { if (auto *Decl = Subst->getAssociatedDecl()) { - if (isRetainPtr(safeGetName(Decl))) + if (isRetainPtrOrOSPtr(safeGetName(Decl))) return false; } } @@ -373,7 +376,7 @@ std::optional isGetterOfSafePtr(const CXXMethodDecl *M) { method == "impl")) return true; - if (isRetainPtr(className) && method == "get") + if (isRetainPtrOrOSPtr(className) && method == "get") return true; // Ref -> T conversion @@ -394,7 +397,7 @@ std::optional isGetterOfSafePtr(const CXXMethodDecl *M) { } } - if (isRetainPtr(className)) { + if (isRetainPtrOrOSPtr(className)) { if (auto *maybeRefToRawOperator = dyn_cast(M)) { auto QT = maybeRefToRawOperator->getConversionType(); auto *T = QT.getTypePtrOrNull(); @@ -425,10 +428,10 @@ bool isCheckedPtr(const CXXRecordDecl *R) { return false; } -bool isRetainPtr(const CXXRecordDecl *R) { +bool isRetainPtrOrOSPtr(const CXXRecordDecl *R) { assert(R); if (auto *TmplR = R->getTemplateInstantiationPattern()) - return isRetainPtr(safeGetName(TmplR)); + return isRetainPtrOrOSPtr(safeGetName(TmplR)); return false; } diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h index d2095d07e1434..8300a6c051f3e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h @@ -57,7 +57,7 @@ bool isRefCounted(const clang::CXXRecordDecl *Class); bool isCheckedPtr(const clang::CXXRecordDecl *Class); /// \returns true if \p Class is a RetainPtr, false if not. -bool isRetainPtr(const clang::CXXRecordDecl *Class); +bool isRetainPtrOrOSPtr(const clang::CXXRecordDecl *Class); /// \returns true if \p Class is a smart pointer (RefPtr, WeakPtr, etc...), /// false if not. @@ -116,7 +116,7 @@ std::optional isUnsafePtr(const QualType T, bool IsArcEnabled); bool isRefOrCheckedPtrType(const clang::QualType T); /// \returns true if \p T is a RetainPtr, false if not. -bool isRetainPtrType(const clang::QualType T); +bool isRetainPtrOrOSPtrType(const clang::QualType T); /// \returns true if \p T is a RefPtr, Ref, CheckedPtr, CheckedRef, or /// unique_ptr, false if not. @@ -141,7 +141,7 @@ bool isRefType(const std::string &Name); bool isCheckedPtr(const std::string &Name); /// \returns true if \p Name is RetainPtr or its variant, false if not. -bool isRetainPtr(const std::string &Name); +bool isRetainPtrOrOSPtr(const std::string &Name); /// \returns true if \p Name is a smart pointer type name, false if not. bool isSmartPtrClass(const std::string &Name); diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp index e80f1749d595b..b841caf8c74b1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp @@ -465,11 +465,11 @@ class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker { } bool isSafePtr(const CXXRecordDecl *Record) const final { - return isRetainPtr(Record); + return isRetainPtrOrOSPtr(Record); } bool isSafePtrType(const QualType type) const final { - return isRetainPtrType(type); + return isRetainPtrOrOSPtrType(type); } bool isSafeExpr(const Expr *E) const final { diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp index 03eeb9999c4dd..2355584435f6a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp @@ -490,7 +490,7 @@ class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker { } virtual bool isPtrType(const std::string &Name) const final { - return isRetainPtr(Name); + return isRetainPtrOrOSPtr(Name); } const char *ptrKind(QualType QT) const final { return "unretained"; } diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp index f4f6e28c97ea1..dd9701fbbb017 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp @@ -434,10 +434,10 @@ class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker { return RTC->isUnretained(T); } bool isSafePtr(const CXXRecordDecl *Record) const final { - return isRetainPtr(Record); + return isRetainPtrOrOSPtr(Record); } bool isSafePtrType(const QualType type) const final { - return isRetainPtrType(type); + return isRetainPtrOrOSPtrType(type); } bool isSafeExpr(const Expr *E) const final { return ento::cocoa::isCocoaObjectRef(E->getType()) && diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefMemberChecker.cpp index 8faf6a219450a..a97a37f85e96c 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(Desugared); - if (!ObjCType) - return nullptr; - return ObjCType->getDecl(); + if (auto *ObjCType = dyn_cast(Desugared)) + return ObjCType->getDecl(); + if (auto *ObjCType = dyn_cast(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..e1f9a77f5a5ca 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp @@ -67,7 +67,7 @@ class RetainPtrCtorAdoptChecker } bool TraverseClassTemplateDecl(ClassTemplateDecl *CTD) { - if (isRetainPtr(safeGetName(CTD))) + if (isRetainPtrOrOSPtr(safeGetName(CTD))) return true; // Skip the contents of RetainPtr. return Base::TraverseClassTemplateDecl(CTD); } @@ -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 { @@ -304,7 +305,7 @@ class RetainPtrCtorAdoptChecker if (!Cls) return; - if (!isRetainPtr(safeGetName(Cls)) || !CE->getNumArgs()) + if (!isRetainPtrOrOSPtr(safeGetName(Cls)) || !CE->getNumArgs()) return; // Ignore RetainPtr construction inside adoptNS, adoptCF, and retainPtr. @@ -491,7 +492,7 @@ class RetainPtrCtorAdoptChecker return IsOwnedResult::NotOwned; if (auto *DRE = dyn_cast(E)) { auto QT = DRE->getType(); - if (isRetainPtrType(QT)) + if (isRetainPtrOrOSPtrType(QT)) return IsOwnedResult::NotOwned; QT = QT.getCanonicalType(); if (RTC.isUnretained(QT, true /* ignoreARC */)) @@ -530,12 +531,13 @@ class RetainPtrCtorAdoptChecker if (auto *CD = dyn_cast(MD)) { auto QT = CD->getConversionType().getCanonicalType(); auto *ResultType = QT.getTypePtrOrNull(); - if (isRetainPtr(safeGetName(Cls)) && ResultType && + if (isRetainPtrOrOSPtr(safeGetName(Cls)) && ResultType && (ResultType->isPointerType() || ResultType->isReferenceType() || ResultType->isObjCObjectPointerType())) return IsOwnedResult::NotOwned; } - if (safeGetName(MD) == "leakRef" && isRetainPtr(safeGetName(Cls))) + if (safeGetName(MD) == "leakRef" && + isRetainPtrOrOSPtr(safeGetName(Cls))) return IsOwnedResult::Owned; } } @@ -553,7 +555,7 @@ class RetainPtrCtorAdoptChecker continue; } auto RetType = Callee->getReturnType(); - if (isRetainPtrType(RetType)) + if (isRetainPtrOrOSPtrType(RetType)) return IsOwnedResult::NotOwned; if (isCreateOrCopyFunction(Callee)) { CreateOrCopyFnCall.insert(CE); 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 struct remove_reference { - typedef T type; -}; - -template struct remove_reference { - typedef T type; -}; - -template typename remove_reference::type&& move(T&& t); - -} // namespace std - RefCountableAndCheckable* makeObj(); CheckedRef makeObjChecked(); void someFunction(RefCountableAndCheckable*); diff --git a/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm b/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm index 084b47322d7f9..104b555c1c41d 100644 --- a/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm +++ b/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm @@ -4,20 +4,6 @@ #include "objc-mock-types.h" #include "mock-system-header.h" -namespace std { - -template struct remove_reference { - typedef T type; -}; - -template struct remove_reference { - typedef T type; -}; - -template typename remove_reference::type&& move(T&& t); - -} // namespace std - typedef struct OpaqueJSString * JSStringRef; class Obj; diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index a03d31870ee0d..a49faa1d25336 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -1,3 +1,22 @@ +#ifndef std_move +#define std_move + +namespace std { + +template struct remove_reference { +typedef T type; +}; + +template struct remove_reference { +typedef T type; +}; + +template typename remove_reference::type&& move(T&& t); + +} + +#endif + #ifndef mock_types_1103988513531 #define mock_types_1103988513531 diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h index 09b303961fd6a..854742b82a2d4 100644 --- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h @@ -1,3 +1,22 @@ +#ifndef std_move +#define std_move + +namespace std { + +template struct remove_reference { +typedef T type; +}; + +template struct remove_reference { +typedef T type; +}; + +template typename remove_reference::type&& move(T&& t); + +} + +#endif + @class NSString; @class NSArray; @class NSMutableArray; @@ -157,6 +176,39 @@ __attribute__((objc_root_class)) - (void)doMoreWork:(OtherObj *)other; @end +@protocol OS_dispatch_queue +@end + +typedef NSObject *dispatch_queue_t; + +@protocol OS_dispatch_queue_attr +@end + +typedef NSObject *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 +void swap(StorageType& a, StorageType& b) +{ + StorageType temp = static_cast(a); + a = static_cast(b); + b = static_cast(temp); +} + +template +StorageType exchange(StorageType& obj, ValueType& value) +{ + StorageType returnValue = static_cast(obj); + obj = static_cast(value); + return returnValue; +} + +} + namespace WTF { void WTFCrash(void); @@ -293,6 +345,117 @@ template inline RetainPtr retainPtr(T* ptr) return ptr; } +template class OSObjectPtr; +template OSObjectPtr adoptOSObject(T); + +template static inline void retainOSObject(T ptr) +{ +#if !__has_feature(objc_arc) + [ptr retain]; +#endif +} + +template static inline void releaseOSObject(T ptr) +{ +#if !__has_feature(objc_arc) + [ptr release]; +#endif +} + +template 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); + +private: + struct AdoptOSObject { }; + OSObjectPtr(AdoptOSObject, T ptr) + : m_ptr(std::move(ptr)) + { + } + + T m_ptr; +}; + +template inline OSObjectPtr adoptOSObject(T ptr) +{ + return OSObjectPtr { typename OSObjectPtr::AdoptOSObject { }, std::move(ptr) }; +} + inline NSObject *bridge_cast(CFTypeRef object) { return (__bridge NSObject *)object; @@ -455,6 +618,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 T&& forward(T& arg); -template -T&& move( T&& t ); - template 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 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 { provide_cf() }.get()); + consume_retainable(provide(), 1, provide_cf(), RetainPtr { 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 { provide() }.get(), 1, RetainPtr { provide_cf() }.get()); } } namespace retained { - RetainPtr provide_obj() { return RetainPtr{}; } - void consume_obj(RetainPtr) {} - - RetainPtr provide_cf() { return CFMutableArrayRef{}; } - void consume_cf(RetainPtr) {} - + RetainPtr provide_obj(); + RetainPtr provide_cf(); + OSObjectPtr 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 ptr; ptr = provide(); + OSObjectPtr objPtr; + objPtr = provide_dispatch(); } } @@ -287,8 +335,10 @@ void foo() { namespace call_with_ptr_on_ref { RetainPtr provideProtected(); RetainPtr provideProtectedCF(); + OSObjectPtr 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(); } + 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 { provide() }.get(), RetainPtr { provide_cf() }.get()]; + // expected-warning@-3{{Call argument is unretained and unsafe}} + [self doWork:@"hello", RetainPtr { provide() }.get(), RetainPtr { 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 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 +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&&); @@ -238,22 +272,25 @@ void callFunction(WTF::Function&& 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 delegate; + OSObjectPtr 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 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 +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&&); @@ -259,24 +308,29 @@ void callFunction(WTF::Function&& 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 delegate; + OSObjectPtr 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 foo; + RetainPtr foo = provide(); { SomeObj *bar = foo.get(); } } void foo2() { - RetainPtr foo; + RetainPtr 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 foo; + RetainPtr foo = provide(); { { SomeObj *bar = foo.get(); } } @@ -57,11 +66,35 @@ void foo3() { void foo4() { { - RetainPtr foo; + RetainPtr foo = provide(); { SomeObj *bar = foo.get(); } } } +CFMutableArrayRef provide_cf(); +void foo5() { + RetainPtr 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 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 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 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 + template struct FooTmpl { T* x; S y; -// expected-warning@-1{{Member variable 'y' in 'members::FooTmpl' is a raw pointer to retainable type}} +// expected-warning@-1{{Member variable 'y' in 'members::FooTmpl *>' is a raw pointer to retainable type}} + R z; }; - void forceTmplToInstantiate(FooTmpl) {} + void forceTmplToInstantiate(FooTmpl) {} struct [[clang::suppress]] FooSuppressed { private: @@ -51,16 +55,19 @@ void forceTmplToInstantiate(FooTmpl) {} // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::List' contains a retainable type 'CFMutableArrayRef'}} }; - template + template struct TemplateList { + T* elements1; S* elements2; - // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::TemplateList' contains a raw pointer to retainable type '__CFArray'}} + // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::TemplateList *>' contains a raw pointer to retainable type '__CFArray'}} + R* elements3; }; - TemplateList list; + TemplateList list; struct SafeList { RetainPtr* elements1; RetainPtr* elements2; + OSObjectPtr 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 b; // No warning. + OSObjectPtr b2; +// No warning. public: SomeObj* c = nullptr; // expected-warning@-1{{Member variable 'c' in 'members::Foo' is a raw pointer to retainable type}} RetainPtr d; + OSObjectPtr d2; CFMutableArrayRef e = nullptr; // expected-warning@-1{{Member variable 'e' in 'members::Foo' is a retainable type 'CFMutableArrayRef'}} + }; - template + template struct FooTmpl { T* a; -// expected-warning@-1{{Member variable 'a' in 'members::FooTmpl' is a raw pointer to retainable type}} +// expected-warning@-1{{Member variable 'a' in 'members::FooTmpl *>' is a raw pointer to retainable type}} S b; -// expected-warning@-1{{Member variable 'b' in 'members::FooTmpl' is a raw pointer to retainable type}} +// expected-warning@-1{{Member variable 'b' in 'members::FooTmpl *>' is a raw pointer to retainable type}} + R c; +// expected-warning@-1{{Member variable 'c' in 'members::FooTmpl *>' is a raw pointer to retainable type}} }; - void forceTmplToInstantiate(FooTmpl) {} + void forceTmplToInstantiate(FooTmpl) {} struct [[clang::suppress]] FooSuppressed { private: @@ -54,6 +62,8 @@ void forceTmplToInstantiate(FooTmpl) {} RetainPtr 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 @@ -72,16 +82,20 @@ void forceTmplToInstantiate(FooTempl) {} // 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 + template struct TemplateList { T** elements1; - // expected-warning@-1{{Member variable 'elements1' in 'ptr_to_ptr_to_retained::TemplateList' contains a raw pointer to retainable type 'SomeObj'}} + // expected-warning@-1{{Member variable 'elements1' in 'ptr_to_ptr_to_retained::TemplateList *>' contains a raw pointer to retainable type 'SomeObj'}} S* elements2; - // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::TemplateList' contains a raw pointer to retainable type '__CFArray'}} + // expected-warning@-1{{Member variable 'elements2' in 'ptr_to_ptr_to_retained::TemplateList *>' contains a raw pointer to retainable type '__CFArray'}} + R* elements3; + // expected-warning@-1{{Member variable 'elements3' in 'ptr_to_ptr_to_retained::TemplateList *>' contains a raw pointer to retainable type 'NSObject'}} }; - TemplateList list; + TemplateList list; struct SafeList { RetainPtr* elements1; @@ -92,20 +106,24 @@ void forceTmplToInstantiate(FooTempl) {} @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