[clang] [alpha.webkit.UncheckedLambdaCapturesChecker] Split unchecked lambda capture checker (PR #201044)

Ryosuke Niwa via cfe-commits cfe-commits at lists.llvm.org
Tue Jun 2 00:29:23 PDT 2026


https://github.com/rniwa updated https://github.com/llvm/llvm-project/pull/201044

>From ad6d6ec948d5f9f4a9d879981f4a6bc10af783c8 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Tue, 2 Jun 2026 00:24:55 -0700
Subject: [PATCH 1/2] [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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -1{{Implicitly captured variable 'obj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}}
+  });
+  callFunctionOpaque([&]() {
+    obj->method();
+    // expected-warning at -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 at -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 at -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 at -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 at -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 at -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 at -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 at -1{{Implicitly captured variable 'obj' is a raw pointer to CheckedPtr capable type 'CheckedObj' [alpha.webkit.UncheckedLambdaCapturesChecker]}}
+  }, [&] {
+    otherObj->method();
+    // expected-warning at -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 at -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 at -1{{Captured variable 'this' is a raw pointer to CheckedPtr capable type 'LambdaInConstructorDestructor' [alpha.webkit.UncheckedLambdaCapturesChecker]}}
+      doWork();
+    });
+  }
+
+  ~LambdaInConstructorDestructor() {
+    call([this]() {
+      // expected-warning at -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 at -1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+      // expected-warning at -1{{Captured variable 'this' is a raw pointer to ref-countable type 'Obj' [webkit.UncountedLambdaCapturesChecker]}}
       bar([this, foo = *baz, foo2 = !baz](auto&&) {
-        // expected-warning at -1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+        // expected-warning at -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 at -1{{Captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'ref_countable' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+  // expected-warning at -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 at -1{{Implicitly captured reference 'ref_countable_ref' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+  // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'otherObj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+      // expected-warning at -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 at -1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+      // expected-warning at -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 at -1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+      // expected-warning at -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 at -1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+      // expected-warning at -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 at -1{{Captured raw-pointer 'x' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+      // expected-warning at -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 at -1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+        // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'obj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}}
   });
   callFunctionOpaque([&]() {
     obj->method();
-    // expected-warning at -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 at -1{{Captured raw-pointer 'ptr' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
-    ptr->method();
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'obj' is a raw pointer to ref-countable type 'RefCountable' [webkit.UncountedLambdaCapturesChecker]}}
   }, [&] {
     otherObj->method();
-    // expected-warning at -1{{Implicitly captured raw-pointer 'otherObj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'obj' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+      // expected-warning at -1{{Captured variable 'this' is a raw pointer to ref-countable type 'LambdaInConstructorDestructor' [webkit.UncountedLambdaCapturesChecker]}}
       doWork();
     });
   }
 
   ~LambdaInConstructorDestructor() {
     call([this]() {
-      // expected-warning at -1{{Captured raw-pointer 'this' to uncounted type is unsafe [webkit.UncountedLambdaCapturesChecker]}}
+      // expected-warning at -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 at -1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     CFArrayAppendValue(cf, nullptr);
   };
   auto bar2 = [&cf](){
-    // expected-warning at -1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     CFArrayAppendValue(cf, nullptr);
   };
   auto bar3 = [&](){
     CFArrayAppendValue(cf, nullptr);
-    // expected-warning at -1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     cf = nullptr;
   };
   auto bar4 = [=](){
     CFArrayAppendValue(cf, nullptr);
-    // expected-warning at -1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured reference 'someCF' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Captured variable 'obj' is a raw pointer to retainable type 'SomeObj' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     [obj doWork];
   };
   call(foo1);
 
   auto foo2 = [&obj](){
-    // expected-warning at -1{{Captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Captured variable 'obj' is a raw pointer to retainable type 'SomeObj' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     [obj doWork];
   };
   auto foo3 = [&](){
     [obj doWork];
-    // expected-warning at -1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'obj' is a raw pointer to retainable type 'SomeObj' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     obj = nullptr;
   };
   auto foo4 = [=](){
     [obj doWork];
-    // expected-warning at -1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     CFArrayAppendValue(cf, nullptr);
   };
   auto bar2 = [&cf](){
-    // expected-warning at -1{{Captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     CFArrayAppendValue(cf, nullptr);
   };
   auto bar3 = [&](){
     CFArrayAppendValue(cf, nullptr);
-    // expected-warning at -1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     cf = nullptr;
   };
   auto bar4 = [=](){
     CFArrayAppendValue(cf, nullptr);
-    // expected-warning at -1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
   };
 
   auto os = make_os();
   auto baz1 = [os](){
-    // expected-warning at -1{{Captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Captured variable 'os' is a retainable type 'dispatch_queue_t' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     dispatch_queue_get_label(os);
   };
   auto baz2 = [&os](){
-    // expected-warning at -1{{Captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'otherObj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured reference 'someCF' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured reference 'someOS' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'obj' is a raw pointer to retainable type 'SomeObj' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     CFArrayAppendValue(cf, nullptr);
-    // expected-warning at -1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     dispatch_queue_get_label(os);
-    // expected-warning at -1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'os' is a retainable type 'dispatch_queue_t' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
   });
   callFunctionOpaque([&]() {
     [obj doWork];
-    // expected-warning at -1{{Implicitly captured raw-pointer 'obj' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'obj' is a raw pointer to retainable type 'SomeObj' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     CFArrayAppendValue(cf, nullptr);
-    // expected-warning at -1{{Implicitly captured reference 'cf' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'cf' is a retainable type 'CFMutableArrayRef' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     dispatch_queue_get_label(os);
-    // expected-warning at -1{{Implicitly captured reference 'os' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'self' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'self' is a raw pointer to retainable type 'ObjWithSelf' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
     someFunction();
     [delegate doWork];
   };
   auto doMoreWork = [=] {
-    // expected-warning at -1{{Implicitly captured raw-pointer 'self' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -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 at -1{{Implicitly captured raw-pointer 'queuePtr' to unretained type is unsafe [alpha.webkit.UnretainedLambdaCapturesChecker]}}
+    // expected-warning at -1{{Implicitly captured variable 'queuePtr' is a retainable type 'OS_dispatch_queue' [alpha.webkit.UnretainedLambdaCapturesChecker]}}
   };
   callFunctionOpaque(doWork);
   callFunctionOpaque(doMoreWork);

>From 02acfa78aa92a1bffb4d854b68fa8171497189d5 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Tue, 2 Jun 2026 00:29:07 -0700
Subject: [PATCH 2/2] Fix formatting.

---
 .../WebKit/RawPtrRefLambdaCapturesChecker.cpp  | 18 ++++++++++--------
 1 file changed, 10 insertions(+), 8 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
index 4d0381a82f250..013608c0ff32c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
@@ -606,19 +606,20 @@ class RawPtrRefLambdaCapturesChecker
     Os << typeName();
 
     if (auto *RD = T->getPointeeType()->getAsRecordDecl()) {
-     Os << " ";
-     printQuotedQualifiedName(Os, RD);
-   } else if (auto *ObjCDecl = getObjCDecl(T)) {
-     Os << " ";
-     printQuotedQualifiedName(Os, ObjCDecl);
-   }
+      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 variable") {}
+      : RawPtrRefLambdaCapturesChecker("Lambda capture of uncounted variable") {
+  }
 
   std::optional<bool> isUnsafePtr(QualType QT) const final {
     return isUncountedPtr(QT.getCanonicalType());
@@ -634,7 +635,8 @@ class UncountedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
 class UncheckedLambdaCapturesChecker : public RawPtrRefLambdaCapturesChecker {
 public:
   UncheckedLambdaCapturesChecker()
-      : RawPtrRefLambdaCapturesChecker("Lambda capture of unchecked variable") {}
+      : RawPtrRefLambdaCapturesChecker("Lambda capture of unchecked variable") {
+  }
 
   std::optional<bool> isUnsafePtr(QualType QT) const final {
     return isUncheckedPtr(QT.getCanonicalType());



More information about the cfe-commits mailing list