[clang] [llvm] [alpha.webkit.UnretainedLambdaCapturesChecker] Add a WebKit checker for lambda capturing NS or CF types. (PR #128651)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Feb 24 23:51:11 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
@llvm/pr-subscribers-clang-static-analyzer-1
Author: Ryosuke Niwa (rniwa)
<details>
<summary>Changes</summary>
Add a new WebKit checker for checking that lambda captures of CF types use RetainPtr either when ARC is disabled or enabled, and those of NS types use RetainPtr when ARC is disabled.
---
Patch is 45.38 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128651.diff
10 Files Affected:
- (modified) clang/docs/analyzer/checkers.rst (+12)
- (modified) clang/include/clang/StaticAnalyzer/Checkers/Checkers.td (+4)
- (modified) clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt (+2-2)
- (renamed) clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp (+112-24)
- (modified) clang/test/Analysis/Checkers/WebKit/objc-mock-types.h (+132-14)
- (modified) clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures-decl-protects-this-crash.cpp (+2-2)
- (modified) clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp (+17-17)
- (added) clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-arc.mm (+273)
- (added) clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm (+296)
- (modified) llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn (+1-1)
``````````diff
diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index c1eedb33e74d2..57733d45167f5 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3487,6 +3487,18 @@ Raw pointers and references to an object which supports CheckedPtr or CheckedRef
See `WebKit Guidelines for Safer C++ Programming <https://github.com/WebKit/WebKit/wiki/Safer-CPP-Guidelines>`_ for details.
+alpha.webkit.UnretainedLambdaCapturesChecker
+""""""""""""""""""""""""""""""""""""""""""""
+Raw pointers and references to unretained types can't be captured in lambdas. Only RetainPtr is allowed.
+
+.. code-block:: cpp
+
+ void foo(NSObject *a, NSObject *b) {
+ [&, a](){ // warn about 'a'
+ do_something(b); // warn about 'b'
+ };
+ };
+
.. _alpha-webkit-UncountedCallArgsChecker:
alpha.webkit.UncountedCallArgsChecker
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 410f841630660..06aa083671b7a 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1766,6 +1766,10 @@ def NoUncheckedPtrMemberChecker : Checker<"NoUncheckedPtrMemberChecker">,
HelpText<"Check for no unchecked member variables.">,
Documentation<HasDocumentation>;
+def UnretainedLambdaCapturesChecker : Checker<"UnretainedLambdaCapturesChecker">,
+ HelpText<"Check unretained lambda captures.">,
+ Documentation<HasDocumentation>;
+
def UncountedCallArgsChecker : Checker<"UncountedCallArgsChecker">,
HelpText<"Check uncounted call arguments.">,
Documentation<HasDocumentation>;
diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 5910043440987..9aac200cd7370 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -128,14 +128,14 @@ add_clang_library(clangStaticAnalyzerCheckers
VLASizeChecker.cpp
ValistChecker.cpp
VirtualCallChecker.cpp
- WebKit/RawPtrRefMemberChecker.cpp
WebKit/ASTUtils.cpp
WebKit/MemoryUnsafeCastChecker.cpp
WebKit/PtrTypesSemantics.cpp
WebKit/RefCntblBaseVirtualDtorChecker.cpp
WebKit/RawPtrRefCallArgsChecker.cpp
- WebKit/UncountedLambdaCapturesChecker.cpp
+ WebKit/RawPtrRefLambdaCapturesChecker.cpp
WebKit/RawPtrRefLocalVarsChecker.cpp
+ WebKit/RawPtrRefMemberChecker.cpp
LINK_LIBS
clangAST
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
similarity index 77%
rename from clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
rename to clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
index 506442f352288..314b3dc72c8e5 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
@@ -21,15 +21,23 @@ using namespace clang;
using namespace ento;
namespace {
-class UncountedLambdaCapturesChecker
+class RawPtrRefLambdaCapturesChecker
: public Checker<check::ASTDecl<TranslationUnitDecl>> {
private:
- BugType Bug{this, "Lambda capture of uncounted variable",
- "WebKit coding guidelines"};
+ BugType Bug;
mutable BugReporter *BR = nullptr;
TrivialFunctionAnalysis TFA;
+protected:
+ mutable std::optional<RetainTypeChecker> RTC;
+
public:
+ RawPtrRefLambdaCapturesChecker(const char *description)
+ : Bug(this, description, "WebKit coding guidelines") {}
+
+ virtual std::optional<bool> isUnsafePtr(QualType) const = 0;
+ virtual const char *ptrKind(QualType QT) const = 0;
+
void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
BugReporter &BRArg) const {
BR = &BRArg;
@@ -38,7 +46,7 @@ class UncountedLambdaCapturesChecker
// visit template instantiations or lambda classes. We
// want to visit those, so we make our own RecursiveASTVisitor.
struct LocalVisitor : DynamicRecursiveASTVisitor {
- const UncountedLambdaCapturesChecker *Checker;
+ const RawPtrRefLambdaCapturesChecker *Checker;
llvm::DenseSet<const DeclRefExpr *> DeclRefExprsToIgnore;
llvm::DenseSet<const LambdaExpr *> LambdasToIgnore;
llvm::DenseSet<const ValueDecl *> ProtectedThisDecls;
@@ -46,7 +54,7 @@ class UncountedLambdaCapturesChecker
QualType ClsType;
- explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker)
+ explicit LocalVisitor(const RawPtrRefLambdaCapturesChecker *Checker)
: Checker(Checker) {
assert(Checker);
ShouldVisitTemplateInstantiations = true;
@@ -60,16 +68,23 @@ class UncountedLambdaCapturesChecker
return DynamicRecursiveASTVisitor::TraverseCXXMethodDecl(CXXMD);
}
+ bool VisitTypedefDecl(TypedefDecl *TD) override {
+ if (Checker->RTC)
+ Checker->RTC->visitTypedef(TD);
+ return true;
+ }
+
bool shouldCheckThis() {
- auto result =
- !ClsType.isNull() ? isUnsafePtr(ClsType, false) : std::nullopt;
+ auto result = !ClsType.isNull() ?
+ Checker->isUnsafePtr(ClsType) : std::nullopt;
return result && *result;
}
bool VisitLambdaExpr(LambdaExpr *L) override {
if (LambdasToIgnore.contains(L))
return true;
- Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L));
+ Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),
+ ClsType);
return true;
}
@@ -97,7 +112,8 @@ class UncountedLambdaCapturesChecker
if (!L)
return true;
LambdasToIgnore.insert(L);
- Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L));
+ Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),
+ ClsType);
return true;
}
@@ -123,7 +139,8 @@ class UncountedLambdaCapturesChecker
LambdasToIgnore.insert(L);
if (!Param->hasAttr<NoEscapeAttr>())
Checker->visitLambdaExpr(L, shouldCheckThis() &&
- !hasProtectedThis(L));
+ !hasProtectedThis(L),
+ ClsType);
}
++ArgIndex;
}
@@ -144,7 +161,8 @@ class UncountedLambdaCapturesChecker
LambdasToIgnore.insert(L);
if (!Param->hasAttr<NoEscapeAttr>() && !TreatAllArgsAsNoEscape)
Checker->visitLambdaExpr(L, shouldCheckThis() &&
- !hasProtectedThis(L));
+ !hasProtectedThis(L),
+ ClsType);
}
++ArgIndex;
}
@@ -169,14 +187,22 @@ class UncountedLambdaCapturesChecker
auto *CtorArg = CE->getArg(0)->IgnoreParenCasts();
if (!CtorArg)
return nullptr;
- if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) {
+ auto *InnerCE = dyn_cast_or_null<CXXConstructExpr>(CtorArg);
+ if (InnerCE && InnerCE->getNumArgs())
+ CtorArg = InnerCE->getArg(0)->IgnoreParenCasts();
+ auto updateIgnoreList = [&] {
ConstructToIgnore.insert(CE);
+ if (InnerCE)
+ ConstructToIgnore.insert(InnerCE);
+ };
+ if (auto *Lambda = dyn_cast<LambdaExpr>(CtorArg)) {
+ updateIgnoreList();
return Lambda;
}
if (auto *TempExpr = dyn_cast<CXXBindTemporaryExpr>(CtorArg)) {
E = TempExpr->getSubExpr()->IgnoreParenCasts();
if (auto *Lambda = dyn_cast<LambdaExpr>(E)) {
- ConstructToIgnore.insert(CE);
+ updateIgnoreList();
return Lambda;
}
}
@@ -189,10 +215,14 @@ class UncountedLambdaCapturesChecker
auto *Init = VD->getInit();
if (!Init)
return nullptr;
+ if (auto *Lambda = dyn_cast<LambdaExpr>(Init)) {
+ updateIgnoreList();
+ return Lambda;
+ }
TempExpr = dyn_cast<CXXBindTemporaryExpr>(Init->IgnoreParenCasts());
if (!TempExpr)
return nullptr;
- ConstructToIgnore.insert(CE);
+ updateIgnoreList();
return dyn_cast_or_null<LambdaExpr>(TempExpr->getSubExpr());
}
@@ -226,7 +256,7 @@ class UncountedLambdaCapturesChecker
DeclRefExprsToIgnore.insert(ArgRef);
LambdasToIgnore.insert(L);
Checker->visitLambdaExpr(L, shouldCheckThis() && !hasProtectedThis(L),
- /* ignoreParamVarDecl */ true);
+ ClsType, /* ignoreParamVarDecl */ true);
}
bool hasProtectedThis(LambdaExpr *L) {
@@ -293,10 +323,13 @@ class UncountedLambdaCapturesChecker
};
LocalVisitor visitor(this);
+ if (RTC)
+ RTC->visitTranslationUnitDecl(TUD);
visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
}
void visitLambdaExpr(LambdaExpr *L, bool shouldCheckThis,
+ const QualType T,
bool ignoreParamVarDecl = false) const {
if (TFA.isTrivial(L->getBody()))
return;
@@ -306,13 +339,13 @@ class UncountedLambdaCapturesChecker
if (ignoreParamVarDecl && isa<ParmVarDecl>(CapturedVar))
continue;
QualType CapturedVarQualType = CapturedVar->getType();
- auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType(), false);
+ auto IsUncountedPtr = isUnsafePtr(CapturedVar->getType());
if (IsUncountedPtr && *IsUncountedPtr)
reportBug(C, CapturedVar, CapturedVarQualType);
} else if (C.capturesThis() && shouldCheckThis) {
if (ignoreParamVarDecl) // this is always a parameter to this function.
continue;
- reportBugOnThisPtr(C);
+ reportBugOnThisPtr(C, T);
}
}
}
@@ -320,6 +353,9 @@ class UncountedLambdaCapturesChecker
void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
const QualType T) const {
assert(CapturedVar);
+
+ if (isa<ImplicitParamDecl>(CapturedVar) && !Capture.getLocation().isValid())
+ return; // Ignore implicit captruing of self.
SmallString<100> Buf;
llvm::raw_svector_ostream Os(Buf);
@@ -329,22 +365,22 @@ class UncountedLambdaCapturesChecker
} else {
Os << "Implicitly captured ";
}
- if (T->isPointerType()) {
+ if (isa<PointerType>(T) || isa<ObjCObjectPointerType>(T)) {
Os << "raw-pointer ";
} else {
- assert(T->isReferenceType());
Os << "reference ";
}
- printQuotedQualifiedName(Os, Capture.getCapturedVar());
- Os << " to ref-counted type or CheckedPtr-capable type is unsafe.";
+ printQuotedQualifiedName(Os, CapturedVar);
+ Os << " to " << ptrKind(T) << " type is unsafe.";
PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager());
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
BR->emitReport(std::move(Report));
}
- void reportBugOnThisPtr(const LambdaCapture &Capture) const {
+ void reportBugOnThisPtr(const LambdaCapture &Capture,
+ const QualType T) const {
SmallString<100> Buf;
llvm::raw_svector_ostream Os(Buf);
@@ -354,14 +390,57 @@ class UncountedLambdaCapturesChecker
Os << "Implicitly captured ";
}
- Os << "raw-pointer 'this' to ref-counted type or CheckedPtr-capable type "
- "is unsafe.";
+ Os << "raw-pointer 'this' to " << ptrKind(T) << " type is unsafe.";
PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager());
auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
BR->emitReport(std::move(Report));
}
};
+
+class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
+public:
+ UncountedLambdaCapturesChecker()
+ : RawPtrRefLambdaCapturesChecker("Lambda capture of uncounted or "
+ "unchecked variable") {}
+
+ std::optional<bool> isUnsafePtr(QualType QT) const final {
+ auto result1 = isUncountedPtr(QT);
+ auto result2 = isUncheckedPtr(QT);
+ if (result1 && *result1)
+ return true;
+ if (result2 && *result2)
+ return true;
+ if (result1)
+ return *result1;
+ return result2;
+ }
+
+ const char *ptrKind(QualType QT) const final {
+ if (isUncounted(QT))
+ return "uncounted";
+ return "unchecked";
+ }
+
+};
+
+class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
+public:
+ UnretainedLambdaCapturesChecker()
+ : RawPtrRefLambdaCapturesChecker("Lambda capture of unretained "
+ "variables") {
+ RTC = RetainTypeChecker();
+ }
+
+ std::optional<bool> isUnsafePtr(QualType QT) const final {
+ return RTC->isUnretained(QT);
+ }
+
+ const char *ptrKind(QualType QT) const final {
+ return "unretained";
+ }
+};
+
} // namespace
void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) {
@@ -372,3 +451,12 @@ bool ento::shouldRegisterUncountedLambdaCapturesChecker(
const CheckerManager &mgr) {
return true;
}
+
+void ento::registerUnretainedLambdaCapturesChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<UnretainedLambdaCapturesChecker>();
+}
+
+bool ento::shouldRegisterUnretainedLambdaCapturesChecker(
+ const CheckerManager &mgr) {
+ return true;
+}
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index 7bb33bcb6cf44..9b13810d0c5c9 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -1,30 +1,94 @@
@class NSString;
@class NSArray;
@class NSMutableArray;
+#define nil ((id)0)
#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 signed char BOOL;
typedef signed long CFIndex;
-typedef const struct __CFAllocator * CFAllocatorRef;
+typedef unsigned long NSUInteger;
+typedef const struct CF_BRIDGED_TYPE(id) __CFAllocator * CFAllocatorRef;
typedef const struct CF_BRIDGED_TYPE(NSString) __CFString * CFStringRef;
typedef const struct CF_BRIDGED_TYPE(NSArray) __CFArray * CFArrayRef;
-typedef struct CF_BRIDGED_TYPE(NSMutableArray) __CFArray * CFMutableArrayRef;
+typedef struct CF_BRIDGED_MUTABLE_TYPE(NSMutableArray) __CFArray * CFMutableArrayRef;
+typedef struct CF_BRIDGED_MUTABLE_TYPE(CFRunLoopRef) __CFRunLoop * CFRunLoopRef;
extern const CFAllocatorRef kCFAllocatorDefault;
+typedef struct _NSZone NSZone;
CFMutableArrayRef CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity);
extern void CFArrayAppendValue(CFMutableArrayRef theArray, const void *value);
CFArrayRef CFArrayCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues);
CFIndex CFArrayGetCount(CFArrayRef theArray);
+CFRunLoopRef CFRunLoopGetCurrent(void);
+CFRunLoopRef CFRunLoopGetMain(void);
extern CFTypeRef CFRetain(CFTypeRef cf);
extern void CFRelease(CFTypeRef cf);
+#define CFSTR(cStr) ((CFStringRef) __builtin___CFStringMakeConstantString ("" cStr ""))
+extern Class NSClassFromString(NSString *aClassName);
__attribute__((objc_root_class))
@interface NSObject
+ (instancetype) alloc;
++ (Class) class;
++ (Class) superclass;
- (instancetype) init;
- (instancetype)retain;
- (void)release;
+- (BOOL)isKindOfClass:(Class)aClass;
+ at end
+
+ at protocol NSCopying
+- (id)copyWithZone:(NSZone *)zone;
+ at end
+
+ at protocol NSFastEnumeration
+- (int)countByEnumeratingWithState:(void *)state objects:(id *)objects count:(unsigned)count;
+- (void)protocolMethod;
+ at end
+
+ at interface NSEnumerator <NSFastEnumeration>
+ at end
+
+ at interface NSDictionary : NSObject <NSCopying>
+- (NSUInteger)count;
+- (id)objectForKey:(id)aKey;
+- (id)objectForKeyedSubscript:(id)aKey;
+- (NSEnumerator *)keyEnumerator;
++ (id)dictionary;
++ (id)dictionaryWithObject:(id)object forKey:(id <NSCopying>)key;
++ (instancetype)dictionaryWithObjects:(const id [])objects forKeys:(const id <NSCopying> [])keys count:(NSUInteger)cnt;
+ at end
+
+ at interface NSArray : NSObject <NSCopying, NSFastEnumeration>
+- (NSUInteger)count;
+- (NSEnumerator *)objectEnumerator;
++ (NSArray *)arrayWithObjects:(const id [])objects count:(NSUInteger)count;
+ at end
+
+ at interface NSString : NSObject <NSCopying>
+- (NSUInteger)length;
+- (NSString *)stringByAppendingString:(NSString *)aString;
+- ( const char *)UTF8String;
+- (id)initWithUTF8String:(const char *)nullTerminatedCString;
++ (id)stringWithUTF8String:(const char *)nullTerminatedCString;
+ at end
+
+ at interface NSMutableString : NSString
+ at end
+
+ at interface NSValue : NSObject <NSCopying>
+- (void)getValue:(void *)value;
+ at end
+
+ at interface NSNumber : NSValue
+- (char)charValue;
+- (id)initWithInt:(int)value;
++ (NSNumber *)numberWithInt:(int)value;
@end
@interface SomeObj : NSObject
+- (SomeObj *)mutableCopy;
+- (SomeObj *)copyWithValue:(int)value;
- (void)doWork;
- (SomeObj *)other;
- (SomeObj *)next;
@@ -57,28 +121,34 @@ template <typename T> struct RetainPtr {
PtrType t;
RetainPtr() : t(nullptr) { }
-
RetainPtr(PtrType t)
: t(t) {
if (t)
- CFRetain(t);
+ CFRetain(toCFTypeRef(t));
}
- RetainPtr(RetainPtr&& o)
- : RetainPtr(o.t)
- {
- o.t = nullptr;
- }
- RetainPtr(const RetainPtr& o)
+ RetainPtr(RetainPtr&&);
+ RetainPtr(const RetainPtr&);
+ template <typename U>
+ RetainPtr(const RetainPtr<U>& o)
: RetainPtr(o.t)
+ {}
+ RetainPtr operator=(const RetainPtr& o)
{
+ if (t)
+ CFRelease(toCFTypeRef(t));
+ t = o.t;
+ if (t)
+ CFRetain(toCFTypeRef(t));
+ return *this;
}
- RetainPtr operator=(const RetainPtr& o)
+ template <typename U>
+ RetainPtr operator=(const RetainPtr<U>& o)
{
if (t)
- CFRelease(t);
+ CFRelease(toCFTypeRef(t));
t = o.t;
if (t)
- CFRetain(t);
+ CFRetain(toCFTypeRef(t));
return *this;
}
~RetainPtr() {
@@ -86,7 +156,7 @@ template <typename T> struct RetainPtr {
}
void clear() {
if (t)
- CFRelease(t);
+ CFRelease(toCFTypeRef(t));
t = nullptr;
}
void swap(RetainPtr& o) {
@@ -102,10 +172,19 @@ template <typename T> struct RetainPtr {
swap(o);
return *this;
}
+ PtrType leakRef()
+ {
+ PtrType s = t;
+ t = nullptr;
+ return s;
+ }
operator PtrType() const { return t; }
operator bool() const { return t; }
private:
+ CFTypeRef toCFTypeRef(id ptr) { return (__bridge CFTypeRef)ptr; }
+ CFTypeRef toCFTypeRef(const void* ptr) { return (CFTypeRef)ptr; }
+
template <typename U> friend RetainPtr<U> adoptNS(U*);
template <typename U> friend RetainPtr<U> adoptCF(U);
@@ -113,9 +192,26 @@ template <typename T> struct RetainPtr {
RetainPtr(PtrType t, AdoptTag) : t(t) { }
};
+template <typename T>
+RetainPtr<T>::RetainPtr(RetainPtr<T>&& o)
+ : RetainPtr(o.t)
+{
+ o.t = nullptr;
+}
+
+template <typename T>
+RetainPtr<T>::RetainPtr(const RetainPtr<T>& o)
+ : RetainPtr(o.t)
+{
+}
+
template <typename T>
RetainPtr<T> adoptNS(T* t) {
+#if __has_feature(objc_arc)
+ return t;
+#else
return RetainPtr<T>(t, RetainPtr<T>::Adopt);
+#endif
}
template <typename T>
@@ -123,9 +219,31 @@ RetainPtr<T> adoptCF(T t) {
return RetainPtr<T>(t, RetainPtr<T>::Adopt);
}
+template<typename T> inline RetainPtr<T> retainPtr(T ptr)
+{
+ return ptr;
+}
+
+template<typename T> inline RetainPtr<T> retainPtr(T* ptr)
+{
+ return ptr;
+}
+
+inline NSObject *bridge_cast(CFTypeRef object)
+{
+ return (__bridge NSObject *)object;
+}
+
+inline CFTypeRef bridge_cast(NSObject *object)
+{
+ return (__bridge CFTypeRef)object;
+}
+
}
using WTF::RetainPtr;
using WTF::adoptNS;
using WTF::adoptCF;
+using WTF::retainPtr;
using WTF::downcast;
+using WTF::bridge_cast;
\ No newline at end of file
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures-decl-protects-this-crash.cpp b/clang/test/Analysis/Check...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/128651
More information about the llvm-commits
mailing list