[clang] [webkit.RefCntblBaseVirtualDtor] Ignore a base class which has a specialized deref function for a given derived class. (PR #92501)

Ryosuke Niwa via cfe-commits cfe-commits at lists.llvm.org
Mon May 20 15:58:32 PDT 2024


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

>From 87cfc8234e1294dedc103b9bcd2b7d9d31874c4a Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at apple.com>
Date: Thu, 16 May 2024 23:24:13 -0700
Subject: [PATCH 1/9] [webkit.RefCntblBaseVirtualDtor] Ignore a base class
 which has a specialized deref function for a given derived class.

When a base class B has a deref function which calls delete operator on a derived class D,
don't emit a warning for B even if it did not have a virtual destructor.
---
 .../WebKit/RefCntblBaseVirtualDtorChecker.cpp | 140 ++++++++++++-
 .../ref-cntbl-base-virtual-dtor-templates.cpp | 196 +++++++++++++++++-
 2 files changed, 330 insertions(+), 6 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
index 7f4c3a7b787e8..36ab581f1edbd 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -11,16 +11,128 @@
 #include "PtrTypesSemantics.h"
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/StmtVisitor.h"
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/DenseSet.h"
 #include <optional>
 
 using namespace clang;
 using namespace ento;
 
 namespace {
+
+class DerefAnalysisVisitor
+    : public ConstStmtVisitor<DerefAnalysisVisitor, bool> {
+  // Returns true if any of child statements return true.
+  bool VisitChildren(const Stmt *S) {
+    for (const Stmt *Child : S->children()) {
+      if (Child && Visit(Child))
+        return true;
+    }
+    return false;
+  }
+
+  bool VisitBody(const Stmt *Body) {
+    if (!Body)
+      return false;
+
+    auto [It, IsNew] = VisitedBody.insert(Body);
+    if (!IsNew) // This body is recursive
+      return false;
+
+    return Visit(Body);
+  }
+
+public:
+  DerefAnalysisVisitor(const TemplateArgumentList &ArgList,
+                       const CXXRecordDecl *ClassDecl)
+    : ArgList(&ArgList)
+    , ClassDecl(ClassDecl)
+  { }
+
+  DerefAnalysisVisitor(const CXXRecordDecl *ClassDecl)
+    : ClassDecl(ClassDecl)
+  { }
+
+  bool HasSpecializedDelete(CXXMethodDecl *Decl) {
+    if (auto *Tmpl = Decl->getTemplateInstantiationPattern())
+      return VisitBody(Tmpl->getBody());
+    return VisitBody(Decl->getBody());
+  }
+
+  bool VisitCallExpr(const CallExpr *CE) {
+    auto *Callee = CE->getCallee();
+    while (auto *Expr = dyn_cast<CastExpr>(Callee))
+      Callee = Expr->getSubExpr();
+    if (auto *DeclRef = dyn_cast<DeclRefExpr>(Callee)) {
+      auto *Decl = DeclRef->getDecl();
+      if (auto *VD = dyn_cast<VarDecl>(Decl)) {
+        if (auto *Init = VD->getInit()) {
+          if (auto *Lambda = dyn_cast<LambdaExpr>(Init))
+            return VisitBody(Lambda->getBody());
+        }
+      } else if (auto *FD = dyn_cast<FunctionDecl>(Decl))
+        return VisitBody(FD->getBody());
+    }
+    return false;
+  }
+
+  bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE) {
+    auto *Callee = MCE->getMethodDecl();
+    if (!Callee)
+      return false;
+    return VisitBody(Callee->getBody());
+  }
+
+  bool VisitCXXDeleteExpr(const CXXDeleteExpr *E) {
+    auto *Arg = E->getArgument();
+    while (Arg) {
+      if (auto *Paren = dyn_cast<ParenExpr>(Arg))
+        Arg = Paren->getSubExpr();
+      else if (auto *Cast = dyn_cast<ExplicitCastExpr>(Arg)) {
+        Arg = Cast->getSubExpr();
+        auto CastType = Cast->getType();
+        if (auto *PtrType = dyn_cast<PointerType>(CastType)) {
+          auto PointeeType = PtrType->getPointeeType();
+          while (auto *ET = dyn_cast<ElaboratedType>(PointeeType)) {
+            if (ET->isSugared())
+              PointeeType = ET->desugar();
+          }
+          if (auto *ParmType = dyn_cast<TemplateTypeParmType>(PointeeType)) {
+            if (ArgList) {
+              auto ParmIndex = ParmType->getIndex();
+              auto Type = ArgList->get(ParmIndex).getAsType();
+              if (auto *RD = dyn_cast<RecordType>(Type)) {
+                if (RD->getDecl() == ClassDecl)
+                  return true;
+              }
+            }
+          } else if (auto *RD = dyn_cast<RecordType>(PointeeType)) {
+            if (RD->getDecl() == ClassDecl)
+              return true;
+          }
+        }
+      } else
+        break;
+    }
+    return false;
+  }
+
+  bool VisitStmt(const Stmt *S) { return VisitChildren(S); }
+
+  // Return false since the contents of lambda isn't necessarily executed.
+  // If it is executed, VisitCallExpr above will visit its body.
+  bool VisitLambdaExpr(const LambdaExpr *) { return false; }
+
+private:
+  const TemplateArgumentList* ArgList { nullptr };
+  const CXXRecordDecl* ClassDecl;
+  llvm::DenseSet<const Stmt *> VisitedBody;
+};
+
 class RefCntblBaseVirtualDtorChecker
     : public Checker<check::ASTDecl<TranslationUnitDecl>> {
 private:
@@ -91,8 +203,6 @@ class RefCntblBaseVirtualDtorChecker
           const CXXRecordDecl *C = T->getAsCXXRecordDecl();
           if (!C)
             return false;
-          if (isRefCountedClass(C))
-            return false;
 
           bool AnyInconclusiveBase = false;
           const auto hasPublicRefInBase =
@@ -124,6 +234,9 @@ class RefCntblBaseVirtualDtorChecker
           if (AnyInconclusiveBase || !hasRef || !hasDeref)
             return false;
 
+          if (isClassWithSpecializedDelete(C, RD))
+            return false;
+
           const auto *Dtor = C->getDestructor();
           if (!Dtor || !Dtor->isVirtual()) {
             ProblematicBaseSpecifier = Base;
@@ -182,6 +295,29 @@ class RefCntblBaseVirtualDtorChecker
             ClsName == "ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr");
   }
 
+  static bool isClassWithSpecializedDelete(const CXXRecordDecl *C,
+                                           const CXXRecordDecl *DerivedClass) {
+    if (auto *ClsTmplSpDecl = dyn_cast<ClassTemplateSpecializationDecl>(C)) {
+      for (auto *MethodDecl : C->methods()) {
+        if (safeGetName(MethodDecl) == "deref") {
+          DerefAnalysisVisitor DerefAnalysis(ClsTmplSpDecl->getTemplateArgs(),
+                                             DerivedClass);
+          if (DerefAnalysis.HasSpecializedDelete(MethodDecl))
+            return true;
+        }
+      }
+      return false;
+    }
+    for (auto *MethodDecl : C->methods()) {
+        if (safeGetName(MethodDecl) == "deref") {
+          DerefAnalysisVisitor DerefAnalysis(DerivedClass);
+          if (DerefAnalysis.HasSpecializedDelete(MethodDecl))
+            return true;
+        }
+    }
+    return false;
+  }
+
   void reportBug(const CXXRecordDecl *DerivedClass,
                  const CXXBaseSpecifier *BaseSpec,
                  const CXXRecordDecl *ProblematicBaseClass) const {
diff --git a/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp b/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
index eeb62d5d89ec4..37f5d46110896 100644
--- a/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
@@ -58,24 +58,101 @@ class RefCounted : public RefCountedBase {
   RefCounted() { }
 };
 
+template <typename X, typename T>
+class ExoticRefCounted : RefCountedBase {
+public:
+  void deref() const {
+    if (derefBase())
+      delete (const_cast<T*>(static_cast<const T*>(this)));
+  }
+};
+
+template <typename X, typename T>
+class BadBase : RefCountedBase {
+public:
+  void deref() const {
+    if (derefBase())
+      delete (const_cast<X*>(static_cast<const X*>(this)));
+  }
+};
+
+template <typename T>
+class FancyDeref {
+public:
+  void ref() const
+  {
+    ++refCount;
+  }
+
+  void deref() const
+  {
+    --refCount;
+    if (refCount)
+      return;
+    auto deleteThis = [this] {
+      delete static_cast<const T*>(this);
+    };
+    deleteThis();
+  }
+private:
+  mutable unsigned refCount { 0 };
+};
+
+template <typename T>
+class BadFancyDeref {
+public:
+  void ref() const
+  {
+    ++refCount;
+  }
+
+  void deref() const
+  {
+    --refCount;
+    if (refCount)
+      return;
+    auto deleteThis = [this] {
+      delete static_cast<const T*>(this);
+    };
+    delete this;
+  }
+private:
+  mutable unsigned refCount { 0 };
+};
+
 template <typename T>
 class ThreadSafeRefCounted {
 public:
-  void ref() const;
-  bool deref() const;
+  void ref() const { ++refCount; }
+  void deref() const {
+    if (!--refCount)
+      delete const_cast<T*>(static_cast<const T*>(this));
+  }
+private:
+  mutable unsigned refCount { 0 };
 };
 
 template <typename T>
 class ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr {
 public:
-  void ref() const;
-  bool deref() const;
+  void ref() const { ++refCount; }
+  void deref() const {
+    if (!--refCount)
+      delete const_cast<T*>(static_cast<const T*>(this));
+  }
+private:
+  mutable unsigned refCount { 0 };
 };
 
 } // namespace WTF
 
 class DerivedClass4 : public WTF::RefCounted<DerivedClass4> { };
 
+class DerivedClass4b : public WTF::ExoticRefCounted<int, DerivedClass4b> { };
+
+class DerivedClass4c : public WTF::BadBase<int, DerivedClass4c> { };
+// expected-warning at -1{{Class 'WTF::BadBase<int, DerivedClass4c>' is used as a base of class 'DerivedClass4c' but doesn't have virtual destructor}}
+
 class DerivedClass5 : public DerivedClass4 { };
 // expected-warning at -1{{Class 'DerivedClass4' is used as a base of class 'DerivedClass5' but doesn't have virtual destructor}}
 
@@ -88,3 +165,114 @@ class DerivedClass8 : public WTF::ThreadSafeRefCountedAndCanMakeThreadSafeWeakPt
 
 class DerivedClass9 : public DerivedClass8 { };
 // expected-warning at -1{{Class 'DerivedClass8' is used as a base of class 'DerivedClass9' but doesn't have virtual destructor}}
+
+class DerivedClass10 : public WTF::FancyDeref<DerivedClass10> { };
+
+class DerivedClass10b : public WTF::BadFancyDeref<DerivedClass10b> { };
+// expected-warning at -1{{Class 'WTF::BadFancyDeref<DerivedClass10b>' is used as a base of class 'DerivedClass10b' but doesn't have virtual destructor}}
+
+class BaseClass1 {
+public:
+  void ref() const { ++refCount; }
+  void deref() const;
+private:
+  enum class Type { Base, Derived } type { Type::Base };
+  mutable unsigned refCount { 0 };
+};
+
+class DerivedClass11 : public BaseClass1 { };
+
+void BaseClass1::deref() const
+{
+  --refCount;
+  if (refCount)
+    return;
+  switch (type) {
+  case Type::Base:
+    delete const_cast<BaseClass1*>(this);
+    break;
+  case Type::Derived:
+    delete const_cast<DerivedClass11*>(static_cast<const DerivedClass11*>(this));
+    break;
+  }
+}
+
+class BaseClass2;
+static void deleteBase2(BaseClass2*);
+
+class BaseClass2 {
+public:
+  void ref() const { ++refCount; }
+  void deref() const
+  {
+    if (!--refCount)
+      deleteBase2(const_cast<BaseClass2*>(this));
+  }
+  virtual bool isDerived() { return false; }
+private:
+  mutable unsigned refCount { 0 };
+};
+
+class DerivedClass12 : public BaseClass2 {
+  bool isDerived() final { return true; }
+};
+
+void deleteBase2(BaseClass2* obj) {
+  if (obj->isDerived())
+    delete static_cast<DerivedClass12*>(obj);
+  else
+    delete obj;
+}
+
+class BaseClass3 {
+public:
+  void ref() const { ++refCount; }
+  void deref() const
+  {
+    if (!--refCount)
+      const_cast<BaseClass3*>(this)->destory();
+  }
+  virtual bool isDerived() { return false; }
+
+private:
+  void destory();
+
+  mutable unsigned refCount { 0 };
+};
+
+class DerivedClass13 : public BaseClass3 {
+  bool isDerived() final { return true; }
+};
+
+void BaseClass3::destory() {
+  if (isDerived())
+    delete static_cast<DerivedClass13*>(this);
+  else
+    delete this;
+}
+
+class RecursiveBaseClass {
+public:
+  void ref() const {
+    if (otherObject)
+      otherObject->ref();
+    else
+      ++refCount;
+  }
+  void deref() const {
+    if (otherObject)
+      otherObject->deref();
+    else {
+      --refCount;
+      if (refCount)
+        return;
+      delete this;
+    }
+  }
+private:
+  RecursiveBaseClass* otherObject { nullptr };
+  mutable unsigned refCount { 0 };
+};
+
+class RecursiveDerivedClass : public RecursiveBaseClass { };
+// expected-warning at -1{{Class 'RecursiveBaseClass' is used as a base of class 'RecursiveDerivedClass' but doesn't have virtual destructor}}

>From c538cd4b291e550b830c2c450e095638f6785452 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at apple.com>
Date: Thu, 16 May 2024 23:37:40 -0700
Subject: [PATCH 2/9] Fix formatting.

---
 .../WebKit/RefCntblBaseVirtualDtorChecker.cpp | 22 ++++++++-----------
 1 file changed, 9 insertions(+), 13 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
index 36ab581f1edbd..92c1c6ea75263 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -49,13 +49,9 @@ class DerefAnalysisVisitor
 public:
   DerefAnalysisVisitor(const TemplateArgumentList &ArgList,
                        const CXXRecordDecl *ClassDecl)
-    : ArgList(&ArgList)
-    , ClassDecl(ClassDecl)
-  { }
+      : ArgList(&ArgList) , ClassDecl(ClassDecl) {}
 
-  DerefAnalysisVisitor(const CXXRecordDecl *ClassDecl)
-    : ClassDecl(ClassDecl)
-  { }
+  DerefAnalysisVisitor(const CXXRecordDecl *ClassDecl) : ClassDecl(ClassDecl) {}
 
   bool HasSpecializedDelete(CXXMethodDecl *Decl) {
     if (auto *Tmpl = Decl->getTemplateInstantiationPattern())
@@ -128,8 +124,8 @@ class DerefAnalysisVisitor
   bool VisitLambdaExpr(const LambdaExpr *) { return false; }
 
 private:
-  const TemplateArgumentList* ArgList { nullptr };
-  const CXXRecordDecl* ClassDecl;
+  const TemplateArgumentList *ArgList{nullptr};
+  const CXXRecordDecl *ClassDecl;
   llvm::DenseSet<const Stmt *> VisitedBody;
 };
 
@@ -309,11 +305,11 @@ class RefCntblBaseVirtualDtorChecker
       return false;
     }
     for (auto *MethodDecl : C->methods()) {
-        if (safeGetName(MethodDecl) == "deref") {
-          DerefAnalysisVisitor DerefAnalysis(DerivedClass);
-          if (DerefAnalysis.HasSpecializedDelete(MethodDecl))
-            return true;
-        }
+      if (safeGetName(MethodDecl) == "deref") {
+        DerefAnalysisVisitor DerefAnalysis(DerivedClass);
+        if (DerefAnalysis.HasSpecializedDelete(MethodDecl))
+          return true;
+      }
     }
     return false;
   }

>From 97dfd40cf89901411ea6f21a0f9b7492f184ac39 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at apple.com>
Date: Thu, 16 May 2024 23:50:58 -0700
Subject: [PATCH 3/9] Fix formatting 2.

---
 .../Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp          | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
index 92c1c6ea75263..17e5514850b21 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -49,7 +49,7 @@ class DerefAnalysisVisitor
 public:
   DerefAnalysisVisitor(const TemplateArgumentList &ArgList,
                        const CXXRecordDecl *ClassDecl)
-      : ArgList(&ArgList) , ClassDecl(ClassDecl) {}
+      : ArgList(&ArgList), ClassDecl(ClassDecl) {}
 
   DerefAnalysisVisitor(const CXXRecordDecl *ClassDecl) : ClassDecl(ClassDecl) {}
 

>From 3e4d9495a1e52c04620036da026727655c65c86f Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at apple.com>
Date: Fri, 17 May 2024 01:54:21 -0700
Subject: [PATCH 4/9] [webkit.RefCntblBaseVirtualDtor] Only check immediate
 base class

This PR changes RefCntblBaseVirtualDtor checker so that the checker will only check
if a given class's immediate ref-counted base class has a virtual destructor,
not every ref-counted ancestor class.

This is sufficient because the checker will eventually see every class declaration,
and transitively proves every ref-counted base class has a virtual destructor or
deref function which casts this pointer back to the derived class before deleting.
---
 .../WebKit/RefCntblBaseVirtualDtorChecker.cpp | 119 ++++++++----------
 .../ref-cntbl-base-virtual-dtor-templates.cpp |   6 +
 2 files changed, 58 insertions(+), 67 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
index 17e5514850b21..452d2b4a14a7e 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -172,80 +172,65 @@ class RefCntblBaseVirtualDtorChecker
     if (shouldSkipDecl(RD))
       return;
 
-    CXXBasePaths Paths;
-    Paths.setOrigin(RD);
-
-    const CXXBaseSpecifier *ProblematicBaseSpecifier = nullptr;
-    const CXXRecordDecl *ProblematicBaseClass = nullptr;
-
-    const auto IsPublicBaseRefCntblWOVirtualDtor =
-        [RD, &ProblematicBaseSpecifier,
-         &ProblematicBaseClass](const CXXBaseSpecifier *Base, CXXBasePath &) {
-          const auto AccSpec = Base->getAccessSpecifier();
-          if (AccSpec == AS_protected || AccSpec == AS_private ||
-              (AccSpec == AS_none && RD->isClass()))
-            return false;
-
-          auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
-          auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
-
-          bool hasRef = hasRefInBase && *hasRefInBase != nullptr;
-          bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr;
-
-          QualType T = Base->getType();
-          if (T.isNull())
-            return false;
-
-          const CXXRecordDecl *C = T->getAsCXXRecordDecl();
-          if (!C)
-            return false;
-
-          bool AnyInconclusiveBase = false;
-          const auto hasPublicRefInBase =
-              [&AnyInconclusiveBase](const CXXBaseSpecifier *Base,
-                                     CXXBasePath &) {
-                auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
-                if (!hasRefInBase) {
-                  AnyInconclusiveBase = true;
-                  return false;
-                }
-                return (*hasRefInBase) != nullptr;
-              };
-          const auto hasPublicDerefInBase = [&AnyInconclusiveBase](
-                                                const CXXBaseSpecifier *Base,
-                                                CXXBasePath &) {
-            auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
-            if (!hasDerefInBase) {
+    for (auto& Base : RD->bases()) {
+      const auto AccSpec = Base.getAccessSpecifier();
+      if (AccSpec == AS_protected || AccSpec == AS_private ||
+          (AccSpec == AS_none && RD->isClass()))
+        continue;
+
+      auto hasRefInBase = clang::hasPublicMethodInBase(&Base, "ref");
+      auto hasDerefInBase = clang::hasPublicMethodInBase(&Base, "deref");
+
+      bool hasRef = hasRefInBase && *hasRefInBase != nullptr;
+      bool hasDeref = hasDerefInBase && *hasDerefInBase != nullptr;
+
+      QualType T = Base.getType();
+      if (T.isNull())
+        continue;
+
+      const CXXRecordDecl *C = T->getAsCXXRecordDecl();
+      if (!C)
+        continue;
+
+      bool AnyInconclusiveBase = false;
+      const auto hasPublicRefInBase =
+          [&AnyInconclusiveBase](const CXXBaseSpecifier *Base,
+                                 CXXBasePath &) {
+            auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
+            if (!hasRefInBase) {
               AnyInconclusiveBase = true;
               return false;
             }
-            return (*hasDerefInBase) != nullptr;
+            return (*hasRefInBase) != nullptr;
           };
-          CXXBasePaths Paths;
-          Paths.setOrigin(C);
-          hasRef = hasRef || C->lookupInBases(hasPublicRefInBase, Paths,
+      const auto hasPublicDerefInBase = [&AnyInconclusiveBase](
+                                            const CXXBaseSpecifier *Base,
+                                            CXXBasePath &) {
+        auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
+        if (!hasDerefInBase) {
+          AnyInconclusiveBase = true;
+          return false;
+        }
+        return (*hasDerefInBase) != nullptr;
+      };
+      CXXBasePaths Paths;
+      Paths.setOrigin(C);
+      hasRef = hasRef || C->lookupInBases(hasPublicRefInBase, Paths,
+                                          /*LookupInDependent =*/true);
+      hasDeref = hasDeref || C->lookupInBases(hasPublicDerefInBase, Paths,
                                               /*LookupInDependent =*/true);
-          hasDeref = hasDeref || C->lookupInBases(hasPublicDerefInBase, Paths,
-                                                  /*LookupInDependent =*/true);
-          if (AnyInconclusiveBase || !hasRef || !hasDeref)
-            return false;
-
-          if (isClassWithSpecializedDelete(C, RD))
-            return false;
-
-          const auto *Dtor = C->getDestructor();
-          if (!Dtor || !Dtor->isVirtual()) {
-            ProblematicBaseSpecifier = Base;
-            ProblematicBaseClass = C;
-            return true;
-          }
+      if (AnyInconclusiveBase || !hasRef || !hasDeref)
+        continue;
 
-          return false;
-        };
+      if (isClassWithSpecializedDelete(C, RD))
+        continue;
 
-    if (RD->lookupInBases(IsPublicBaseRefCntblWOVirtualDtor, Paths,
-                          /*LookupInDependent =*/true)) {
-      reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
+      const auto *Dtor = C->getDestructor();
+      if (!Dtor || !Dtor->isVirtual()) {
+        auto* ProblematicBaseSpecifier = &Base;
+        auto* ProblematicBaseClass = C;
+        reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
+      }
     }
   }
 
diff --git a/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp b/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
index 37f5d46110896..c42d8520b5f05 100644
--- a/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
@@ -276,3 +276,9 @@ class RecursiveBaseClass {
 
 class RecursiveDerivedClass : public RecursiveBaseClass { };
 // expected-warning at -1{{Class 'RecursiveBaseClass' is used as a base of class 'RecursiveDerivedClass' but doesn't have virtual destructor}}
+
+class DerivedClass14 : public WTF::RefCounted<DerivedClass14> {
+public:
+  virtual ~DerivedClass14() { }
+};
+class DerivedClass15 : public DerivedClass14 { };

>From cb956371721c2b573363fb2de00d59535877ca4f Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at apple.com>
Date: Fri, 17 May 2024 02:06:32 -0700
Subject: [PATCH 5/9] Fix formatting.

---
 .../WebKit/RefCntblBaseVirtualDtorChecker.cpp | 28 +++++++++----------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
index 452d2b4a14a7e..e851d1f5d5df9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -172,7 +172,7 @@ class RefCntblBaseVirtualDtorChecker
     if (shouldSkipDecl(RD))
       return;
 
-    for (auto& Base : RD->bases()) {
+    for (auto &Base : RD->bases()) {
       const auto AccSpec = Base.getAccessSpecifier();
       if (AccSpec == AS_protected || AccSpec == AS_private ||
           (AccSpec == AS_none && RD->isClass()))
@@ -194,8 +194,7 @@ class RefCntblBaseVirtualDtorChecker
 
       bool AnyInconclusiveBase = false;
       const auto hasPublicRefInBase =
-          [&AnyInconclusiveBase](const CXXBaseSpecifier *Base,
-                                 CXXBasePath &) {
+          [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
             auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
             if (!hasRefInBase) {
               AnyInconclusiveBase = true;
@@ -203,16 +202,15 @@ class RefCntblBaseVirtualDtorChecker
             }
             return (*hasRefInBase) != nullptr;
           };
-      const auto hasPublicDerefInBase = [&AnyInconclusiveBase](
-                                            const CXXBaseSpecifier *Base,
-                                            CXXBasePath &) {
-        auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
-        if (!hasDerefInBase) {
-          AnyInconclusiveBase = true;
-          return false;
-        }
-        return (*hasDerefInBase) != nullptr;
-      };
+      const auto hasPublicDerefInBase =
+          [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
+            auto hasDerefInBase = clang::hasPublicMethodInBase(Base, "deref");
+            if (!hasDerefInBase) {
+              AnyInconclusiveBase = true;
+              return false;
+            }
+            return (*hasDerefInBase) != nullptr;
+          };
       CXXBasePaths Paths;
       Paths.setOrigin(C);
       hasRef = hasRef || C->lookupInBases(hasPublicRefInBase, Paths,
@@ -227,8 +225,8 @@ class RefCntblBaseVirtualDtorChecker
 
       const auto *Dtor = C->getDestructor();
       if (!Dtor || !Dtor->isVirtual()) {
-        auto* ProblematicBaseSpecifier = &Base;
-        auto* ProblematicBaseClass = C;
+        auto *ProblematicBaseSpecifier = &Base;
+        auto *ProblematicBaseClass = C;
         reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
       }
     }

>From 04a17ea95ae1f6ce224c26a1d1b388c1910e03e7 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at apple.com>
Date: Fri, 17 May 2024 21:03:04 -0700
Subject: [PATCH 6/9] [webkit.RefCntblBaseVirtualDtor] Fix a bug that
 ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr generates a warning.

The bug was caused by DerefAnalysisVisitor attempting to find a delete expression
within a non-specialized template. Because we could not determine the type of
the object being deleted in such cases, we were erroneously concluding that
deref didn't have a proper delete expression.

Fixed the bug by making DerefAnalysisVisitor::HasSpecializedDelete return nullopt
when there is no fully specialized instance. Update the tests accordingly.
---
 .../WebKit/RefCntblBaseVirtualDtorChecker.cpp | 42 +++++++++++-------
 .../ref-cntbl-base-virtual-dtor-templates.cpp | 44 +++++++++++++++----
 2 files changed, 63 insertions(+), 23 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
index e851d1f5d5df9..2a5707837726b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -53,10 +53,12 @@ class DerefAnalysisVisitor
 
   DerefAnalysisVisitor(const CXXRecordDecl *ClassDecl) : ClassDecl(ClassDecl) {}
 
-  bool HasSpecializedDelete(CXXMethodDecl *Decl) {
+  std::optional<bool> HasSpecializedDelete(CXXMethodDecl *Decl) {
+    if (auto *Body = Decl->getBody())
+      return VisitBody(Body);
     if (auto *Tmpl = Decl->getTemplateInstantiationPattern())
-      return VisitBody(Tmpl->getBody());
-    return VisitBody(Decl->getBody());
+      return std::nullopt; // Indeterminate. There was no concrete instance.
+    return false;
   }
 
   bool VisitCallExpr(const CallExpr *CE) {
@@ -88,7 +90,7 @@ class DerefAnalysisVisitor
     while (Arg) {
       if (auto *Paren = dyn_cast<ParenExpr>(Arg))
         Arg = Paren->getSubExpr();
-      else if (auto *Cast = dyn_cast<ExplicitCastExpr>(Arg)) {
+      else if (auto *Cast = dyn_cast<CastExpr>(Arg)) {
         Arg = Cast->getSubExpr();
         auto CastType = Cast->getType();
         if (auto *PtrType = dyn_cast<PointerType>(CastType)) {
@@ -109,6 +111,12 @@ class DerefAnalysisVisitor
           } else if (auto *RD = dyn_cast<RecordType>(PointeeType)) {
             if (RD->getDecl() == ClassDecl)
               return true;
+          } else if (auto* ST = dyn_cast<SubstTemplateTypeParmType>(PointeeType)) {
+            auto Type = ST->getReplacementType();
+            if (auto *RD = dyn_cast<RecordType>(Type)) {
+              if (RD->getDecl() == ClassDecl)
+                return true;
+            }
           }
         }
       } else
@@ -172,7 +180,7 @@ class RefCntblBaseVirtualDtorChecker
     if (shouldSkipDecl(RD))
       return;
 
-    for (auto &Base : RD->bases()) {
+    for (auto& Base : RD->bases()) {
       const auto AccSpec = Base.getAccessSpecifier();
       if (AccSpec == AS_protected || AccSpec == AS_private ||
           (AccSpec == AS_none && RD->isClass()))
@@ -194,7 +202,8 @@ class RefCntblBaseVirtualDtorChecker
 
       bool AnyInconclusiveBase = false;
       const auto hasPublicRefInBase =
-          [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
+          [&AnyInconclusiveBase](const CXXBaseSpecifier *Base,
+                                 CXXBasePath &) {
             auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
             if (!hasRefInBase) {
               AnyInconclusiveBase = true;
@@ -220,13 +229,14 @@ class RefCntblBaseVirtualDtorChecker
       if (AnyInconclusiveBase || !hasRef || !hasDeref)
         continue;
 
-      if (isClassWithSpecializedDelete(C, RD))
+      auto HasSpecializedDelete = isClassWithSpecializedDelete(C, RD);
+      if (!HasSpecializedDelete || *HasSpecializedDelete)
         continue;
 
       const auto *Dtor = C->getDestructor();
       if (!Dtor || !Dtor->isVirtual()) {
-        auto *ProblematicBaseSpecifier = &Base;
-        auto *ProblematicBaseClass = C;
+        auto* ProblematicBaseSpecifier = &Base;
+        auto* ProblematicBaseClass = C;
         reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
       }
     }
@@ -274,15 +284,16 @@ class RefCntblBaseVirtualDtorChecker
             ClsName == "ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr");
   }
 
-  static bool isClassWithSpecializedDelete(const CXXRecordDecl *C,
-                                           const CXXRecordDecl *DerivedClass) {
+  static std::optional<bool> isClassWithSpecializedDelete(
+      const CXXRecordDecl *C, const CXXRecordDecl *DerivedClass) {
     if (auto *ClsTmplSpDecl = dyn_cast<ClassTemplateSpecializationDecl>(C)) {
       for (auto *MethodDecl : C->methods()) {
         if (safeGetName(MethodDecl) == "deref") {
           DerefAnalysisVisitor DerefAnalysis(ClsTmplSpDecl->getTemplateArgs(),
                                              DerivedClass);
-          if (DerefAnalysis.HasSpecializedDelete(MethodDecl))
-            return true;
+          auto Result = DerefAnalysis.HasSpecializedDelete(MethodDecl);
+          if (!Result || *Result)
+            return Result;
         }
       }
       return false;
@@ -290,8 +301,9 @@ class RefCntblBaseVirtualDtorChecker
     for (auto *MethodDecl : C->methods()) {
       if (safeGetName(MethodDecl) == "deref") {
         DerefAnalysisVisitor DerefAnalysis(DerivedClass);
-        if (DerefAnalysis.HasSpecializedDelete(MethodDecl))
-          return true;
+        auto Result = DerefAnalysis.HasSpecializedDelete(MethodDecl);
+        if (!Result || *Result)
+          return Result;
       }
     }
     return false;
diff --git a/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp b/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
index c42d8520b5f05..f04c68cb641f1 100644
--- a/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
@@ -132,16 +132,36 @@ class ThreadSafeRefCounted {
   mutable unsigned refCount { 0 };
 };
 
-template <typename T>
-class ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr {
+class ThreadSafeRefCountedControlBlock {
 public:
-  void ref() const { ++refCount; }
-  void deref() const {
-    if (!--refCount)
-      delete const_cast<T*>(static_cast<const T*>(this));
+  ThreadSafeRefCountedControlBlock(void* object) : object(object) { }
+  void strongRef() const { ++refCount; }
+  template <typename T>
+  void strongDeref() const {
+    --refCount;
+    if (refCount)
+      return;
+
+    auto deleteObject = [&] {
+      delete static_cast<const T*>(object);
+      delete this;
+    };
+
+    deleteObject();
   }
 private:
   mutable unsigned refCount { 0 };
+  mutable void* object { nullptr };
+};
+
+template <typename T>
+class ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr {
+public:
+  ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr() { }
+  void ref() const { controlBlock.strongRef(); }
+  void deref() const { controlBlock.template strongDeref<T>(); }
+private:
+  ThreadSafeRefCountedControlBlock& controlBlock { *new ThreadSafeRefCountedControlBlock(static_cast<T*>(this)) };
 };
 
 } // namespace WTF
@@ -150,8 +170,12 @@ class DerivedClass4 : public WTF::RefCounted<DerivedClass4> { };
 
 class DerivedClass4b : public WTF::ExoticRefCounted<int, DerivedClass4b> { };
 
-class DerivedClass4c : public WTF::BadBase<int, DerivedClass4c> { };
-// expected-warning at -1{{Class 'WTF::BadBase<int, DerivedClass4c>' is used as a base of class 'DerivedClass4c' but doesn't have virtual destructor}}
+class OtherType;
+class DerivedClass4c : public WTF::BadBase<OtherType, DerivedClass4c> { };
+// expected-warning at -1{{Class 'WTF::BadBase<OtherType, DerivedClass4c>' is used as a base of class 'DerivedClass4c' but doesn't have virtual destructor}}
+class OtherType : public DerivedClass4c { };
+// expected-warning at -1{{Class 'DerivedClass4c' is used as a base of class 'OtherType' but doesn't have virtual destructor}}
+void UseDerived4c(DerivedClass4c &obj) { obj.deref(); }
 
 class DerivedClass5 : public DerivedClass4 { };
 // expected-warning at -1{{Class 'DerivedClass4' is used as a base of class 'DerivedClass5' but doesn't have virtual destructor}}
@@ -163,6 +187,9 @@ class DerivedClass7 : public DerivedClass6 { };
 
 class DerivedClass8 : public WTF::ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<DerivedClass8> { };
 
+class DerivedClass8b : public WTF::ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<DerivedClass8b> { };
+void UseDerived8b(DerivedClass8b &obj) { obj.deref(); }
+
 class DerivedClass9 : public DerivedClass8 { };
 // expected-warning at -1{{Class 'DerivedClass8' is used as a base of class 'DerivedClass9' but doesn't have virtual destructor}}
 
@@ -170,6 +197,7 @@ class DerivedClass10 : public WTF::FancyDeref<DerivedClass10> { };
 
 class DerivedClass10b : public WTF::BadFancyDeref<DerivedClass10b> { };
 // expected-warning at -1{{Class 'WTF::BadFancyDeref<DerivedClass10b>' is used as a base of class 'DerivedClass10b' but doesn't have virtual destructor}}
+void UseDerived10b(DerivedClass10b &obj) { obj.deref(); }
 
 class BaseClass1 {
 public:

>From 2ee2bdff5ab887d5cbf700890178f609d39d7333 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at apple.com>
Date: Fri, 17 May 2024 21:31:05 -0700
Subject: [PATCH 7/9] Fix formatting.

---
 .../WebKit/RefCntblBaseVirtualDtorChecker.cpp   | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
index 2a5707837726b..e565b2323063c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -111,7 +111,8 @@ class DerefAnalysisVisitor
           } else if (auto *RD = dyn_cast<RecordType>(PointeeType)) {
             if (RD->getDecl() == ClassDecl)
               return true;
-          } else if (auto* ST = dyn_cast<SubstTemplateTypeParmType>(PointeeType)) {
+          } else if (auto *ST =
+                         dyn_cast<SubstTemplateTypeParmType>(PointeeType)) {
             auto Type = ST->getReplacementType();
             if (auto *RD = dyn_cast<RecordType>(Type)) {
               if (RD->getDecl() == ClassDecl)
@@ -180,7 +181,7 @@ class RefCntblBaseVirtualDtorChecker
     if (shouldSkipDecl(RD))
       return;
 
-    for (auto& Base : RD->bases()) {
+    for (auto &Base : RD->bases()) {
       const auto AccSpec = Base.getAccessSpecifier();
       if (AccSpec == AS_protected || AccSpec == AS_private ||
           (AccSpec == AS_none && RD->isClass()))
@@ -202,8 +203,7 @@ class RefCntblBaseVirtualDtorChecker
 
       bool AnyInconclusiveBase = false;
       const auto hasPublicRefInBase =
-          [&AnyInconclusiveBase](const CXXBaseSpecifier *Base,
-                                 CXXBasePath &) {
+          [&AnyInconclusiveBase](const CXXBaseSpecifier *Base, CXXBasePath &) {
             auto hasRefInBase = clang::hasPublicMethodInBase(Base, "ref");
             if (!hasRefInBase) {
               AnyInconclusiveBase = true;
@@ -235,8 +235,8 @@ class RefCntblBaseVirtualDtorChecker
 
       const auto *Dtor = C->getDestructor();
       if (!Dtor || !Dtor->isVirtual()) {
-        auto* ProblematicBaseSpecifier = &Base;
-        auto* ProblematicBaseClass = C;
+        auto *ProblematicBaseSpecifier = &Base;
+        auto *ProblematicBaseClass = C;
         reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
       }
     }
@@ -284,8 +284,9 @@ class RefCntblBaseVirtualDtorChecker
             ClsName == "ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr");
   }
 
-  static std::optional<bool> isClassWithSpecializedDelete(
-      const CXXRecordDecl *C, const CXXRecordDecl *DerivedClass) {
+  static std::optional<bool>
+  isClassWithSpecializedDelete(const CXXRecordDecl *C,
+                               const CXXRecordDecl *DerivedClass) {
     if (auto *ClsTmplSpDecl = dyn_cast<ClassTemplateSpecializationDecl>(C)) {
       for (auto *MethodDecl : C->methods()) {
         if (safeGetName(MethodDecl) == "deref") {

>From ac43a0cc15f27150e2fa5ba7ff4a17e1002d0a13 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at apple.com>
Date: Mon, 20 May 2024 11:22:32 -0700
Subject: [PATCH 8/9] Add more test cases.

---
 .../ref-cntbl-base-virtual-dtor-templates.cpp | 164 +++++++++++++-----
 1 file changed, 122 insertions(+), 42 deletions(-)

diff --git a/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp b/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
index f04c68cb641f1..d4f751bc1a8a8 100644
--- a/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
@@ -10,8 +10,7 @@ struct DerivedClassTmpl1 : T { };
 // expected-warning at -1{{Struct 'RefCntblBase' is used as a base of struct 'DerivedClassTmpl1<RefCntblBase>' but doesn't have virtual destructor}}
 
 DerivedClassTmpl1<RefCntblBase> a;
-
-
+void foo(DerivedClassTmpl1<RefCntblBase>& obj) { obj.deref(); }
 
 template<class T>
 struct DerivedClassTmpl2 : T { };
@@ -21,7 +20,6 @@ template<class T> int foo(T) { DerivedClassTmpl2<T> f; return 42; }
 int b = foo(RefCntblBase{});
 
 
-
 template<class T>
 struct DerivedClassTmpl3 : T { };
 // expected-warning at -1{{Struct 'RefCntblBase' is used as a base of struct 'DerivedClassTmpl3<RefCntblBase>' but doesn't have virtual destructor}}
@@ -29,7 +27,6 @@ struct DerivedClassTmpl3 : T { };
 typedef DerivedClassTmpl3<RefCntblBase> Foo;
 Foo c;
 
-
 namespace WTF {
 
 class RefCountedBase {
@@ -98,6 +95,93 @@ class FancyDeref {
   mutable unsigned refCount { 0 };
 };
 
+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(WTFMove(callable)) { }
+    CallableWrapper(const CallableWrapper&) = delete;
+    CallableWrapper& operator=(const CallableWrapper&) = delete;
+    Out call(In... in) final { return m_callable(in...); }
+  private:
+    CallableType m_callable;
+  };
+
+} // namespace Detail
+
+template<typename> class Function;
+
+template <typename Out, typename... In>
+class Function<Out(In...)> {
+public:
+  using Impl = Detail::CallableWrapperBase<Out, In...>;
+
+  Function() = default;
+
+  template<typename CallableType>
+  Function(CallableType&& callable)
+      : m_callableWrapper(new Detail::CallableWrapper<CallableType, Out, In...>>(callable)) { }
+
+  template<typename FunctionType>
+  Function(FunctionType f)
+      : m_callableWrapper(new Detail::CallableWrapper<FunctionType, Out, In...>>(f)) { }
+
+  ~Function() {
+  }
+
+  Out operator()(In... in) const {
+      ASSERT(m_callableWrapper);
+      return m_callableWrapper->call(in...);
+  }
+
+  explicit operator bool() const { return !!m_callableWrapper; }
+
+private:
+  Impl* m_callableWrapper;
+};
+
+void ensureOnMainThread(const Function<void()>&& function);
+
+enum class DestructionThread { Any, MainThread };
+
+template <typename T, DestructionThread destructionThread = DestructionThread::Any>
+class FancyDeref2 {
+public:
+  void ref() const
+  {
+    ++refCount;
+  }
+
+  void deref() const
+  {
+    --refCount;
+    if (refCount)
+      return;
+    const_cast<FancyDeref2<T, destructionThread>*>(this)->destroy();
+  }
+
+private:
+  void destroy() {
+    delete static_cast<T*>(this);
+  }
+  mutable unsigned refCount { 0 };
+};
+
+template <typename S>
+class DerivedFancyDeref2 : public FancyDeref2<S> {
+};
+
 template <typename T>
 class BadFancyDeref {
 public:
@@ -132,36 +216,16 @@ class ThreadSafeRefCounted {
   mutable unsigned refCount { 0 };
 };
 
-class ThreadSafeRefCountedControlBlock {
-public:
-  ThreadSafeRefCountedControlBlock(void* object) : object(object) { }
-  void strongRef() const { ++refCount; }
-  template <typename T>
-  void strongDeref() const {
-    --refCount;
-    if (refCount)
-      return;
-
-    auto deleteObject = [&] {
-      delete static_cast<const T*>(object);
-      delete this;
-    };
-
-    deleteObject();
-  }
-private:
-  mutable unsigned refCount { 0 };
-  mutable void* object { nullptr };
-};
-
 template <typename T>
 class ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr {
 public:
-  ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr() { }
-  void ref() const { controlBlock.strongRef(); }
-  void deref() const { controlBlock.template strongDeref<T>(); }
+  void ref() const { ++refCount; }
+  void deref() const {
+    if (!--refCount)
+      delete const_cast<T*>(static_cast<const T*>(this));
+  }
 private:
-  ThreadSafeRefCountedControlBlock& controlBlock { *new ThreadSafeRefCountedControlBlock(static_cast<T*>(this)) };
+  mutable unsigned refCount { 0 };
 };
 
 } // namespace WTF
@@ -170,34 +234,39 @@ class DerivedClass4 : public WTF::RefCounted<DerivedClass4> { };
 
 class DerivedClass4b : public WTF::ExoticRefCounted<int, DerivedClass4b> { };
 
-class OtherType;
-class DerivedClass4c : public WTF::BadBase<OtherType, DerivedClass4c> { };
-// expected-warning at -1{{Class 'WTF::BadBase<OtherType, DerivedClass4c>' is used as a base of class 'DerivedClass4c' but doesn't have virtual destructor}}
-class OtherType : public DerivedClass4c { };
-// expected-warning at -1{{Class 'DerivedClass4c' is used as a base of class 'OtherType' but doesn't have virtual destructor}}
-void UseDerived4c(DerivedClass4c &obj) { obj.deref(); }
+class DerivedClass4cSub;
+class DerivedClass4c : public WTF::BadBase<DerivedClass4cSub, DerivedClass4c> { };
+// expected-warning at -1{{Class 'WTF::BadBase<DerivedClass4cSub, DerivedClass4c>' is used as a base of class 'DerivedClass4c' but doesn't have virtual destructor}}
+class DerivedClass4cSub : public DerivedClass4c { };
+void UseDerivedClass4c(DerivedClass4c &obj) { obj.deref(); }
 
 class DerivedClass5 : public DerivedClass4 { };
 // expected-warning at -1{{Class 'DerivedClass4' is used as a base of class 'DerivedClass5' but doesn't have virtual destructor}}
+void UseDerivedClass5(DerivedClass5 &obj) { obj.deref(); }
 
 class DerivedClass6 : public WTF::ThreadSafeRefCounted<DerivedClass6> { };
+void UseDerivedClass6(DerivedClass6 &obj) { obj.deref(); }
 
 class DerivedClass7 : public DerivedClass6 { };
 // expected-warning at -1{{Class 'DerivedClass6' is used as a base of class 'DerivedClass7' but doesn't have virtual destructor}}
+void UseDerivedClass7(DerivedClass7 &obj) { obj.deref(); }
 
 class DerivedClass8 : public WTF::ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<DerivedClass8> { };
-
-class DerivedClass8b : public WTF::ThreadSafeRefCountedAndCanMakeThreadSafeWeakPtr<DerivedClass8b> { };
-void UseDerived8b(DerivedClass8b &obj) { obj.deref(); }
+void UseDerivedClass8(DerivedClass8 &obj) { obj.deref(); }
 
 class DerivedClass9 : public DerivedClass8 { };
 // expected-warning at -1{{Class 'DerivedClass8' is used as a base of class 'DerivedClass9' but doesn't have virtual destructor}}
+void UseDerivedClass9(DerivedClass9 &obj) { obj.deref(); }
 
 class DerivedClass10 : public WTF::FancyDeref<DerivedClass10> { };
+void UseDerivedClass10(DerivedClass10 &obj) { obj.deref(); }
+
+class DerivedClass10b : public WTF::DerivedFancyDeref2<DerivedClass10b> { };
+void UseDerivedClass10b(DerivedClass10b &obj) { obj.deref(); }
 
-class DerivedClass10b : public WTF::BadFancyDeref<DerivedClass10b> { };
-// expected-warning at -1{{Class 'WTF::BadFancyDeref<DerivedClass10b>' is used as a base of class 'DerivedClass10b' but doesn't have virtual destructor}}
-void UseDerived10b(DerivedClass10b &obj) { obj.deref(); }
+class DerivedClass10c : public WTF::BadFancyDeref<DerivedClass10c> { };
+// expected-warning at -1{{Class 'WTF::BadFancyDeref<DerivedClass10c>' is used as a base of class 'DerivedClass10c' but doesn't have virtual destructor}}
+void UseDerivedClass10c(DerivedClass10c &obj) { obj.deref(); }
 
 class BaseClass1 {
 public:
@@ -225,6 +294,8 @@ void BaseClass1::deref() const
   }
 }
 
+void UseDerivedClass11(DerivedClass11& obj) { obj.deref(); }
+
 class BaseClass2;
 static void deleteBase2(BaseClass2*);
 
@@ -245,6 +316,8 @@ class DerivedClass12 : public BaseClass2 {
   bool isDerived() final { return true; }
 };
 
+void UseDerivedClass11(DerivedClass12& obj) { obj.deref(); }
+
 void deleteBase2(BaseClass2* obj) {
   if (obj->isDerived())
     delete static_cast<DerivedClass12*>(obj);
@@ -272,6 +345,8 @@ class DerivedClass13 : public BaseClass3 {
   bool isDerived() final { return true; }
 };
 
+void UseDerivedClass11(DerivedClass13& obj) { obj.deref(); }
+
 void BaseClass3::destory() {
   if (isDerived())
     delete static_cast<DerivedClass13*>(this);
@@ -309,4 +384,9 @@ class DerivedClass14 : public WTF::RefCounted<DerivedClass14> {
 public:
   virtual ~DerivedClass14() { }
 };
+
+void UseDerivedClass14(DerivedClass14& obj) { obj.deref(); }
+
 class DerivedClass15 : public DerivedClass14 { };
+
+void UseDerivedClass15(DerivedClass15& obj) { obj.deref(); }

>From 7a2509f380dc5143a5a0bdf65224b6198aa4a122 Mon Sep 17 00:00:00 2001
From: Ryosuke Niwa <rniwa at apple.com>
Date: Mon, 20 May 2024 15:56:26 -0700
Subject: [PATCH 9/9] [webkit.RefCntblBaseVirtualDtor] Exclude CRTP (curiously
 recurring template pattern) classes from analysis.

---
 .../WebKit/RefCntblBaseVirtualDtorChecker.cpp | 57 ++++++++++++++++++-
 .../ref-cntbl-base-virtual-dtor-templates.cpp |  8 ++-
 2 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
index e565b2323063c..a145d6e3117db 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -17,6 +17,7 @@
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/SetVector.h"
 #include <optional>
 
 using namespace clang;
@@ -168,13 +169,56 @@ class RefCntblBaseVirtualDtorChecker
       bool shouldVisitImplicitCode() const { return false; }
 
       bool VisitCXXRecordDecl(const CXXRecordDecl *RD) {
-        Checker->visitCXXRecordDecl(RD);
+        if (!RD->hasDefinition())
+          return true;
+
+        Decls.insert(RD);
+
+        for (auto &Base : RD->bases()) {
+          const auto AccSpec = Base.getAccessSpecifier();
+          if (AccSpec == AS_protected || AccSpec == AS_private ||
+              (AccSpec == AS_none && RD->isClass()))
+            continue;
+
+          QualType T = Base.getType();
+          if (T.isNull())
+            continue;
+
+          const CXXRecordDecl *C = T->getAsCXXRecordDecl();
+          if (!C)
+            continue;
+
+          if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(C)) {
+            auto &Args = CTSD->getTemplateArgs();
+            for (unsigned i = 0; i < Args.size(); ++i) {
+              if (Args[i].getKind() != TemplateArgument::Type)
+                continue;
+              auto TemplT = Args[i].getAsType();
+              if (TemplT.isNull())
+                continue;
+
+              bool IsCRTP = TemplT->getAsCXXRecordDecl() == RD;
+              if (!IsCRTP)
+                continue;
+              CRTPs.insert(C);
+            }
+          }
+        }
+
         return true;
       }
+
+      llvm::SetVector<const CXXRecordDecl*> Decls;
+      llvm::DenseSet<const CXXRecordDecl*> CRTPs;
     };
 
     LocalVisitor visitor(this);
     visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+    for (auto *RD : visitor.Decls) {
+      if (visitor.CRTPs.contains(RD))
+        continue;
+      visitCXXRecordDecl(RD);
+    }
   }
 
   void visitCXXRecordDecl(const CXXRecordDecl *RD) const {
@@ -232,6 +276,17 @@ class RefCntblBaseVirtualDtorChecker
       auto HasSpecializedDelete = isClassWithSpecializedDelete(C, RD);
       if (!HasSpecializedDelete || *HasSpecializedDelete)
         continue;
+      if (C->lookupInBases([&](const CXXBaseSpecifier *Base, CXXBasePath &) {
+        auto *T = Base->getType().getTypePtrOrNull();
+        if (!T)
+          return false;
+        auto *R = T->getAsCXXRecordDecl();
+        if (!R)
+          return false;
+        auto Result = isClassWithSpecializedDelete(R, RD);
+        return Result && *Result;
+        }, Paths, /*LookupInDependent =*/true))
+        continue;
 
       const auto *Dtor = C->getDestructor();
       if (!Dtor || !Dtor->isVirtual()) {
diff --git a/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp b/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
index d4f751bc1a8a8..4fc1624d7a154 100644
--- a/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
+++ b/clang/test/Analysis/Checkers/WebKit/ref-cntbl-base-virtual-dtor-templates.cpp
@@ -56,7 +56,7 @@ class RefCounted : public RefCountedBase {
 };
 
 template <typename X, typename T>
-class ExoticRefCounted : RefCountedBase {
+class ExoticRefCounted : public RefCountedBase {
 public:
   void deref() const {
     if (derefBase())
@@ -240,6 +240,12 @@ class DerivedClass4c : public WTF::BadBase<DerivedClass4cSub, DerivedClass4c> {
 class DerivedClass4cSub : public DerivedClass4c { };
 void UseDerivedClass4c(DerivedClass4c &obj) { obj.deref(); }
 
+class DerivedClass4d : public WTF::RefCounted<DerivedClass4d> {
+public:
+  virtual ~DerivedClass4d() { }
+};
+class DerivedClass4dSub : public DerivedClass4d { };
+
 class DerivedClass5 : public DerivedClass4 { };
 // expected-warning at -1{{Class 'DerivedClass4' is used as a base of class 'DerivedClass5' but doesn't have virtual destructor}}
 void UseDerivedClass5(DerivedClass5 &obj) { obj.deref(); }



More information about the cfe-commits mailing list