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

>From a1b8315f466fd6bca617af2cac037eff01a1c8e3 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <[email protected]>
Date: Sat, 6 Jun 2026 13:56:04 -0700
Subject: [PATCH 1/3] [webkit.UncountedLambdaCapturesChecker] Improve
 diagnostics

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.
---
 .../WebKit/RawPtrRefLambdaCapturesChecker.cpp | 93 +++++++++++++++----
 .../Analysis/Checkers/WebKit/mock-types.h     |  1 +
 ...mbda-captures-decl-protects-this-crash.cpp |  4 +-
 .../WebKit/uncounted-lambda-captures.cpp      | 52 +++++------
 .../WebKit/unretained-lambda-captures-arc.mm  | 14 +--
 .../WebKit/unretained-lambda-captures.mm      | 49 +++++-----
 6 files changed, 137 insertions(+), 76 deletions(-)

diff --git 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
index 287c7dd039dd8..f68d0286d47da 100644
--- 
a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
+++ 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
@@ -542,25 +542,39 @@ 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 ";
+    printPointer(Os, T);
 
     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,19 +586,39 @@ class RawPtrRefLambdaCapturesChecker
       Os << "Implicitly captured ";
     }
 
-    Os << "raw-pointer 'this' to " << ptrKind(T) << " type is unsafe.";
+    Os << "variable 'this' is a raw pointer to " << ptrKind(T);
+    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, QualType QT) const {
+    auto *T = QT.getTypePtrOrNull();
+    T = T->getUnqualifiedDesugaredType();
+    bool IsPtr = isa<PointerType>(T) || isa<ObjCObjectPointerType>(T);
+    Os << (IsPtr ? "raw pointer" : "raw reference") << " to ";
+    Os << ptrKind(QT);
+
+    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);
@@ -603,9 +637,10 @@ class UncountedLambdaCapturesChecker : public 
RawPtrRefLambdaCapturesChecker {
   }
 
   const char *ptrKind(QualType QT) const final {
-    if (isUncounted(QT))
-      return "uncounted";
-    return "unchecked";
+    auto IsCountable = isUncountedPtr(QT);
+    if (IsCountable && *IsCountable)
+      return "ref-countable type";
+    return "CheckedPtr capable type";
   }
 };
 
@@ -627,7 +662,31 @@ class UnretainedLambdaCapturesChecker : public 
RawPtrRefLambdaCapturesChecker {
     return isRetainPtrOrOSPtr(Name);
   }
 
-  const char *ptrKind(QualType QT) const final { return "unretained"; }
+  const char *ptrKind(QualType QT) const final { return "retainable type"; }
+
+  void printPointer(llvm::raw_svector_ostream &Os, const QualType QT) const 
final {
+    auto *T = QT.getTypePtrOrNull();
+    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 << ptrKind(QT) << " ";
+            printQuotedQualifiedName(Os, P);
+            return;
+          }
+        }
+      }
+    }
+    if (!isa<ObjCObjectPointerType>(T) && T->getAs<TypedefType>()) {
+      auto Typedef = T->getAs<TypedefType>();
+      assert(Typedef);
+      Os << ptrKind(QT) << " ";
+      printQuotedQualifiedName(Os, Typedef->getDecl());
+      return;
+    }
+    return RawPtrRefLambdaCapturesChecker::printPointer(Os, QT);
+  }
 };
 
 } // namespace
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index de5c2d35f2408..078fcba9c9001 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -308,6 +308,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/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..8cede12df4755 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,11 +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]}}
+    // expected-warning@-1{{Implicitly captured variable 'obj' is a raw 
pointer to ref-countable type 'RefCountable' 
[webkit.UncountedLambdaCapturesChecker]}}
   });
 }
 
@@ -515,7 +515,7 @@ void capture_copy_in_lambda(CheckedObj& checked) {
   });
   auto* ptr = &checked;
   callFunctionOpaque([ptr]() mutable {
-    // expected-warning@-1{{Captured raw-pointer 'ptr' to uncounted type is 
unsafe [webkit.UncountedLambdaCapturesChecker]}}
+    // expected-warning@-1{{Captured variable 'ptr' is a raw pointer to 
CheckedPtr capable type 'CheckedObj' [webkit.UncountedLambdaCapturesChecker]}}
     ptr->method();
   });
 }
@@ -616,7 +616,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 +624,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 +640,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 +666,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 +674,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 +685,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 +694,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);

>From 5bc73d4d29c19704a3cb539fea52439c771569a1 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <[email protected]>
Date: Sat, 6 Jun 2026 14:02:45 -0700
Subject: [PATCH 2/3] [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.
---
 clang/docs/analyzer/checkers.rst              |  17 +
 .../clang/StaticAnalyzer/Checkers/Checkers.td |   4 +
 .../WebKit/RawPtrRefLambdaCapturesChecker.cpp |  69 +-
 .../WebKit/unchecked-lambda-captures.cpp      | 713 ++++++++++++++++++
 .../WebKit/uncounted-lambda-captures.cpp      |  11 -
 5 files changed, 775 insertions(+), 39 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 f68d0286d47da..013608c0ff32c 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 {
@@ -554,7 +554,8 @@ class RawPtrRefLambdaCapturesChecker
       Os << " is a ";
     else
       Os << " contains a ";
-    printPointer(Os, T);
+    auto *CapturedType = T.getTypePtrOrNull();
+    printPointer(Os, CapturedType);
 
     PathDiagnosticLocation BSLoc(Location, BR->getSourceManager());
     auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
@@ -586,7 +587,7 @@ class RawPtrRefLambdaCapturesChecker
       Os << "Implicitly captured ";
     }
 
-    Os << "variable 'this' is a raw pointer to " << ptrKind(T);
+    Os << "variable 'this' is a raw pointer to " << typeName();
     if (auto *RD = T->getPointeeCXXRecordDecl()) {
       Os << " ";
       printQuotedQualifiedName(Os, RD);
@@ -597,12 +598,12 @@ class RawPtrRefLambdaCapturesChecker
     BR->emitReport(std::move(Report));
   }
 
-  virtual void printPointer(llvm::raw_svector_ostream &Os, QualType QT) const {
-    auto *T = QT.getTypePtrOrNull();
+  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 << ptrKind(QT);
+    Os << typeName();
 
     if (auto *RD = T->getPointeeType()->getAsRecordDecl()) {
       Os << " ";
@@ -621,27 +622,31 @@ class UncountedLambdaCapturesChecker : public 
RawPtrRefLambdaCapturesChecker {
   }
 
   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 *ptrKind(QualType QT) const final {
-    auto IsCountable = isUncountedPtr(QT);
-    if (IsCountable && *IsCountable)
-      return "ref-countable type";
-    return "CheckedPtr capable type";
+  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());
+  }
+
+  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 {
@@ -662,16 +667,15 @@ class UnretainedLambdaCapturesChecker : public 
RawPtrRefLambdaCapturesChecker {
     return isRetainPtrOrOSPtr(Name);
   }
 
-  const char *ptrKind(QualType QT) const final { return "retainable type"; }
+  const char *typeName() const final { return "retainable type"; }
 
-  void printPointer(llvm::raw_svector_ostream &Os, const QualType QT) const 
final {
-    auto *T = QT.getTypePtrOrNull();
+  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 << ptrKind(QT) << " ";
+            Os << typeName() << " ";
             printQuotedQualifiedName(Os, P);
             return;
           }
@@ -681,11 +685,11 @@ class UnretainedLambdaCapturesChecker : public 
RawPtrRefLambdaCapturesChecker {
     if (!isa<ObjCObjectPointerType>(T) && T->getAs<TypedefType>()) {
       auto Typedef = T->getAs<TypedefType>();
       assert(Typedef);
-      Os << ptrKind(QT) << " ";
+      Os << typeName() << " ";
       printQuotedQualifiedName(Os, Typedef->getDecl());
       return;
     }
-    return RawPtrRefLambdaCapturesChecker::printPointer(Os, QT);
+    return RawPtrRefLambdaCapturesChecker::printPointer(Os, T);
   }
 };
 
@@ -700,6 +704,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/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.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
index 8cede12df4755..9c8204b5454c2 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
@@ -509,17 +509,6 @@ void lambda_converted_to_function(RefCountable* obj)
   });
 }
 
-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' [webkit.UncountedLambdaCapturesChecker]}}
-    ptr->method();
-  });
-}
-
 struct TemplateFunctionCallsLambda {
   void ref() const;
   void deref() const;

>From b3a956ad68372542102d51bcb182762dd045ed45 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <[email protected]>
Date: Sat, 6 Jun 2026 14:13:45 -0700
Subject: [PATCH 3/3] Share more mock types in std and WTF across tests.

---
 .../WebKit/call-args-wtf-containers.cpp       |  19 --
 .../Analysis/Checkers/WebKit/mock-types.h     | 209 ++++++++++++++++--
 .../WebKit/unchecked-lambda-captures.cpp      | 180 ---------------
 .../WebKit/uncounted-lambda-captures.cpp      | 180 ---------------
 .../WebKit/unretained-lambda-captures.mm      | 105 +--------
 5 files changed, 194 insertions(+), 499 deletions(-)

diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp 
b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp
index 17e25d9a62703..45e40e051c18e 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-wtf-containers.cpp
@@ -104,25 +104,6 @@ namespace WTF {
     unsigned m_size { 0 };
   };
 
-  template <typename T, typename S>
-  class HashMap {
-  public:
-    struct Item {
-      T key;
-      S value;
-    };
-
-    template <typename U> Item* find(U&) const;
-    template <typename U> bool contains(U&) const;
-    template <typename U> S* get(U&) const;
-    template <typename U> S* inlineGet(U&) const;
-    template <typename U> void add(U&) const;
-    template <typename U> void remove(U&) const;
-
-  private:
-    Item* m_table { nullptr };
-  };
-
   template <typename T>
   class WeakHashSet {
   public:
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h 
b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index 078fcba9c9001..a2aba140d7b6e 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -11,7 +11,19 @@ template <typename T> struct remove_reference<T&> {
 typedef T type;
 };
 
-template<typename T> typename remove_reference<T>::type&& move(T&& t);
+template <typename T>
+T&& move(T& t) {
+  return static_cast<T&&>(t);
+}
+
+}
+
+#endif
+
+#ifndef mock_types_1103988513531
+#define mock_types_1103988513531
+
+namespace std {
 
 template<typename T, typename U>
 T exchange(T& t, U&& u)
@@ -29,21 +41,6 @@ T exchange(T& t, decltype(nullptr))
   return r;
 }
 
-}
-
-#endif
-
-namespace WTF {
-
-template<typename T> typename std::remove_reference<T>::type&& move(T&& t);
-
-}
-
-#ifndef mock_types_1103988513531
-#define mock_types_1103988513531
-
-namespace std {
-
 template <typename T>
 class unique_ptr {
 private:
@@ -68,6 +65,186 @@ class unique_ptr {
   explicit operator bool() const { return !!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(), end = collection.end(); it != end; 
++it) {
+      if (!predicate(*it))
+        return false;
+    }
+    return true;
+  }
+};
+inline constexpr auto all_of = all_of_impl {};
+
+}
+
+}
+
+namespace WTF {
+
+template<typename T> typename std::remove_reference<T>::type&& move(T&& t);
+
+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)>&);
+
+  template <typename U> ValueType* find(U&) const;
+  template <typename U> bool contains(U&) const;
+  template <typename U> ValueType* get(U&) const;
+  template <typename U> ValueType* inlineGet(U&) const;
+  template <typename U> void add(U&) const;
+  template <typename U> void remove(U&) const;
+
+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();
+}
+
 };
 
 template<typename T>
diff --git a/clang/test/Analysis/Checkers/WebKit/unchecked-lambda-captures.cpp 
b/clang/test/Analysis/Checkers/WebKit/unchecked-lambda-captures.cpp
index c63e44b4e51cf..611c62063db0a 100644
--- a/clang/test/Analysis/Checkers/WebKit/unchecked-lambda-captures.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/unchecked-lambda-captures.cpp
@@ -2,186 +2,6 @@
 
 #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();
 };
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp 
b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
index 9c8204b5454c2..fc8230838c5cb 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
@@ -2,186 +2,6 @@
 
 #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();
 };
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm 
b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm
index 9b39eeb9350db..aad6cd245f1bd 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-lambda-captures.mm
@@ -1,111 +1,8 @@
 // RUN: %clang_analyze_cc1 
-analyzer-checker=alpha.webkit.UnretainedLambdaCapturesChecker -verify %s
 
+#include "mock-types.h"
 #include "objc-mock-types.h"
 
-namespace std {
-
-template <typename T>
-class unique_ptr {
-private:
-  T *t;
-
-public:
-  unique_ptr() : t(nullptr) { }
-  unique_ptr(T *t) : t(t) { }
-  ~unique_ptr() {
-    if (t)
-      delete t;
-  }
-  template <typename U> unique_ptr(unique_ptr<U>&& u)
-    : t(u.t)
-  {
-    u.t = nullptr;
-  }
-  T *get() const { return t; }
-  T *operator->() const { return t; }
-  T &operator*() const { return *t; }
-  unique_ptr &operator=(T *) { return *this; }
-  explicit operator bool() const { return !!t; }
-};
-
-};
-
-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 };
-};
-
-} // namespace WTF
-
 struct A {
   static void b();
 };

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to