https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/201044
Split alpha.webkit.UncheckedLambdaCapturesChecker off of UncountedLambdaCapturesChecker, which currently checks for the use of ref counted objects as well as CheckedPtr capable types unlike all other WebKit checkers, which only check for one type of smart pointers. Also improve the wording of the warning text so that it can be easily extended to support checking for the lambda capturing of raw pointers and references to CheckedPtr/CheckedRef. >From ad6d6ec948d5f9f4a9d879981f4a6bc10af783c8 Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa <[email protected]> Date: Tue, 2 Jun 2026 00:24:55 -0700 Subject: [PATCH] [alpha.webkit.UncheckedLambdaCapturesChecker] Split unchecked lambda capture checker Split alpha.webkit.UncheckedLambdaCapturesChecker off of UncountedLambdaCapturesChecker, which currently checks for the use of ref counted objects as well as CheckedPtr capable types unlike all other WebKit checkers, which only check for one type of smart pointers. Also improve the wording of the warning text so that it can be easily extended to support checking for the lambda capturing of raw pointers and references to CheckedPtr/CheckedRef. --- clang/docs/analyzer/checkers.rst | 17 + .../clang/StaticAnalyzer/Checkers/Checkers.td | 4 + .../WebKit/RawPtrRefLambdaCapturesChecker.cpp | 130 +++- .../Analysis/Checkers/WebKit/mock-types.h | 1 + .../WebKit/unchecked-lambda-captures.cpp | 713 ++++++++++++++++++ ...mbda-captures-decl-protects-this-crash.cpp | 4 +- .../WebKit/uncounted-lambda-captures.cpp | 61 +- .../WebKit/unretained-lambda-captures-arc.mm | 14 +- .../WebKit/unretained-lambda-captures.mm | 49 +- 9 files changed, 894 insertions(+), 99 deletions(-) create mode 100644 clang/test/Analysis/Checkers/WebKit/unchecked-lambda-captures.cpp diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index 61f591916018e..09b49c779b451 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -3940,6 +3940,23 @@ The goal of this rule is to make sure that lifetime of any dynamically allocated The rules of when to use and not to use CheckedPtr / CheckedRef are same as alpha.webkit.UncountedCallArgsChecker for ref-counted objects. +alpha.webkit.UncheckedLambdaCapturesChecker +""""""""""""""""""""""""""""""""""""""""""" +Raw pointers and references to unchecked types can't be captured in lambdas. Only CheckedPtr or CheckedRef is allowed. + +.. code-block:: cpp + + struct CheckedObject { + void incrementCheckedPtr() {} + void decrementCheckedPtr() {} + }; + + void foo(CheckedObject* a, CheckedObject& b) { + [&, a](){ // warn about 'a' + do_something(b); // warn about 'b' + }; + }; + alpha.webkit.UnretainedCallArgsChecker """""""""""""""""""""""""""""""""""""" The goal of this rule is to make sure that lifetime of any dynamically allocated NS or CF objects passed as a call argument keeps its memory region past the end of the call. This applies to call to any function, method, lambda, function pointer or functor. NS or CF objects aren't supposed to be allocated on stack so we check arguments for parameters of raw pointers and references to unretained types. diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index eca2afbe340a9..27415781193e7 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -1755,6 +1755,10 @@ def UncountedLocalVarsChecker : Checker<"UncountedLocalVarsChecker">, HelpText<"Check uncounted local variables.">, Documentation<HasDocumentation>; +def UncheckedLambdaCapturesChecker : Checker<"UncheckedLambdaCapturesChecker">, + HelpText<"Check unchecked lambda captures.">, + Documentation<HasDocumentation>; + def UncheckedLocalVarsChecker : Checker<"UncheckedLocalVarsChecker">, HelpText<"Check unchecked local variables.">, Documentation<HasDocumentation>; diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp index 287c7dd039dd8..4d0381a82f250 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp @@ -1,4 +1,4 @@ -//=======- UncountedLambdaCapturesChecker.cpp --------------------*- C++ -*-==// +//=======- RawPtrRefLambdaCapturesChecker.cpp --------------------*- C++ -*-==// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -36,7 +36,7 @@ class RawPtrRefLambdaCapturesChecker virtual std::optional<bool> isUnsafePtr(QualType) const = 0; virtual bool isPtrType(const std::string &) const = 0; - virtual const char *ptrKind(QualType QT) const = 0; + virtual const char *typeName() const = 0; void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, BugReporter &BRArg) const { @@ -542,25 +542,40 @@ class RawPtrRefLambdaCapturesChecker SmallString<100> Buf; llvm::raw_svector_ostream Os(Buf); - if (Capture.isExplicit()) { + if (Capture.isExplicit()) Os << "Captured "; - } else { + else Os << "Implicitly captured "; - } - if (isa<PointerType>(T) || isa<ObjCObjectPointerType>(T)) { - Os << "raw-pointer "; - } else { - Os << "reference "; - } - + Os << "variable "; printQuotedQualifiedName(Os, CapturedVar); - Os << " to " << ptrKind(T) << " type is unsafe."; + + bool IsUnsafePtr = CapturedVar->getType() == T; + if (IsUnsafePtr) + Os << " is a "; + else + Os << " contains a "; + auto *CapturedType = T.getTypePtrOrNull(); + printPointer(Os, CapturedType); PathDiagnosticLocation BSLoc(Location, BR->getSourceManager()); auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); BR->emitReport(std::move(Report)); } + ObjCInterfaceDecl *getObjCDecl(const Type *TypePtr) const { + auto *PointeeType = TypePtr->getPointeeType().getTypePtrOrNull(); + if (!PointeeType) + return nullptr; + auto *Desugared = PointeeType->getUnqualifiedDesugaredType(); + if (!Desugared) + return nullptr; + if (auto *ObjCType = dyn_cast<ObjCInterfaceType>(Desugared)) + return ObjCType->getDecl(); + if (auto *ObjCType = dyn_cast<ObjCObjectType>(Desugared)) + return ObjCType->getInterface(); + return nullptr; + } + void reportBugOnThisPtr(const LambdaCapture &Capture, const QualType T) const { SmallString<100> Buf; @@ -572,41 +587,64 @@ class RawPtrRefLambdaCapturesChecker Os << "Implicitly captured "; } - Os << "raw-pointer 'this' to " << ptrKind(T) << " type is unsafe."; + Os << "variable 'this' is a raw pointer to " << typeName(); + if (auto *RD = T->getPointeeCXXRecordDecl()) { + Os << " "; + printQuotedQualifiedName(Os, RD); + } PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager()); auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); BR->emitReport(std::move(Report)); } + + virtual void printPointer(llvm::raw_svector_ostream &Os, + const Type *T) const { + T = T->getUnqualifiedDesugaredType(); + bool IsPtr = isa<PointerType>(T) || isa<ObjCObjectPointerType>(T); + Os << (IsPtr ? "raw pointer" : "raw reference") << " to "; + Os << typeName(); + + if (auto *RD = T->getPointeeType()->getAsRecordDecl()) { + Os << " "; + printQuotedQualifiedName(Os, RD); + } else if (auto *ObjCDecl = getObjCDecl(T)) { + Os << " "; + printQuotedQualifiedName(Os, ObjCDecl); + } + } }; class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker { public: UncountedLambdaCapturesChecker() - : RawPtrRefLambdaCapturesChecker("Lambda capture of uncounted or " - "unchecked variable") {} + : RawPtrRefLambdaCapturesChecker("Lambda capture of uncounted 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; + return isUncountedPtr(QT.getCanonicalType()); } virtual bool isPtrType(const std::string &Name) const final { - return isRefType(Name) || isCheckedPtr(Name); + return isRefType(Name); + } + + const char *typeName() const final { return "ref-countable type"; } +}; + +class UncheckedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker { +public: + UncheckedLambdaCapturesChecker() + : RawPtrRefLambdaCapturesChecker("Lambda capture of unchecked variable") {} + + std::optional<bool> isUnsafePtr(QualType QT) const final { + return isUncheckedPtr(QT.getCanonicalType()); } - const char *ptrKind(QualType QT) const final { - if (isUncounted(QT)) - return "uncounted"; - return "unchecked"; + virtual bool isPtrType(const std::string &Name) const final { + return isCheckedPtr(Name); } + + const char *typeName() const final { return "CheckedPtr capable type"; } }; class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker { @@ -627,7 +665,30 @@ class UnretainedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker { return isRetainPtrOrOSPtr(Name); } - const char *ptrKind(QualType QT) const final { return "unretained"; } + const char *typeName() const final { return "retainable type"; } + + void printPointer(llvm::raw_svector_ostream &Os, const Type *T) const final { + if (auto *ObjCPtr = dyn_cast<ObjCObjectPointerType>(T)) { + for (ObjCProtocolDecl *P : ObjCPtr->quals()) { + if (const auto *II = P->getIdentifier()) { + auto Name = II->getName(); + if (Name.starts_with("OS_")) { + Os << typeName() << " "; + printQuotedQualifiedName(Os, P); + return; + } + } + } + } + if (!isa<ObjCObjectPointerType>(T) && T->getAs<TypedefType>()) { + auto Typedef = T->getAs<TypedefType>(); + assert(Typedef); + Os << typeName() << " "; + printQuotedQualifiedName(Os, Typedef->getDecl()); + return; + } + return RawPtrRefLambdaCapturesChecker::printPointer(Os, T); + } }; } // namespace @@ -641,6 +702,15 @@ bool ento::shouldRegisterUncountedLambdaCapturesChecker( return true; } +void ento::registerUncheckedLambdaCapturesChecker(CheckerManager &Mgr) { + Mgr.registerChecker<UncheckedLambdaCapturesChecker>(); +} + +bool ento::shouldRegisterUncheckedLambdaCapturesChecker( + const CheckerManager &mgr) { + return true; +} + void ento::registerUnretainedLambdaCapturesChecker(CheckerManager &Mgr) { Mgr.registerChecker<UnretainedLambdaCapturesChecker>(); } diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h index af63268ac9695..2861ba9c090a0 100644 --- a/clang/test/Analysis/Checkers/WebKit/mock-types.h +++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h @@ -307,6 +307,7 @@ class CheckedObj { void incrementCheckedPtrCount() { ++m_ptrCount; } void decrementCheckedPtrCount() { --m_ptrCount; } void method(); + void constMethod() const; int trivial() { return 123; } CheckedObj* next(); diff --git a/clang/test/Analysis/Checkers/WebKit/unchecked-lambda-captures.cpp b/clang/test/Analysis/Checkers/WebKit/unchecked-lambda-captures.cpp new file mode 100644 index 0000000000000..c63e44b4e51cf --- /dev/null +++ b/clang/test/Analysis/Checkers/WebKit/unchecked-lambda-captures.cpp @@ -0,0 +1,713 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.UncheckedLambdaCapturesChecker -verify %s + +#include "mock-types.h" + +namespace std { + +template <typename T> +T&& move(T& t) { + return static_cast<T&&>(t); +} + +namespace ranges { + +template<typename IteratorType, typename CallbackType> +void for_each(IteratorType first, IteratorType last, CallbackType callback) { + for (auto it = first; !(it == last); ++it) + callback(*it); +} + +struct all_of_impl { + template <typename Collection, typename Predicate> + constexpr bool operator()(const Collection& collection, Predicate predicate) const { + for (auto it = collection.begin(); it != collection.end(); ++it) { + if (!predicate(*it)) + return false; + } + return true; + } +}; +inline constexpr auto all_of = all_of_impl {}; + +} + +} + +namespace WTF { + +namespace Detail { + +template<typename Out, typename... In> +class CallableWrapperBase { +public: + virtual ~CallableWrapperBase() { } + virtual Out call(In...) = 0; +}; + +template<typename, typename, typename...> class CallableWrapper; + +template<typename CallableType, typename Out, typename... In> +class CallableWrapper : public CallableWrapperBase<Out, In...> { +public: + explicit CallableWrapper(CallableType& callable) + : m_callable(callable) { } + Out call(In... in) final { return m_callable(in...); } + +private: + CallableType m_callable; +}; + +} // namespace Detail + +template<typename> class Function; + +template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>*); + +template <typename Out, typename... In> +class Function<Out(In...)> { +public: + using Impl = Detail::CallableWrapperBase<Out, In...>; + + Function() = default; + + template<typename FunctionType> + Function(FunctionType f) + : m_callableWrapper(new Detail::CallableWrapper<FunctionType, Out, In...>(f)) { } + + Out operator()(In... in) const { return m_callableWrapper->call(in...); } + explicit operator bool() const { return !!m_callableWrapper; } + +private: + enum AdoptTag { Adopt }; + Function(Impl* impl, AdoptTag) + : m_callableWrapper(impl) + { + } + + friend Function adopt<Out, In...>(Impl*); + + std::unique_ptr<Impl> m_callableWrapper; +}; + +template<typename Out, typename... In> Function<Out(In...)> adopt(Detail::CallableWrapperBase<Out, In...>* impl) +{ + return Function<Out(In...)>(impl, Function<Out(In...)>::Adopt); +} + +template <typename KeyType, typename ValueType> +class HashMap { +public: + HashMap(); + HashMap([[clang::noescape]] const Function<ValueType()>&); + void ensure(const KeyType&, [[clang::noescape]] const Function<ValueType()>&); + bool operator+([[clang::noescape]] const Function<ValueType()>&) const; + static void ifAny(HashMap, [[clang::noescape]] const Function<bool(ValueType)>&); + +private: + ValueType* m_table { nullptr }; +}; + +class ScopeExit final { +public: + template<typename ExitFunctionParameter> + explicit ScopeExit(ExitFunctionParameter&& exitFunction) + : m_exitFunction(std::move(exitFunction)) { + } + + ScopeExit(ScopeExit&& other) + : m_exitFunction(std::move(other.m_exitFunction)) + , m_execute(std::move(other.m_execute)) { + } + + ~ScopeExit() { + if (m_execute) + m_exitFunction(); + } + + WTF::Function<void()> take() { + m_execute = false; + return std::move(m_exitFunction); + } + + void release() { m_execute = false; } + + ScopeExit(const ScopeExit&) = delete; + ScopeExit& operator=(const ScopeExit&) = delete; + ScopeExit& operator=(ScopeExit&&) = delete; + +private: + WTF::Function<void()> m_exitFunction; + bool m_execute { true }; +}; + +template<typename ExitFunction> ScopeExit makeScopeExit(ExitFunction&&); +template<typename ExitFunction> +ScopeExit makeScopeExit(ExitFunction&& exitFunction) +{ + return ScopeExit(std::move(exitFunction)); +} + +// Visitor adapted from http://stackoverflow.com/questions/25338795/is-there-a-name-for-this-tuple-creation-idiom + +template<class A, class... B> struct Visitor : Visitor<A>, Visitor<B...> { + Visitor(A a, B... b) + : Visitor<A>(a) + , Visitor<B...>(b...) + { + } + + using Visitor<A>::operator (); + using Visitor<B...>::operator (); +}; + +template<class A> struct Visitor<A> : A { + Visitor(A a) + : A(a) + { + } + + using A::operator(); +}; + +template<class... F> Visitor<F...> makeVisitor(F... f) +{ + return Visitor<F...>(f...); +} + +void opaqueFunction(); +template <typename Visitor, typename... Variants> void visit(Visitor&& v, Variants&&... values) +{ + opaqueFunction(); +} + +} // namespace WTF + +struct A { + static void b(); +}; + +CheckedObj* make_obj(); + +void someFunction(); +template <typename Callback> void call(Callback callback) { + someFunction(); + callback(); +} +void callAsync(const WTF::Function<void()>&); + +void raw_ptr() { + CheckedObj* checked_ptr_capable = make_obj(); + auto foo1 = [checked_ptr_capable](){ + // expected-warning@-1{{Captured variable 'checked_ptr_capable' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + checked_ptr_capable->method(); + }; + auto foo2 = [&checked_ptr_capable](){ + // expected-warning@-1{{Captured variable 'checked_ptr_capable' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + checked_ptr_capable->method(); + }; + auto foo3 = [&](){ + checked_ptr_capable->method(); + // expected-warning@-1{{Implicitly captured variable 'checked_ptr_capable' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + checked_ptr_capable = nullptr; + }; + + auto foo4 = [=](){ + checked_ptr_capable->method(); + // expected-warning@-1{{Implicitly captured variable 'checked_ptr_capable' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }; + + call(foo1); + call(foo2); + call(foo3); + call(foo4); + + // Confirm that the checker respects [[clang::suppress]]. + CheckedObj* suppressed_checked_ptr_capable = nullptr; + [[clang::suppress]] auto foo5 = [suppressed_checked_ptr_capable](){}; + // no warning. + call(foo5); +} + +void references() { + CheckedObj automatic; + CheckedObj& checked_ptr_capable_ref = automatic; + auto foo1 = [checked_ptr_capable_ref](){ checked_ptr_capable_ref.constMethod(); }; + auto foo2 = [&checked_ptr_capable_ref](){ checked_ptr_capable_ref.method(); }; + // expected-warning@-1{{Captured variable 'checked_ptr_capable_ref' is a raw reference to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + auto foo3 = [&](){ checked_ptr_capable_ref.method(); }; + // expected-warning@-1{{Implicitly captured variable 'checked_ptr_capable_ref' is a raw reference to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + auto foo4 = [=](){ checked_ptr_capable_ref.constMethod(); }; + + call(foo1); + call(foo2); + call(foo3); + call(foo4); +} + +void quiet() { +// This code is not expected to trigger any warnings. + { + CheckedObj automatic; + CheckedObj &checked_ptr_capable_ref = automatic; + } + + auto foo3 = [&]() {}; + auto foo4 = [=]() {}; + + call(foo3); + call(foo4); + + CheckedObj *checked_ptr_capable = nullptr; +} + +template <typename Callback> +void map(CheckedObj* start, [[clang::noescape]] Callback&& callback) +{ + while (start) { + callback(*start); + start = start->next(); + } +} + +template <typename Callback1, typename Callback2> +void doubleMap(CheckedObj* start, [[clang::noescape]] Callback1&& callback1, Callback2&& callback2) +{ + while (start) { + callback1(*start); + callback2(*start); + start = start->next(); + } +} + +void noescape_lambda() { + CheckedObj* someObj = make_obj(); + CheckedObj* otherObj = make_obj(); + map(make_obj(), [&](CheckedObj& obj) { + otherObj->method(); + }); + doubleMap(make_obj(), [&](CheckedObj& obj) { + otherObj->method(); + }, [&](CheckedObj& obj) { + otherObj->method(); + // expected-warning@-1{{Implicitly captured variable 'otherObj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }); + ([&] { + someObj->method(); + })(); +} + +void lambda_capture_param(CheckedObj* obj) { + auto someLambda = [&]() { + obj->method(); + }; + someLambda(); + someLambda(); +} + +struct CheckedObjWithLambdaCapturingThis { + void incrementCheckedPtrCount() const; + void decrementCheckedPtrCount() const; + void nonTrivial(); + + void method_captures_this_safe() { + auto lambda = [&]() { + nonTrivial(); + }; + lambda(); + } + + void method_captures_this_unsafe() { + auto lambda = [&]() { + nonTrivial(); + // expected-warning@-1{{Implicitly captured variable 'this' is a raw pointer to CheckedPtr capable type 'CheckedObjWithLambdaCapturingThis' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }; + call(lambda); + } + + void method_captures_this_unsafe_capture_local_var_explicitly() { + CheckedObj* x = make_obj(); + call([this, protectedThis = CheckedPtr { this }, x]() { + // expected-warning@-1{{Captured variable 'x' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + nonTrivial(); + x->method(); + }); + } + + void method_captures_this_with_other_protected_var() { + CheckedObj* x = make_obj(); + call([this, protectedX = CheckedPtr { x }]() { + // expected-warning@-1{{Captured variable 'this' is a raw pointer to CheckedPtr capable type 'CheckedObjWithLambdaCapturingThis' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + nonTrivial(); + protectedX->method(); + }); + } + + void method_captures_this_unsafe_capture_local_var_explicitly_with_deref() { + CheckedObj* x = make_obj(); + call([this, protectedThis = CheckedRef { *this }, x]() { + // expected-warning@-1{{Captured variable 'x' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + nonTrivial(); + x->method(); + }); + } + + void method_captures_this_unsafe_local_var_via_vardecl() { + CheckedObj* x = make_obj(); + auto lambda = [this, protectedThis = CheckedRef { *this }, x]() { + // expected-warning@-1{{Captured variable 'x' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + nonTrivial(); + x->method(); + }; + call(lambda); + } + + void method_captures_this_with_guardian() { + auto lambda = [this, protectedThis = CheckedRef { *this }]() { + nonTrivial(); + }; + call(lambda); + } + + void method_captures_this_with_guardian_refptr() { + auto lambda = [this, protectedThis = CheckedPtr { &*this }]() { + nonTrivial(); + }; + call(lambda); + } + + void forEach(const WTF::Function<void(CheckedObj&)>&); + void method_captures_this_with_lambda_with_no_escape() { + auto run = [&]([[clang::noescape]] const WTF::Function<void(CheckedObj&)>& func) { + forEach(func); + }; + run([&](CheckedObj&) { + nonTrivial(); + }); + } + + static void callLambda([[clang::noescape]] const WTF::Function<CheckedPtr<CheckedObj>()>&); + void method_captures_this_in_template_method() { + CheckedObj* obj = make_obj(); + WTF::HashMap<int, CheckedPtr<CheckedObj>> nextMap; + nextMap.ensure(3, [&] { + return obj->next(); + }); + nextMap+[&] { + return obj->next(); + }; + WTF::HashMap<int, CheckedPtr<CheckedObj>>::ifAny(nextMap, [&](auto& item) -> bool { + return item->next() && obj->next(); + }); + callLambda([&]() -> CheckedPtr<CheckedObj> { + return obj->next(); + }); + WTF::HashMap<int, CheckedPtr<CheckedObj>> anotherMap([&] { + return obj->next(); + }); + } + + void callAsyncNoescape([[clang::noescape]] WTF::Function<bool(CheckedObj&)>&&); + void method_temp_lambda(CheckedObj* obj) { + callAsyncNoescape([this, otherObj = CheckedPtr { obj }](auto& obj) { + return otherObj.get() == &obj; + }); + } + + void method_nested_lambda() { + callAsync([this, protectedThis = CheckedRef { *this }] { + callAsync([this, protectedThis = static_cast<const CheckedRef<CheckedObjWithLambdaCapturingThis>&&>(protectedThis)] { + nonTrivial(); + }); + }); + } + + void method_nested_lambda2() { + callAsync([this, protectedThis = CheckedPtr { this }] { + callAsync([this, protectedThis = std::move(*protectedThis)] { + nonTrivial(); + }); + }); + } + + void method_nested_lambda3() { + callAsync([this, protectedThis = CheckedPtr { this }] { + callAsync([this] { + // expected-warning@-1{{Captured variable 'this' is a raw pointer to CheckedPtr capable type 'CheckedObjWithLambdaCapturingThis' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + nonTrivial(); + }); + }); + } + + void method_nested_lambda4() { + callAsync([this, protectedThis = CheckedPtr { this }] { + callAsync([this, protectedThis = WTF::move(*protectedThis)] { + nonTrivial(); + }); + }); + } +}; + +struct NonCheckedObjWithLambdaCapturingThis { + void nonTrivial(); + + void method_captures_this_safe() { + auto lambda = [&]() { + nonTrivial(); + }; + lambda(); + } + + void method_captures_this_unsafe() { + auto lambda = [&]() { + nonTrivial(); + }; + call(lambda); + } +}; + +void trivial_lambda() { + CheckedObj* checked_ptr_capable = make_obj(); + auto trivial_lambda = [&]() { + return checked_ptr_capable->trivial(); + }; + trivial_lambda(); +} + +bool call_lambda_var_decl() { + CheckedObj* checked_ptr_capable = make_obj(); + auto lambda1 = [&]() -> bool { + return checked_ptr_capable->next(); + }; + auto lambda2 = [=]() -> bool { + return checked_ptr_capable->next(); + }; + return lambda1() && lambda2(); +} + +void lambda_with_args(CheckedObj* obj) { + auto trivial_lambda = [&](int v) { + obj->method(); + }; + trivial_lambda(1); +} + +void callFunctionOpaque(WTF::Function<void()>&&); +void callFunction(WTF::Function<void()>&& function) { + someFunction(); + function(); +} + +void lambda_converted_to_function(CheckedObj* obj) +{ + callFunction([&]() { + obj->method(); + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }); + callFunctionOpaque([&]() { + obj->method(); + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }); +} + +void capture_copy_in_lambda(CheckedObj& checked) { + callFunctionOpaque([checked]() mutable { + checked.method(); + }); + auto* ptr = &checked; + callFunctionOpaque([ptr]() mutable { + // expected-warning@-1{{Captured variable 'ptr' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + ptr->method(); + }); +} + +struct TemplateFunctionCallsLambda { + void ref() const; + void deref() const; + + CheckedObj* obj(); + + template <typename T> + CheckedPtr<T> method(T* t) { + auto ret = ([&]() -> CheckedPtr<T> { + if constexpr (T::isEncodable) + return t; + return obj() ? t : nullptr; + })(); + return ret; + } +}; + +class Iterator { +public: + Iterator(void* array, unsigned long sizeOfElement, unsigned int index); + Iterator(const Iterator&); + Iterator& operator=(const Iterator&); + bool operator==(const Iterator&); + + Iterator& operator++(); + int& operator*(); + +private: + void* current { nullptr }; + unsigned long sizeOfElement { 0 }; +}; + +void ranges_for_each(CheckedObj* obj) { + int array[] = { 1, 2, 3, 4, 5 }; + std::ranges::for_each(Iterator(array, sizeof(*array), 0), Iterator(array, sizeof(*array), 5), [&](int& item) { + obj->method(); + ++item; + }); +} + +class IntCollection { +public: + int* begin(); + int* end(); + const int* begin() const; + const int* end() const; +}; + +class CheckedPtrCapableObj { +public: + void incrementCheckedPtrCount(); + void decrementCheckedPtrCount(); + + bool allOf(const IntCollection&); + bool isMatch(int); + + void call() const; + void callLambda([[clang::noescape]] const WTF::Function<void ()>& callback) const; + void doSomeWork() const; +}; + +bool CheckedPtrCapableObj::allOf(const IntCollection& collection) { + return std::ranges::all_of(collection, [&](auto& number) { + return isMatch(number); + }); +} + +void CheckedPtrCapableObj::callLambda([[clang::noescape]] const WTF::Function<void ()>& callback) const +{ + callback(); +} + +void CheckedPtrCapableObj::call() const +{ + auto lambda = [&] { + doSomeWork(); + }; + callLambda(lambda); +} + +void scope_exit(CheckedObj* obj) { + auto scope = WTF::makeScopeExit([&] { + obj->method(); + }); + someFunction(); + WTF::ScopeExit scope2([&] { + obj->method(); + }); + someFunction(); +} + +void doWhateverWith(WTF::ScopeExit& obj); + +void scope_exit_with_side_effect(CheckedObj* obj) { + auto scope = WTF::makeScopeExit([&] { + obj->method(); + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }); + doWhateverWith(scope); +} + +void scope_exit_static(CheckedObj* obj) { + static auto scope = WTF::makeScopeExit([&] { + obj->method(); + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }); +} + +WTF::Function<void()> scope_exit_take_lambda(CheckedObj* obj) { + auto scope = WTF::makeScopeExit([&] { + obj->method(); + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }); + return scope.take(); +} + +// FIXME: Ideally, we treat release() as a trivial function. +void scope_exit_release(CheckedObj* obj) { + auto scope = WTF::makeScopeExit([&] { + obj->method(); + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }); + scope.release(); +} + +void make_visitor(CheckedObj* obj) { + auto visitor = WTF::makeVisitor([&] { + obj->method(); + }); +} + +void use_visitor(CheckedObj* obj) { + auto visitor = WTF::makeVisitor([&] { + obj->method(); + }); + WTF::visit(visitor, obj); +} + +template <typename Visitor, typename ObjectType> +void bad_visit(Visitor&, ObjectType*) { + someFunction(); +} + +void static_visitor(CheckedObj* obj) { + static auto visitor = WTF::makeVisitor([&] { + obj->method(); + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }); +} + +void make_visitor_with_multiple_lambdas(CheckedObj* obj) { + auto* otherObj = make_obj(); + auto visitor = WTF::makeVisitor([&] { + obj->method(); + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }, [&] { + otherObj->method(); + // expected-warning@-1{{Implicitly captured variable 'otherObj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }); + bad_visit(visitor, obj); +} + +void bad_use_visitor(CheckedObj* obj) { + auto visitor = WTF::makeVisitor([&] { + obj->method(); + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + }); + bad_visit(visitor, obj); +} + +class LambdaInConstructorDestructor { +public: + LambdaInConstructorDestructor() { + call([this]() { + // expected-warning@-1{{Captured variable 'this' is a raw pointer to CheckedPtr capable type 'LambdaInConstructorDestructor' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + doWork(); + }); + } + + ~LambdaInConstructorDestructor() { + call([this]() { + // expected-warning@-1{{Captured variable 'this' is a raw pointer to CheckedPtr capable type 'LambdaInConstructorDestructor' [alpha.webkit.UncheckedLambdaCapturesChecker]}} + doWork(); + }); + } + + void incrementCheckedPtrCount() const; + void decrementCheckedPtrCount() const; + + void doWork(); +}; diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures-decl-protects-this-crash.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures-decl-protects-this-crash.cpp index 0c10c69a97eda..1afbd0df93f18 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures-decl-protects-this-crash.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures-decl-protects-this-crash.cpp @@ -28,9 +28,9 @@ struct Obj { void foo(Foo foo) { bar([this](auto baz) { - // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'this' is a raw pointer to ref-countable type 'Obj' [webkit.UncountedLambdaCapturesChecker]}} bar([this, foo = *baz, foo2 = !baz](auto&&) { - // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'this' is a raw pointer to ref-countable type 'Obj' [webkit.UncountedLambdaCapturesChecker]}} someFunction(); }); }); diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp index 43834f5414f41..9c8204b5454c2 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp @@ -198,22 +198,22 @@ void callAsync(const WTF::Function<void()>&); void raw_ptr() { RefCountable* ref_countable = make_obj(); auto foo1 = [ref_countable](){ - // expected-warning@-1{{Captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'ref_countable' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} ref_countable->method(); }; auto foo2 = [&ref_countable](){ - // expected-warning@-1{{Captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'ref_countable' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} ref_countable->method(); }; auto foo3 = [&](){ ref_countable->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'ref_countable' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} ref_countable = nullptr; }; auto foo4 = [=](){ ref_countable->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'ref_countable' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} }; call(foo1); @@ -233,9 +233,9 @@ void references() { RefCountable& ref_countable_ref = automatic; auto foo1 = [ref_countable_ref](){ ref_countable_ref.constMethod(); }; auto foo2 = [&ref_countable_ref](){ ref_countable_ref.method(); }; - // expected-warning@-1{{Captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'ref_countable_ref' is a raw reference to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} auto foo3 = [&](){ ref_countable_ref.method(); }; - // expected-warning@-1{{Implicitly captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'ref_countable_ref' is a raw reference to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} auto foo4 = [=](){ ref_countable_ref.constMethod(); }; call(foo1); @@ -289,7 +289,7 @@ void noescape_lambda() { otherObj->method(); }, [&](RefCountable& obj) { otherObj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'otherObj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'otherObj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} }); ([&] { someObj->method(); @@ -319,7 +319,7 @@ struct RefCountableWithLambdaCapturingThis { void method_captures_this_unsafe() { auto lambda = [&]() { nonTrivial(); - // expected-warning@-1{{Implicitly captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'this' is a raw pointer to ref-countable type 'RefCountableWithLambdaCapturingThis' [webkit.UncountedLambdaCapturesChecker]}} }; call(lambda); } @@ -327,7 +327,7 @@ struct RefCountableWithLambdaCapturingThis { void method_captures_this_unsafe_capture_local_var_explicitly() { RefCountable* x = make_obj(); call([this, protectedThis = RefPtr { this }, x]() { - // expected-warning@-1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'x' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); x->method(); }); @@ -336,7 +336,7 @@ struct RefCountableWithLambdaCapturingThis { void method_captures_this_with_other_protected_var() { RefCountable* x = make_obj(); call([this, protectedX = RefPtr { x }]() { - // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'this' is a raw pointer to ref-countable type 'RefCountableWithLambdaCapturingThis' [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); protectedX->method(); }); @@ -345,7 +345,7 @@ struct RefCountableWithLambdaCapturingThis { void method_captures_this_unsafe_capture_local_var_explicitly_with_deref() { RefCountable* x = make_obj(); call([this, protectedThis = Ref { *this }, x]() { - // expected-warning@-1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'x' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); x->method(); }); @@ -354,7 +354,7 @@ struct RefCountableWithLambdaCapturingThis { void method_captures_this_unsafe_local_var_via_vardecl() { RefCountable* x = make_obj(); auto lambda = [this, protectedThis = Ref { *this }, x]() { - // expected-warning@-1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'x' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); x->method(); }; @@ -432,7 +432,7 @@ struct RefCountableWithLambdaCapturingThis { void method_nested_lambda3() { callAsync([this, protectedThis = RefPtr { this }] { callAsync([this] { - // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'this' is a raw pointer to ref-countable type 'RefCountableWithLambdaCapturingThis' [webkit.UncountedLambdaCapturesChecker]}} nonTrivial(); }); }); @@ -501,22 +501,11 @@ void lambda_converted_to_function(RefCountable* obj) { callFunction([&]() { obj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} }); callFunctionOpaque([&]() { obj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} - }); -} - -void capture_copy_in_lambda(CheckedObj& checked) { - callFunctionOpaque([checked]() mutable { - checked.method(); - }); - auto* ptr = &checked; - callFunctionOpaque([ptr]() mutable { - // expected-warning@-1{{Captured raw-pointer 'ptr' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} - ptr->method(); + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} }); } @@ -616,7 +605,7 @@ void doWhateverWith(WTF::ScopeExit& obj); void scope_exit_with_side_effect(RefCountable* obj) { auto scope = WTF::makeScopeExit([&] { obj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} }); doWhateverWith(scope); } @@ -624,14 +613,14 @@ void scope_exit_with_side_effect(RefCountable* obj) { void scope_exit_static(RefCountable* obj) { static auto scope = WTF::makeScopeExit([&] { obj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} }); } WTF::Function<void()> scope_exit_take_lambda(RefCountable* obj) { auto scope = WTF::makeScopeExit([&] { obj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} }); return scope.take(); } @@ -640,7 +629,7 @@ WTF::Function<void()> scope_exit_take_lambda(RefCountable* obj) { void scope_exit_release(RefCountable* obj) { auto scope = WTF::makeScopeExit([&] { obj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} }); scope.release(); } @@ -666,7 +655,7 @@ void bad_visit(Visitor&, ObjectType*) { void static_visitor(RefCountable* obj) { static auto visitor = WTF::makeVisitor([&] { obj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} }); } @@ -674,10 +663,10 @@ void make_visitor_with_multiple_lambdas(RefCountable* obj) { auto* otherObj = make_obj(); auto visitor = WTF::makeVisitor([&] { obj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} }, [&] { otherObj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'otherObj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'otherObj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} }); bad_visit(visitor, obj); } @@ -685,7 +674,7 @@ void make_visitor_with_multiple_lambdas(RefCountable* obj) { void bad_use_visitor(RefCountable* obj) { auto visitor = WTF::makeVisitor([&] { obj->method(); - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}} }); bad_visit(visitor, obj); } @@ -694,14 +683,14 @@ class LambdaInConstructorDestructor { public: LambdaInConstructorDestructor() { call([this]() { - // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'this' is a raw pointer to ref-countable type 'LambdaInConstructorDestructor' [webkit.UncountedLambdaCapturesChecker]}} doWork(); }); } ~LambdaInConstructorDestructor() { call([this]() { - // expected-warning@-1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'this' is a raw pointer to ref-countable type 'LambdaInConstructorDestructor' [webkit.UncountedLambdaCapturesChecker]}} doWork(); }); } 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 4e024dea7037a..15337cb011267 100644 --- a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-arc.mm +++ b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures-arc.mm @@ -139,21 +139,21 @@ void raw_ptr() { auto cf = make_cf(); auto bar1 = [cf](){ - // expected-warning@-1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} CFArrayAppendValue(cf, nullptr); }; auto bar2 = [&cf](){ - // expected-warning@-1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} CFArrayAppendValue(cf, nullptr); }; auto bar3 = [&](){ CFArrayAppendValue(cf, nullptr); - // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} cf = nullptr; }; auto bar4 = [=](){ CFArrayAppendValue(cf, nullptr); - // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} }; auto os = make_os(); @@ -255,7 +255,7 @@ void noescape_lambda() { CFArrayAppendValue(someCF, nullptr); }, [&](CFIndex count) { CFArrayAppendValue(someCF, nullptr); - // expected-warning@-1{{Implicitly captured reference 'someCF' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'someCF' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); dispatch_queue_t someOS = make_os(); @@ -277,13 +277,13 @@ void lambda_converted_to_function(SomeObj* obj, CFMutableArrayRef cf, dispatch_q callFunction([&]() { [obj doWork]; CFArrayAppendValue(cf, nullptr); - // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [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]}} + // expected-warning@-1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} dispatch_queue_get_label(os); }); } diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm index 76a1da7707a2a..9b39eeb9350db 100644 --- a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm +++ b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm @@ -124,61 +124,61 @@ explicit CallableWrapper(CallableType& callable) void raw_ptr() { SomeObj* obj = make_obj(); auto foo1 = [obj](){ - // expected-warning@-1{{Captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'obj' is a raw pointer to retainable type 'SomeObj' [alpha.webkit.UnretainedLambdaCapturesChecker]}} [obj doWork]; }; call(foo1); auto foo2 = [&obj](){ - // expected-warning@-1{{Captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'obj' is a raw pointer to retainable type 'SomeObj' [alpha.webkit.UnretainedLambdaCapturesChecker]}} [obj doWork]; }; auto foo3 = [&](){ [obj doWork]; - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to retainable type 'SomeObj' [alpha.webkit.UnretainedLambdaCapturesChecker]}} obj = nullptr; }; auto foo4 = [=](){ [obj doWork]; - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to retainable type 'SomeObj' [alpha.webkit.UnretainedLambdaCapturesChecker]}} }; auto cf = make_cf(); auto bar1 = [cf](){ - // expected-warning@-1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} CFArrayAppendValue(cf, nullptr); }; auto bar2 = [&cf](){ - // expected-warning@-1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} CFArrayAppendValue(cf, nullptr); }; auto bar3 = [&](){ CFArrayAppendValue(cf, nullptr); - // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} cf = nullptr; }; auto bar4 = [=](){ CFArrayAppendValue(cf, nullptr); - // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} }; auto os = make_os(); auto baz1 = [os](){ - // expected-warning@-1{{Captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Captured variable 'os' is a retainable type 'dispatch_queue_t' [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]}} + // expected-warning@-1{{Captured variable 'os' is a retainable type 'dispatch_queue_t' [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]}} + // expected-warning@-1{{Implicitly captured variable 'os' is a retainable type 'dispatch_queue_t' [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]}} + // expected-warning@-1{{Implicitly captured variable 'os' is a retainable type 'dispatch_queue_t' [alpha.webkit.UnretainedLambdaCapturesChecker]}} }; call(foo1); @@ -219,6 +219,7 @@ void raw_ptr() { }; // no warning. call(baz5); + } void quiet() { @@ -279,7 +280,7 @@ void noescape_lambda() { [otherObj doWork]; }, [&](SomeObj *obj) { [otherObj doWork]; - // expected-warning@-1{{Implicitly captured raw-pointer 'otherObj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'otherObj' is a raw pointer to retainable type 'SomeObj' [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); ([&] { [someObj doWork]; @@ -290,7 +291,7 @@ void noescape_lambda() { CFArrayAppendValue(someCF, nullptr); }, [&](CFIndex count) { CFArrayAppendValue(someCF, nullptr); - // expected-warning@-1{{Implicitly captured reference 'someCF' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'someCF' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); dispatch_queue_t someOS = make_os(); @@ -298,7 +299,7 @@ void noescape_lambda() { 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]}} + // expected-warning@-1{{Implicitly captured variable 'someOS' is a retainable type 'dispatch_queue_t' [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); } @@ -312,19 +313,19 @@ void lambda_converted_to_function(SomeObj* obj, CFMutableArrayRef cf, dispatch_q { callFunction([&]() { [obj doWork]; - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to retainable type 'SomeObj' [alpha.webkit.UnretainedLambdaCapturesChecker]}} CFArrayAppendValue(cf, nullptr); - // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} dispatch_queue_get_label(os); - // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'os' is a retainable type 'dispatch_queue_t' [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); callFunctionOpaque([&]() { [obj doWork]; - // expected-warning@-1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'obj' is a raw pointer to retainable type 'SomeObj' [alpha.webkit.UnretainedLambdaCapturesChecker]}} CFArrayAppendValue(cf, nullptr); - // expected-warning@-1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}} dispatch_queue_get_label(os); - // expected-warning@-1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'os' is a retainable type 'dispatch_queue_t' [alpha.webkit.UnretainedLambdaCapturesChecker]}} }); } @@ -340,12 +341,12 @@ -(void)run; @implementation ObjWithSelf -(void)doWork { auto doWork = [&] { - // expected-warning@-1{{Implicitly captured raw-pointer 'self' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'self' is a raw pointer to retainable type 'ObjWithSelf' [alpha.webkit.UnretainedLambdaCapturesChecker]}} someFunction(); [delegate doWork]; }; auto doMoreWork = [=] { - // expected-warning@-1{{Implicitly captured raw-pointer 'self' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'self' is a raw pointer to retainable type 'ObjWithSelf' [alpha.webkit.UnretainedLambdaCapturesChecker]}} someFunction(); [delegate doWork]; }; @@ -357,7 +358,7 @@ -(void)doWork { auto doAdditionalWork = [&] { someFunction(); dispatch_queue_get_label(queuePtr); - // expected-warning@-1{{Implicitly captured raw-pointer 'queuePtr' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}} + // expected-warning@-1{{Implicitly captured variable 'queuePtr' is a retainable type 'OS_dispatch_queue' [alpha.webkit.UnretainedLambdaCapturesChecker]}} }; callFunctionOpaque(doWork); callFunctionOpaque(doMoreWork); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
