Skip to content

Conversation

@rniwa
Copy link
Contributor

@rniwa rniwa commented Dec 5, 2025

This PR adds support for treating WTF::move like std::move in various WebKit checkers.

@rniwa rniwa assigned t-rasmud and unassigned t-rasmud Dec 5, 2025
@rniwa rniwa requested a review from t-rasmud December 5, 2025 09:05
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer labels Dec 5, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 5, 2025

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Ryosuke Niwa (rniwa)

Changes

This PR adds support for treating WTF::move like std::move in various WebKit checkers.


Full diff: https://github.com/llvm/llvm-project/pull/170820.diff

11 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp (+5-5)
  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp (+1-2)
  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp (+9)
  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h (+3)
  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp (+2-5)
  • (modified) clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp (+1)
  • (modified) clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm (+3)
  • (modified) clang/test/Analysis/Checkers/WebKit/mock-types.h (+6)
  • (modified) clang/test/Analysis/Checkers/WebKit/objc-mock-types.h (+8-2)
  • (modified) clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp (+8)
  • (modified) clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp (+1)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 84adbf318e9f8..8f104feffa66b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -132,11 +132,6 @@ bool tryToFindPtrOrigin(
         }
       }
 
-      if (call->isCallToStdMove() && call->getNumArgs() == 1) {
-        E = call->getArg(0)->IgnoreParenCasts();
-        continue;
-      }
-
       if (auto *callee = call->getDirectCallee()) {
         if (isCtorOfSafePtr(callee)) {
           if (StopAtFirstRefCountedObj)
@@ -146,6 +141,11 @@ bool tryToFindPtrOrigin(
           continue;
         }
 
+        if (isStdOrWTFMove(callee) && call->getNumArgs() == 1) {
+          E = call->getArg(0)->IgnoreParenCasts();
+          continue;
+        }
+
         if (isSafePtrType(callee->getReturnType()))
           return callback(E, true);
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
index 1d4e6dd572749..59336328a2823 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
@@ -267,8 +267,7 @@ class ForwardDeclChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
       ArgExpr = ArgExpr->IgnoreParenCasts();
       if (auto *InnerCE = dyn_cast<CallExpr>(ArgExpr)) {
         auto *InnerCallee = InnerCE->getDirectCallee();
-        if (InnerCallee && InnerCallee->isInStdNamespace() &&
-            safeGetName(InnerCallee) == "move" && InnerCE->getNumArgs() == 1) {
+        if (isStdOrWTFMove(InnerCallee) && InnerCE->getNumArgs() == 1) {
           ArgExpr = InnerCE->getArg(0);
           continue;
         }
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 5cd894af1fd65..3636b37dea632 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -185,6 +185,15 @@ bool isCtorOfSafePtr(const clang::FunctionDecl *F) {
          isCtorOfRetainPtrOrOSPtr(F);
 }
 
+bool isStdOrWTFMove(const clang::FunctionDecl *F) {
+  auto FnName = safeGetName(F);
+  auto *Namespace = F->getParent();
+  if (!Namespace)
+    return false;
+  auto NsName = safeGetName(Namespace);
+  return (NsName == "WTF" || NsName == "std") && FnName == "move";
+}
+
 template <typename Predicate>
 static bool isPtrOfType(const clang::QualType T, Predicate Pred) {
   QualType type = T;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 12e2e2d75b75d..96250f7f851e9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -134,6 +134,9 @@ bool isCtorOfCheckedPtr(const clang::FunctionDecl *F);
 /// uncounted parameter, false if not.
 bool isCtorOfSafePtr(const clang::FunctionDecl *F);
 
+/// \returns true if \p F is std::move or WTF::move.
+bool isStdOrWTFMove(const clang::FunctionDecl *F);
+
 /// \returns true if \p Name is RefPtr, Ref, or its variant, false if not.
 bool isRefType(const std::string &Name);
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
index f60d1936b7584..d9b5261612a36 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
@@ -416,12 +416,9 @@ class RawPtrRefLambdaCapturesChecker
             return false;
           }
           if (auto *CE = dyn_cast<CallExpr>(Arg)) {
-            if (CE->isCallToStdMove() && CE->getNumArgs() == 1) {
-              Arg = CE->getArg(0)->IgnoreParenCasts();
-              continue;
-            }
             if (auto *Callee = CE->getDirectCallee()) {
-              if (isCtorOfSafePtr(Callee) && CE->getNumArgs() == 1) {
+              if ((isStdOrWTFMove(Callee) || isCtorOfSafePtr(Callee)) &&
+                  CE->getNumArgs() == 1) {
                 Arg = CE->getArg(0)->IgnoreParenCasts();
                 continue;
               }
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp
index e9f01c8ed7540..b257d09236c07 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp
@@ -80,6 +80,7 @@ namespace call_with_std_move {
 void consume(CheckedObj&&);
 void foo(CheckedObj&& obj) {
   consume(std::move(obj));
+  consume(WTF::move(obj));
 }
 
 }
diff --git a/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm b/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm
index 8aad838b71b35..1f0a0281eaf7a 100644
--- a/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm
+++ b/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm
@@ -44,6 +44,7 @@ void opaque_call_arg(Obj* obj, Obj&& otherObj, const RefPtr<Obj>& safeObj, WeakP
   receive_obj_ref(*obj);
   receive_obj_ptr(&*obj);
   receive_obj_rref(std::move(otherObj));
+  receive_obj_rref(WTF::move(otherObj));
   receive_obj_ref(*safeObj.get());
   receive_obj_ptr(weakObj.get());
   // expected-warning@-1{{Call argument for parameter 'p' uses a forward declared type 'Obj *'}}
@@ -59,6 +60,7 @@ void rval(Obj&& arg) {
   auto &&obj = provide_obj_rval();
   // expected-warning@-1{{Local variable 'obj' uses a forward declared type 'Obj &&'}}
   receive_obj_rval(std::move(arg));
+  receive_obj_rval(WTF::move(arg));
 }
 
 ObjCObj *provide_objcobj();
@@ -84,6 +86,7 @@ void construct_ptr(Obj&& arg) {
   WrapperObj wrapper2(provide_obj_ref());
   // expected-warning@-1{{Call argument for parameter 'obj' uses a forward declared type 'Obj &'}}
   WrapperObj wrapper3(std::move(arg));
+  WrapperObj wrapper4(WTF::move(arg));
 }
 
 JSStringRef provide_opaque_ptr();
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index 7055a94753a37..8a24a3c64e0e4 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -17,6 +17,12 @@ template<typename T> typename remove_reference<T>::type&& move(T&& t);
 
 #endif
 
+namespace WTF {
+
+template<typename T> typename std::remove_reference<T>::type&& move(T&& t);
+
+}
+
 #ifndef mock_types_1103988513531
 #define mock_types_1103988513531
 
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index edf40115afa19..124821a8ded55 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -17,6 +17,12 @@ template<typename T> typename remove_reference<T>::type&& move(T&& t);
 
 #endif
 
+namespace WTF {
+
+template<typename T> typename std::remove_reference<T>::type&& move(T&& t);
+
+}
+
 namespace std {
 
 template <bool, typename U = void> struct enable_if {
@@ -453,7 +459,7 @@ template<typename T> class OSObjectPtr {
     }
 
     OSObjectPtr(T ptr)
-        : m_ptr(std::move(ptr))
+        : m_ptr(WTF::move(ptr))
     {
         if (m_ptr)
             retainOSObject(m_ptr);
@@ -483,7 +489,7 @@ template<typename T> class OSObjectPtr {
 
     OSObjectPtr& operator=(T other)
     {
-        OSObjectPtr ptr = std::move(other);
+        OSObjectPtr ptr = WTF::move(other);
         swap(ptr);
         return *this;
     }
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
index fd1eecdda64fd..8eb15f4f77973 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
@@ -437,6 +437,14 @@ struct RefCountableWithLambdaCapturingThis {
       });
     });
   }
+
+  void method_nested_lambda4() {
+    callAsync([this, protectedThis = RefPtr { this }] {
+      callAsync([this, protectedThis = WTF::move(*protectedThis)] {
+        nonTrivial();
+      });
+    });
+  }
 };
 
 struct NonRefCountableWithLambdaCapturingThis {
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
index a9cd77c066f6f..b83eaedf264e4 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -387,6 +387,7 @@ class RefCounted {
   unsigned trivial69() { return offsetof(OtherObj, children); }
   DerivedNumber* trivial70() { [[clang::suppress]] return static_cast<DerivedNumber*>(number); }
   unsigned trivial71() { return std::bit_cast<unsigned>(nullptr); }
+  unsigned trivial72() { Number n { 5 }; return WTF::move(n).value(); }
 
   static RefCounted& singleton() {
     static RefCounted s_RefCounted;

@llvmbot
Copy link
Member

llvmbot commented Dec 5, 2025

@llvm/pr-subscribers-clang

Author: Ryosuke Niwa (rniwa)

Changes

This PR adds support for treating WTF::move like std::move in various WebKit checkers.


Full diff: https://github.com/llvm/llvm-project/pull/170820.diff

11 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp (+5-5)
  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp (+1-2)
  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp (+9)
  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h (+3)
  • (modified) clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp (+2-5)
  • (modified) clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp (+1)
  • (modified) clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm (+3)
  • (modified) clang/test/Analysis/Checkers/WebKit/mock-types.h (+6)
  • (modified) clang/test/Analysis/Checkers/WebKit/objc-mock-types.h (+8-2)
  • (modified) clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp (+8)
  • (modified) clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp (+1)
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 84adbf318e9f8..8f104feffa66b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -132,11 +132,6 @@ bool tryToFindPtrOrigin(
         }
       }
 
-      if (call->isCallToStdMove() && call->getNumArgs() == 1) {
-        E = call->getArg(0)->IgnoreParenCasts();
-        continue;
-      }
-
       if (auto *callee = call->getDirectCallee()) {
         if (isCtorOfSafePtr(callee)) {
           if (StopAtFirstRefCountedObj)
@@ -146,6 +141,11 @@ bool tryToFindPtrOrigin(
           continue;
         }
 
+        if (isStdOrWTFMove(callee) && call->getNumArgs() == 1) {
+          E = call->getArg(0)->IgnoreParenCasts();
+          continue;
+        }
+
         if (isSafePtrType(callee->getReturnType()))
           return callback(E, true);
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
index 1d4e6dd572749..59336328a2823 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
@@ -267,8 +267,7 @@ class ForwardDeclChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
       ArgExpr = ArgExpr->IgnoreParenCasts();
       if (auto *InnerCE = dyn_cast<CallExpr>(ArgExpr)) {
         auto *InnerCallee = InnerCE->getDirectCallee();
-        if (InnerCallee && InnerCallee->isInStdNamespace() &&
-            safeGetName(InnerCallee) == "move" && InnerCE->getNumArgs() == 1) {
+        if (isStdOrWTFMove(InnerCallee) && InnerCE->getNumArgs() == 1) {
           ArgExpr = InnerCE->getArg(0);
           continue;
         }
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 5cd894af1fd65..3636b37dea632 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -185,6 +185,15 @@ bool isCtorOfSafePtr(const clang::FunctionDecl *F) {
          isCtorOfRetainPtrOrOSPtr(F);
 }
 
+bool isStdOrWTFMove(const clang::FunctionDecl *F) {
+  auto FnName = safeGetName(F);
+  auto *Namespace = F->getParent();
+  if (!Namespace)
+    return false;
+  auto NsName = safeGetName(Namespace);
+  return (NsName == "WTF" || NsName == "std") && FnName == "move";
+}
+
 template <typename Predicate>
 static bool isPtrOfType(const clang::QualType T, Predicate Pred) {
   QualType type = T;
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
index 12e2e2d75b75d..96250f7f851e9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -134,6 +134,9 @@ bool isCtorOfCheckedPtr(const clang::FunctionDecl *F);
 /// uncounted parameter, false if not.
 bool isCtorOfSafePtr(const clang::FunctionDecl *F);
 
+/// \returns true if \p F is std::move or WTF::move.
+bool isStdOrWTFMove(const clang::FunctionDecl *F);
+
 /// \returns true if \p Name is RefPtr, Ref, or its variant, false if not.
 bool isRefType(const std::string &Name);
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
index f60d1936b7584..d9b5261612a36 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLambdaCapturesChecker.cpp
@@ -416,12 +416,9 @@ class RawPtrRefLambdaCapturesChecker
             return false;
           }
           if (auto *CE = dyn_cast<CallExpr>(Arg)) {
-            if (CE->isCallToStdMove() && CE->getNumArgs() == 1) {
-              Arg = CE->getArg(0)->IgnoreParenCasts();
-              continue;
-            }
             if (auto *Callee = CE->getDirectCallee()) {
-              if (isCtorOfSafePtr(Callee) && CE->getNumArgs() == 1) {
+              if ((isStdOrWTFMove(Callee) || isCtorOfSafePtr(Callee)) &&
+                  CE->getNumArgs() == 1) {
                 Arg = CE->getArg(0)->IgnoreParenCasts();
                 continue;
               }
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp
index e9f01c8ed7540..b257d09236c07 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-checked.cpp
@@ -80,6 +80,7 @@ namespace call_with_std_move {
 void consume(CheckedObj&&);
 void foo(CheckedObj&& obj) {
   consume(std::move(obj));
+  consume(WTF::move(obj));
 }
 
 }
diff --git a/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm b/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm
index 8aad838b71b35..1f0a0281eaf7a 100644
--- a/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm
+++ b/clang/test/Analysis/Checkers/WebKit/forward-decl-checker.mm
@@ -44,6 +44,7 @@ void opaque_call_arg(Obj* obj, Obj&& otherObj, const RefPtr<Obj>& safeObj, WeakP
   receive_obj_ref(*obj);
   receive_obj_ptr(&*obj);
   receive_obj_rref(std::move(otherObj));
+  receive_obj_rref(WTF::move(otherObj));
   receive_obj_ref(*safeObj.get());
   receive_obj_ptr(weakObj.get());
   // expected-warning@-1{{Call argument for parameter 'p' uses a forward declared type 'Obj *'}}
@@ -59,6 +60,7 @@ void rval(Obj&& arg) {
   auto &&obj = provide_obj_rval();
   // expected-warning@-1{{Local variable 'obj' uses a forward declared type 'Obj &&'}}
   receive_obj_rval(std::move(arg));
+  receive_obj_rval(WTF::move(arg));
 }
 
 ObjCObj *provide_objcobj();
@@ -84,6 +86,7 @@ void construct_ptr(Obj&& arg) {
   WrapperObj wrapper2(provide_obj_ref());
   // expected-warning@-1{{Call argument for parameter 'obj' uses a forward declared type 'Obj &'}}
   WrapperObj wrapper3(std::move(arg));
+  WrapperObj wrapper4(WTF::move(arg));
 }
 
 JSStringRef provide_opaque_ptr();
diff --git a/clang/test/Analysis/Checkers/WebKit/mock-types.h b/clang/test/Analysis/Checkers/WebKit/mock-types.h
index 7055a94753a37..8a24a3c64e0e4 100644
--- a/clang/test/Analysis/Checkers/WebKit/mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/mock-types.h
@@ -17,6 +17,12 @@ template<typename T> typename remove_reference<T>::type&& move(T&& t);
 
 #endif
 
+namespace WTF {
+
+template<typename T> typename std::remove_reference<T>::type&& move(T&& t);
+
+}
+
 #ifndef mock_types_1103988513531
 #define mock_types_1103988513531
 
diff --git a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
index edf40115afa19..124821a8ded55 100644
--- a/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
+++ b/clang/test/Analysis/Checkers/WebKit/objc-mock-types.h
@@ -17,6 +17,12 @@ template<typename T> typename remove_reference<T>::type&& move(T&& t);
 
 #endif
 
+namespace WTF {
+
+template<typename T> typename std::remove_reference<T>::type&& move(T&& t);
+
+}
+
 namespace std {
 
 template <bool, typename U = void> struct enable_if {
@@ -453,7 +459,7 @@ template<typename T> class OSObjectPtr {
     }
 
     OSObjectPtr(T ptr)
-        : m_ptr(std::move(ptr))
+        : m_ptr(WTF::move(ptr))
     {
         if (m_ptr)
             retainOSObject(m_ptr);
@@ -483,7 +489,7 @@ template<typename T> class OSObjectPtr {
 
     OSObjectPtr& operator=(T other)
     {
-        OSObjectPtr ptr = std::move(other);
+        OSObjectPtr ptr = WTF::move(other);
         swap(ptr);
         return *this;
     }
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
index fd1eecdda64fd..8eb15f4f77973 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-lambda-captures.cpp
@@ -437,6 +437,14 @@ struct RefCountableWithLambdaCapturingThis {
       });
     });
   }
+
+  void method_nested_lambda4() {
+    callAsync([this, protectedThis = RefPtr { this }] {
+      callAsync([this, protectedThis = WTF::move(*protectedThis)] {
+        nonTrivial();
+      });
+    });
+  }
 };
 
 struct NonRefCountableWithLambdaCapturingThis {
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
index a9cd77c066f6f..b83eaedf264e4 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp
@@ -387,6 +387,7 @@ class RefCounted {
   unsigned trivial69() { return offsetof(OtherObj, children); }
   DerivedNumber* trivial70() { [[clang::suppress]] return static_cast<DerivedNumber*>(number); }
   unsigned trivial71() { return std::bit_cast<unsigned>(nullptr); }
+  unsigned trivial72() { Number n { 5 }; return WTF::move(n).value(); }
 
   static RefCounted& singleton() {
     static RefCounted s_RefCounted;

@tstellar
Copy link
Collaborator

tstellar commented Dec 8, 2025

@rniwa I think the lldb failure is fixed in main now, so if you click Update branch it should pass.

This PR adds support for treating WTF::move like std::move in various WebKit checkers.
@rniwa rniwa force-pushed the fix-webkit-recognize-wtf-move branch from f7951e6 to 289c817 Compare December 9, 2025 00:18
Comment on lines 188 to 195
bool isStdOrWTFMove(const clang::FunctionDecl *F) {
auto FnName = safeGetName(F);
auto *Namespace = F->getParent();
if (!Namespace)
return false;
auto NsName = safeGetName(Namespace);
return (NsName == "WTF" || NsName == "std") && FnName == "move";
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we ensure here that the parent of this parent is actually the TUDecl?
This way I think we would match: my::internal::std::move etc. I'd say it's not too likely to have something like this...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, we can check that. I'll just add:

  if (Namespace->getParent())
    return false;

before auto NsName = safeGetName(Namespace);.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh oops, I need to check that it's TranslationUnitDecl instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will add this instead:

  auto* TUDeck = Namespace->getParent();
  if (!TUDeck || !isa<TranslationUnitDecl>(TUDeck))
    return false;

Copy link
Contributor

@steakhal steakhal Dec 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered using isa_and_nonnull? Fusing the disjunction?

I'm not sure it makes sense. Its too late to think about demorgan rules.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, that's neat. will use that instead.

@github-actions
Copy link

github-actions bot commented Dec 9, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@github-actions
Copy link

github-actions bot commented Dec 9, 2025

🪟 Windows x64 Test Results

  • 52921 tests passed
  • 2061 tests skipped
  • 1 test failed

Failed Tests

(click on a test name to see its output)

AddressSanitizer-Unit

AddressSanitizer-Unit._/Asan-x86_64-calls-Dynamic-Test_exe/AddressSanitizerInterface/GetHeapSizeTest
Script:
--
C:\_work\llvm-project\llvm-project\build\runtimes\runtimes-bins\compiler-rt\lib\asan\tests\X86_64WindowsDynamicConfig\.\Asan-x86_64-calls-Dynamic-Test.exe --gtest_filter=AddressSanitizerInterface.GetHeapSizeTest
--
C:/_work/llvm-project/llvm-project/compiler-rt/lib/asan/tests/asan_interface_test.cpp:100
Expected equality of these values:
  old_heap_size
    Which is: 9445376
  __sanitizer_get_heap_size()
    Which is: 9969664

C:/_work/llvm-project/llvm-project/compiler-rt/lib/asan/tests/asan_interface_test.cpp:100
Expected equality of these values:
  old_heap_size
    Which is: 9445376
  __sanitizer_get_heap_size()
    Which is: 9969664

C:/_work/llvm-project/llvm-project/compiler-rt/lib/asan/tests/asan_interface_test.cpp:100
Expected equality of these values:
  old_heap_size
    Which is: 9445376
  __sanitizer_get_heap_size()
    Which is: 9969664


If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the infrastructure label.

@rniwa rniwa merged commit aca48a4 into llvm:main Dec 10, 2025
13 of 14 checks passed
@rniwa rniwa deleted the fix-webkit-recognize-wtf-move branch December 10, 2025 19:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:static analyzer clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants