[clang] [WebKit checkers] Treat NULL, 0, and nil like nullptr (PR #157700)

Ryosuke Niwa via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 9 09:15:21 PDT 2025


https://github.com/rniwa created https://github.com/llvm/llvm-project/pull/157700

This PR makes WebKit checkers treat NULL, 0, and nil like nullptr in various places.

>From 8c8c20cf6d3ea822d098bfb97b8426cf8d8eef85 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at webkit.org>
Date: Tue, 9 Sep 2025 09:13:47 -0700
Subject: [PATCH] [WebKit checkers] Treat NULL, 0, and nil like nullptr

This PR makes WebKit checkers treat NULL, 0, and nil like nullptr in various places.
---
 .../lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp  | 12 +++++++++++-
 clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h  |  3 +++
 .../Checkers/WebKit/ForwardDeclChecker.cpp           |  2 +-
 .../Checkers/WebKit/RawPtrRefCallArgsChecker.cpp     |  6 ++----
 .../Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp    |  2 +-
 .../Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp    |  5 +++--
 .../WebKit/call-args-checked-const-member.cpp        | 10 ++++++++++
 .../Checkers/WebKit/retain-ptr-ctor-adopt-use.mm     |  6 ++++++
 .../Checkers/WebKit/uncounted-local-vars.cpp         |  1 +
 .../Analysis/Checkers/WebKit/unretained-call-args.mm |  2 ++
 10 files changed, 40 insertions(+), 9 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
index 478bd85177143..f62a868ccd2cd 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -215,6 +215,16 @@ bool isASafeCallArg(const Expr *E) {
   return isa<CXXThisExpr>(E);
 }
 
+bool isNullPtr(const clang::Expr *E) {
+  if (isa<CXXNullPtrLiteralExpr>(E) || isa<GNUNullExpr>(E))
+    return true;
+  if (auto *Int = dyn_cast_or_null<IntegerLiteral>(E)) {
+    if (Int->getValue().isZero())
+      return true;
+  }
+  return false;
+}
+
 bool isConstOwnerPtrMemberExpr(const clang::Expr *E) {
   if (auto *MCE = dyn_cast<CXXMemberCallExpr>(E)) {
     if (auto *Callee = MCE->getDirectCallee()) {
@@ -273,7 +283,7 @@ class EnsureFunctionVisitor
   bool VisitReturnStmt(const ReturnStmt *RS) {
     if (auto *RV = RS->getRetValue()) {
       RV = RV->IgnoreParenCasts();
-      if (isa<CXXNullPtrLiteralExpr>(RV))
+      if (isNullPtr(RV))
         return true;
       return isConstOwnerPtrMemberExpr(RV);
     }
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
index 8302bbe3084c2..3a009d65efea6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
@@ -66,6 +66,9 @@ bool tryToFindPtrOrigin(
 /// \returns Whether \p E is a safe call arugment.
 bool isASafeCallArg(const clang::Expr *E);
 
+/// \returns true if E is nullptr or __null.
+bool isNullPtr(const clang::Expr *E);
+
 /// \returns true if E is a MemberExpr accessing a const smart pointer type.
 bool isConstOwnerPtrMemberExpr(const clang::Expr *E);
 
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
index ec0c2c117fb29..9deb1845a2f1a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ForwardDeclChecker.cpp
@@ -272,7 +272,7 @@ class ForwardDeclChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
           ArgExpr = ArgExpr->IgnoreParenCasts();
       }
     }
-    if (isa<CXXNullPtrLiteralExpr>(ArgExpr) || isa<IntegerLiteral>(ArgExpr) ||
+    if (isNullPtr(ArgExpr) || isa<IntegerLiteral>(ArgExpr) ||
         isa<CXXDefaultArgExpr>(ArgExpr))
       return;
     if (auto *DRE = dyn_cast<DeclRefExpr>(ArgExpr)) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
index 764e2c640feb8..e80f1749d595b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefCallArgsChecker.cpp
@@ -217,13 +217,11 @@ class RawPtrRefCallArgsChecker
         [&](const clang::Expr *ArgOrigin, bool IsSafe) {
           if (IsSafe)
             return true;
-          if (isa<CXXNullPtrLiteralExpr>(ArgOrigin)) {
-            // foo(nullptr)
+          if (isNullPtr(ArgOrigin))
             return true;
-          }
           if (isa<IntegerLiteral>(ArgOrigin)) {
             // FIXME: Check the value.
-            // foo(NULL)
+            // foo(123)
             return true;
           }
           if (isa<ObjCStringLiteral>(ArgOrigin))
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
index 7cd86a682c0a9..f4f6e28c97ea1 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RawPtrRefLocalVarsChecker.cpp
@@ -295,7 +295,7 @@ class RawPtrRefLocalVarsChecker
                 if (isa<CXXThisExpr>(InitArgOrigin))
                   return true;
 
-                if (isa<CXXNullPtrLiteralExpr>(InitArgOrigin))
+                if (isNullPtr(InitArgOrigin))
                   return true;
 
                 if (isa<IntegerLiteral>(InitArgOrigin))
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
index d74fec2c551d4..5c1b2d7cce45d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RetainPtrCtorAdoptChecker.cpp
@@ -177,7 +177,8 @@ class RetainPtrCtorAdoptChecker
       CreateOrCopyFnCall.insert(Arg); // Avoid double reporting.
       return;
     }
-    if (Result == IsOwnedResult::Owned || Result == IsOwnedResult::Skip) {
+    if (Result == IsOwnedResult::Owned || Result == IsOwnedResult::Skip ||
+        isNullPtr(Arg)) {
       CreateOrCopyFnCall.insert(Arg);
       return;
     }
@@ -486,7 +487,7 @@ class RetainPtrCtorAdoptChecker
           continue;
         }
       }
-      if (isa<CXXNullPtrLiteralExpr>(E))
+      if (isNullPtr(E))
         return IsOwnedResult::NotOwned;
       if (auto *DRE = dyn_cast<DeclRefExpr>(E)) {
         auto QT = DRE->getType();
diff --git a/clang/test/Analysis/Checkers/WebKit/call-args-checked-const-member.cpp b/clang/test/Analysis/Checkers/WebKit/call-args-checked-const-member.cpp
index f7095606c77a4..7959daf0ceaaf 100644
--- a/clang/test/Analysis/Checkers/WebKit/call-args-checked-const-member.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/call-args-checked-const-member.cpp
@@ -71,12 +71,21 @@ class Foo {
     return m_obj5.get();
   }
 
+  CheckedObj* ensureObj6() {
+    if (!m_obj6)
+      const_cast<std::unique_ptr<CheckedObj>&>(m_obj6) = new CheckedObj;
+    if (m_obj6->next())
+      return (CheckedObj *)0;
+    return m_obj6.get();
+  }
+
 private:
   const std::unique_ptr<CheckedObj> m_obj1;
   std::unique_ptr<CheckedObj> m_obj2;
   const std::unique_ptr<CheckedObj> m_obj3;
   const std::unique_ptr<CheckedObj> m_obj4;
   const std::unique_ptr<CheckedObj> m_obj5;
+  const std::unique_ptr<CheckedObj> m_obj6;
 };
 
 void Foo::bar() {
@@ -87,6 +96,7 @@ void Foo::bar() {
   badEnsureObj4().method();
   // expected-warning at -1{{Call argument for 'this' parameter is unchecked and unsafe}}
   ensureObj5()->method();
+  ensureObj6()->method();
 }
 
 } // namespace call_args_const_unique_ptr
diff --git a/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm
index 83c87b14158b9..769901778cdf0 100644
--- a/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm
+++ b/clang/test/Analysis/Checkers/WebKit/retain-ptr-ctor-adopt-use.mm
@@ -13,6 +13,8 @@ void basic_correct() {
   auto ns4 = adoptNS([ns3 mutableCopy]);
   auto ns5 = adoptNS([ns3 copyWithValue:3]);
   auto ns6 = retainPtr([ns3 next]);
+  auto ns7 = retainPtr((SomeObj *)0);
+  auto ns8 = adoptNS(nil);
   CFMutableArrayRef cf1 = adoptCF(CFArrayCreateMutable(kCFAllocatorDefault, 10));
   auto cf2 = adoptCF(SecTaskCreateFromSelf(kCFAllocatorDefault));
   auto cf3 = adoptCF(checked_cf_cast<CFArrayRef>(CFCopyArray(cf1)));
@@ -111,6 +113,10 @@ - (void)setValue:value {
   return adoptCF(rawBuffer);
 }
 
+RetainPtr<SomeObj> return_nil() {
+  return nil;
+}
+
 RetainPtr<SomeObj> return_nullptr() {
   return nullptr;
 }
diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp
index 0540ed93c5707..3364637369799 100644
--- a/clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/uncounted-local-vars.cpp
@@ -121,6 +121,7 @@ void foo8(RefCountable* obj) {
     RefCountable *bar = foo->trivial() ? foo.get() : nullptr;
     // expected-warning at -1{{Local variable 'bar' is uncounted and unsafe [alpha.webkit.UncountedLocalVarsChecker]}}
     foo = nullptr;
+    foo = (RefCountable *)0;
     bar->method();
   }
 }
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
index c69113c48806d..3feecd930f109 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -456,6 +456,8 @@ - (void)doWorkOnSelf {
   // expected-warning at -1{{Call argument is unretained and unsafe}}
   // expected-warning at -2{{Call argument is unretained and unsafe}}
   [self doWork:@"hello", RetainPtr<SomeObj> { provide() }.get(), RetainPtr<CFMutableArrayRef> { provide_cf() }.get()];
+  [self doWork:__null];
+  [self doWork:nil];
 }
 
 - (SomeObj *)getSomeObj {



More information about the cfe-commits mailing list