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

>From b841eedfdb7b2ce61dc7337cb30001a4ce0adfb8 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <[email protected]>
Date: Thu, 4 Jun 2026 03:39:34 -0700
Subject: [PATCH 1/3] [alpha.webkit.NoDeleteChecker] Allow no-delete default
 constructors

This PR fixes the bug in TrivialFunctionAnalysis that it treats a default 
trivial constructor
without an explicit body / definition as not "trivial". Fixed the bug by 
allowing the function
body to be missing when what we have is a trivial default constructor.
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp     |  3 ++
 .../Checkers/WebKit/nodelete-annotation.cpp   | 42 +++++++++++++++++++
 2 files changed, 45 insertions(+)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index d5ed7fc78148a..90151584a58c6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -667,6 +667,9 @@ class TrivialFunctionAnalysisVisitor
           if (!Visit(CtorInit->getInit()))
             return false;
         }
+        if (CtorDecl->isTrivial() && CtorDecl->isDefaultConstructor() &&
+            !CtorDecl->hasBody())
+          return true;
       }
       const Stmt *Body = D->getBody();
       if (!Body)
diff --git a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp 
b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
index a9c50cfb1f45f..0d78fa91046ed 100644
--- a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
@@ -701,3 +701,45 @@ Ref<RefCountable> 
[[clang::annotate_type("webkit.nodelete")]] returnTypedefPrval
 
 } // namespace returned_prvalue_typedef
 
+namespace create_with_default_constructor {
+
+  struct ObjectWithDefaultConstructorWithoutMemberVariables {
+    void ref() const;
+    void deref() const;
+
+    static auto [[clang::annotate_type("webkit.nodelete")]] create() {
+      return adoptRef(*new 
ObjectWithDefaultConstructorWithoutMemberVariables());
+    }
+  };
+
+  struct ObjectWithDefaultConstructorWithPODMemberVariables {
+    void ref() const;
+    void deref() const;
+
+    static auto [[clang::annotate_type("webkit.nodelete")]] create() {
+      return adoptRef(*new 
ObjectWithDefaultConstructorWithPODMemberVariables());
+    }
+
+  private:
+    int value { 0 };
+    RefCountable* ptr { nullptr };
+  };
+
+  struct ObjectWithOpaqueCtor {
+    ObjectWithOpaqueCtor();
+  };
+
+  struct ObjectWithDefaultConstructorWithOpaqueCtorMemberVariables {
+    void ref() const;
+    void deref() const;
+
+    static auto [[clang::annotate_type("webkit.nodelete")]] create() {
+      return adoptRef(*new 
ObjectWithDefaultConstructorWithOpaqueCtorMemberVariables());
+      // expected-warning@-1{{A function 'create' has 
[[clang::annotate_type("webkit.nodelete")]] but it contains code that could 
destruct an object}}
+    }
+
+  private:
+    ObjectWithOpaqueCtor obj;
+  };
+
+} // namespace create_with_default_constructor

>From 3c419fac00a45a160cfab6cdb4ebbde1a7d3c38d Mon Sep 17 00:00:00 2001
From: Balazs Benics <[email protected]>
Date: Thu, 4 Jun 2026 14:01:57 +0100
Subject: [PATCH 2/3] [analyzer][WebKit] TrivialFunctionAnalysis: treat trivial
 implicit / =default special members as trivial

IsFunctionTrivial returned false on null FunctionDecl::getBody(). For an
implicit or =default special member the synthesized body is materialised
only at codegen, so the AST genuinely has no body -- and this conservative
rule misclassified guaranteed-trivial special members as ones that could
delete.

The visible symptom was a false positive on

    struct Clazzzz { void ref() const; void deref() const; };
    Ref<Clazzzz> [[clang::annotate_type("webkit.nodelete")]] create() {
      return adoptRef(*new Clazzzz());
    }

`new Clazzzz()` with parens is value-initialization, which emits a
CXXConstructExpr for Clazzzz's implicit default ctor; VisitCXXConstructExpr
then dispatched to IsFunctionTrivial(getConstructor()), which fell into the
null-body branch. (`new Clazzzz` without parens does not emit the
CXXConstructExpr for a class with no user-provided default ctor, which is
why most existing tests did not hit this.) The same path could affect any
implicit/=default ctor or dtor of a class that satisfies the standard's
trivial-special-member rules.

Fix: in IsFunctionTrivial, before the null-body check, recognise
non-user-provided special members and short-circuit to true when the
parent class reports the corresponding member trivial via
CXXRecordDecl::hasTrivial{Default,Copy,Move}Constructor() /
hasTrivialDestructor(). By the C++ standard these run no user code, so
they cannot delete -- the fast-path cannot introduce false negatives.

Add a minimised regression test (Plain { int x; } + new Plain()) alongside
the existing Clazzzz/create() case so the implicit-trivial-ctor path is
covered without smart-pointer infrastructure.

Assisted-By: claude
---
 .../Checkers/WebKit/PtrTypesSemantics.cpp     | 31 ++++++++++++++++---
 .../Checkers/WebKit/nodelete-annotation.cpp   | 24 ++++++++++++++
 2 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp 
b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
index 90151584a58c6..cf165796c9695 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -652,23 +652,44 @@ class TrivialFunctionAnalysisVisitor
   bool IsFunctionTrivial(const Decl *D) {
     const Stmt **SavedOffendingStmt = std::exchange(OffendingStmt, nullptr);
     auto Result = WithCachedResult(D, [&]() {
-      if (auto *FnDecl = dyn_cast<FunctionDecl>(D)) {
+      auto *FnDecl = dyn_cast<FunctionDecl>(D);
+      auto *MethodDecl = dyn_cast<CXXMethodDecl>(D);
+      auto *CtorDecl = dyn_cast<CXXConstructorDecl>(D);
+      auto *DtorDecl = dyn_cast<CXXDestructorDecl>(D);
+
+      if (FnDecl) {
         if (isNoDeleteFunction(FnDecl))
           return true;
-        if (auto *MD = dyn_cast<CXXMethodDecl>(D); MD && MD->isVirtual())
+        if (MethodDecl && MethodDecl->isVirtual())
           return false;
         for (auto *Param : FnDecl->parameters()) {
           if (!HasTrivialDestructor(Param))
             return false;
         }
       }
-      if (auto *CtorDecl = dyn_cast<CXXConstructorDecl>(D)) {
+      if (CtorDecl) {
         for (auto *CtorInit : CtorDecl->inits()) {
           if (!Visit(CtorInit->getInit()))
             return false;
         }
-        if (CtorDecl->isTrivial() && CtorDecl->isDefaultConstructor() &&
-            !CtorDecl->hasBody())
+      }
+      // An implicit or =default special member runs no user code when it is
+      // trivial in the C++ standard sense, so it cannot delete. Such a
+      // member's synthesized body is typically absent from the AST until
+      // codegen materialises it, which the generic null-body check below
+      // would otherwise conservatively classify as non-trivial.
+      if (MethodDecl && !MethodDecl->isUserProvided()) {
+        if (CtorDecl) {
+          const CXXRecordDecl *RD = CtorDecl->getParent();
+          if ((CtorDecl->isDefaultConstructor() &&
+               RD->hasTrivialDefaultConstructor()) ||
+              (CtorDecl->isCopyConstructor() &&
+               RD->hasTrivialCopyConstructor()) ||
+              (CtorDecl->isMoveConstructor() &&
+               RD->hasTrivialMoveConstructor()))
+            return true;
+        }
+        if (DtorDecl && DtorDecl->getParent()->hasTrivialDestructor())
           return true;
       }
       const Stmt *Body = D->getBody();
diff --git a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp 
b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
index 0d78fa91046ed..17b8bd83aca5d 100644
--- a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
@@ -743,3 +743,27 @@ namespace create_with_default_constructor {
   };
 
 } // namespace create_with_default_constructor
+
+struct Clazzzz {
+    void ref() const;
+    void deref() const;
+};
+
+Ref<Clazzzz> [[clang::annotate_type("webkit.nodelete")]] create() {
+    return adoptRef(*new Clazzzz());
+}
+
+namespace trivial_implicit_ctor_in_new_expr {
+
+// 'new T()' with parens emits a CXXConstructExpr for T's implicit default
+// ctor. That ctor has no body in the AST (the synthesized body is materialised
+// only at codegen), but it is trivial by the C++ standard and runs no user
+// code, so it cannot delete. Verify the fast-path treats it as trivial.
+struct Plain { int x; };
+
+void [[clang::annotate_type("webkit.nodelete")]] valueInitNew() {
+  Plain* p = new Plain();
+  (void)p;
+}
+
+} // namespace trivial_implicit_ctor_in_new_expr

>From fb4b105ce1a6499a73c7b347532e71b07c2632a6 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <[email protected]>
Date: Thu, 4 Jun 2026 10:52:08 -0700
Subject: [PATCH 3/3] Remove the redundant test case

---
 .../test/Analysis/Checkers/WebKit/nodelete-annotation.cpp | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp 
b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
index 17b8bd83aca5d..06ba7c47ae91a 100644
--- a/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/nodelete-annotation.cpp
@@ -744,14 +744,6 @@ namespace create_with_default_constructor {
 
 } // namespace create_with_default_constructor
 
-struct Clazzzz {
-    void ref() const;
-    void deref() const;
-};
-
-Ref<Clazzzz> [[clang::annotate_type("webkit.nodelete")]] create() {
-    return adoptRef(*new Clazzzz());
-}
 
 namespace trivial_implicit_ctor_in_new_expr {
 

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

Reply via email to