https://github.com/CarvedCoder updated 
https://github.com/llvm/llvm-project/pull/183384

>From 5611ee076ce0b1c1b4daf989ff245f6c3628d3ec Mon Sep 17 00:00:00 2001
From: CarvedCoder <[email protected]>
Date: Thu, 26 Feb 2026 02:12:14 +0530
Subject: [PATCH 1/4] [clang-tidy] Add misc-forbid-non-virtual-base-dtor check

Adds a clang-tidy check that warns when a class inherits from a base
class
with a non-virtual destructor while introducing data members. Deleting
such
objects through a base pointer can lead to undefined behavior because
the
derived portion of the object will not be destroyed.

The check skips cases where the base class has a virtual destructor, a
protected non-virtual destructor, or when the derived class introduces
no
data members.

Includes tests and documentation.

Fixes #183101
---
 .../clang-tidy/misc/CMakeLists.txt            |  1 +
 .../misc/ForbidNonVirtualBaseDtorCheck.cpp    | 52 +++++++++++++++++++
 .../misc/ForbidNonVirtualBaseDtorCheck.h      | 35 +++++++++++++
 .../clang-tidy/misc/MiscTidyModule.cpp        |  3 ++
 clang-tools-extra/docs/ReleaseNotes.rst       |  7 +++
 .../docs/clang-tidy/checks/list.rst           |  1 +
 .../misc/forbid-non-virtual-base-dtor.rst     | 52 +++++++++++++++++++
 .../misc/forbid-non-virtual-base-dtor.cpp     | 28 ++++++++++
 8 files changed, 179 insertions(+)
 create mode 100644 
clang-tools-extra/clang-tidy/misc/ForbidNonVirtualBaseDtorCheck.cpp
 create mode 100644 
clang-tools-extra/clang-tidy/misc/ForbidNonVirtualBaseDtorCheck.h
 create mode 100644 
clang-tools-extra/docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/misc/forbid-non-virtual-base-dtor.cpp

diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
index e34b0cf687be3..4552b2a60ce80 100644
--- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
@@ -23,6 +23,7 @@ add_clang_library(clangTidyMiscModule STATIC
   CoroutineHostileRAIICheck.cpp
   DefinitionsInHeadersCheck.cpp
   ConfusableIdentifierCheck.cpp
+  ForbidNonVirtualBaseDtorCheck.cpp
   HeaderIncludeCycleCheck.cpp
   IncludeCleanerCheck.cpp
   MiscTidyModule.cpp
diff --git 
a/clang-tools-extra/clang-tidy/misc/ForbidNonVirtualBaseDtorCheck.cpp 
b/clang-tools-extra/clang-tidy/misc/ForbidNonVirtualBaseDtorCheck.cpp
new file mode 100644
index 0000000000000..d26d412a4da6c
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/misc/ForbidNonVirtualBaseDtorCheck.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ForbidNonVirtualBaseDtorCheck.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/Specifiers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::misc {
+
+void ForbidNonVirtualBaseDtorCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      cxxRecordDecl(isDefinition(),
+                    hasAnyBase(cxxBaseSpecifier().bind("BaseSpecifier")))
+          .bind("derived"),
+      this);
+}
+
+void ForbidNonVirtualBaseDtorCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Derived = Result.Nodes.getNodeAs<CXXRecordDecl>("derived");
+  const auto *BaseSpecifier =
+      Result.Nodes.getNodeAs<CXXBaseSpecifier>("BaseSpecifier");
+  if (!Derived || !BaseSpecifier)
+    return;
+  if (BaseSpecifier->getAccessSpecifier() != AS_public)
+    return;
+  const auto *BaseType = BaseSpecifier->getType()->getAsCXXRecordDecl();
+  if (!BaseType || !BaseType->hasDefinition())
+    return;
+  const auto *Dtor = BaseType->getDestructor();
+  if (Dtor && Dtor->isVirtual())
+    return;
+  if (Dtor && Dtor->getAccess() == AS_protected && !Dtor->isVirtual())
+    return;
+  if (Derived->isEmpty())
+    return;
+
+  diag(Derived->getLocation(),
+       "class '%0' inherits from '%1' which has a non-virtual destructor")
+      << Derived->getName() << BaseType->getName();
+}
+
+} // namespace clang::tidy::misc
diff --git a/clang-tools-extra/clang-tidy/misc/ForbidNonVirtualBaseDtorCheck.h 
b/clang-tools-extra/clang-tidy/misc/ForbidNonVirtualBaseDtorCheck.h
new file mode 100644
index 0000000000000..6c2286b1dfc84
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/misc/ForbidNonVirtualBaseDtorCheck.h
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORBIDNONVIRTUALBASEDTORCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORBIDNONVIRTUALBASEDTORCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::misc {
+
+/// Warns when a class or struct publicly inherits from a base class or struct
+/// whose destructor is neither virtual nor protected, and the derived class
+/// adds data members
+///
+/// For the user-facing documentation see:
+/// 
https://clang.llvm.org/extra/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.html
+class ForbidNonVirtualBaseDtorCheck : public ClangTidyCheck {
+public:
+  ForbidNonVirtualBaseDtorCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+};
+
+} // namespace clang::tidy::misc
+
+#endif // 
LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_FORBIDNONVIRTUALBASEDTORCHECK_H
diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp 
b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
index f8550b30b9789..671415f2648ab 100644
--- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
@@ -13,6 +13,7 @@
 #include "ConstCorrectnessCheck.h"
 #include "CoroutineHostileRAIICheck.h"
 #include "DefinitionsInHeadersCheck.h"
+#include "ForbidNonVirtualBaseDtorCheck.h"
 #include "HeaderIncludeCycleCheck.h"
 #include "IncludeCleanerCheck.h"
 #include "MisleadingBidirectionalCheck.h"
@@ -53,6 +54,8 @@ class MiscModule : public ClangTidyModule {
         "misc-coroutine-hostile-raii");
     CheckFactories.registerCheck<DefinitionsInHeadersCheck>(
         "misc-definitions-in-headers");
+    CheckFactories.registerCheck<ForbidNonVirtualBaseDtorCheck>(
+        "misc-forbid-non-virtual-base-dtor");
     CheckFactories.registerCheck<HeaderIncludeCycleCheck>(
         "misc-header-include-cycle");
     CheckFactories.registerCheck<IncludeCleanerCheck>("misc-include-cleaner");
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 64c8fbbe2f07a..518eba0935943 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -124,6 +124,13 @@ New checks
   ``llvm::to_vector(llvm::make_filter_range(...))`` that can be replaced with
   ``llvm::map_to_vector`` and ``llvm::filter_to_vector``.
 
+- New :doc:`misc-forbid-non-virtual-base-dtor
+  <clang-tidy/checks/misc/forbid-non-virtual-base-dtor>` check.
+
+  Warns when a class or struct publicly inherits from a base whose destructor
+  is neither virtual nor protected, and the derived type adds data members.
+  This pattern causes resource leaks when deleting through a base pointer.
+
 - New :doc:`modernize-use-string-view
   <clang-tidy/checks/modernize/use-string-view>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst 
b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index c475870ed7b31..8045a5e518c97 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -266,6 +266,7 @@ Clang-Tidy Checks
    :doc:`misc-const-correctness <misc/const-correctness>`, "Yes"
    :doc:`misc-coroutine-hostile-raii <misc/coroutine-hostile-raii>`,
    :doc:`misc-definitions-in-headers <misc/definitions-in-headers>`, "Yes"
+   :doc:`misc-forbid-non-virtual-base-dtor 
<misc/forbid-non-virtual-base-dtor>`, "Yes"
    :doc:`misc-header-include-cycle <misc/header-include-cycle>`,
    :doc:`misc-include-cleaner <misc/include-cleaner>`, "Yes"
    :doc:`misc-misleading-bidirectional <misc/misleading-bidirectional>`,
diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst
 
b/clang-tools-extra/docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst
new file mode 100644
index 0000000000000..1c446adf224b2
--- /dev/null
+++ 
b/clang-tools-extra/docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst
@@ -0,0 +1,52 @@
+.. title:: clang-tidy - misc-forbid-non-virtual-base-dtor
+
+misc-forbid-non-virtual-base-dtor
+=================================
+
+Warns when a class or struct publicly inherits from a base class or struct
+whose destructor is neither virtual nor protected, and the derived class adds
+data members. This pattern causes resource leaks when the derived object is
+deleted through a base class pointer, because the derived destructor is never
+called.
+
+Examples
+--------
+
+The following code will trigger a warning:
+
+.. code-block:: c++
+
+  class Base {};  // non-virtual destructor
+
+  class Derived : public Base {  // warning: class 'Derived' inherits from
+      int data;                  // 'Base' which has a non-virtual destructor
+  };
+
+  Base *b = new Derived();
+  delete b;  // leaks Derived::data —> Base::~Base() is called, not ~Derived()
+
+The following patterns are safe and will **not** trigger a warning:
+
+.. code-block:: c++
+
+  // safe: base has a virtual destructor
+  class Base1 {
+  public:
+      virtual ~Base1() {}
+  };
+  class Derived1 : public Base1 { int data; };
+
+  // safe: base has a protected destructor (prevents delete-through-base)
+  class Base2 {
+  protected:
+      ~Base2() {}
+  };
+  class Derived2 : public Base2 { int data; };
+
+  // safe: derived adds no data members
+  class Base3 {};
+  class Derived3 : public Base3 {};  // OK
+
+  // safe: private/protected inheritance (base pointer not accessible)
+  class Base4 {};
+  class Derived4 : private Base4 { int data; };
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/misc/forbid-non-virtual-base-dtor.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/misc/forbid-non-virtual-base-dtor.cpp
new file mode 100644
index 0000000000000..0b7024dfd0249
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/misc/forbid-non-virtual-base-dtor.cpp
@@ -0,0 +1,28 @@
+// RUN: %check_clang_tidy %s misc-forbid-non-virtual-base-dtor %t
+
+// should warn -> non-virtual base + derived has data
+class A {};
+class B: public A{
+    int x;
+};
+// CHECK-MESSAGES: warning: class 'B' inherits from 'A' which has a 
non-virtual destructor [misc-forbid-non-virtual-base-dtor]
+
+//shouldn't warn -> derived has no data
+class C : public A{};
+
+// shouldn't warn -> base has virtual destructor
+class D {
+    public:
+    virtual ~D(){};
+};
+class E : public D{
+    int y;
+};
+
+//shouldn't crash -> incomplete base
+class F;
+class F {};
+class G: public F{
+    int z;
+};
+// CHECK-MESSAGES: warning: class 'G' inherits from 'F' which has a 
non-virtual destructor [misc-forbid-non-virtual-base-dtor]

>From 9d667605941e771a0e08fc7e60f760a5d4210772 Mon Sep 17 00:00:00 2001
From: CarvedCoder <[email protected]>
Date: Thu, 26 Feb 2026 07:21:55 +0530
Subject: [PATCH 2/4] Apply suggestion from @EugeneZelenko

Co-authored-by: EugeneZelenko <[email protected]>
---
 clang-tools-extra/docs/ReleaseNotes.rst | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 518eba0935943..1244344c4ca67 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -129,7 +129,6 @@ New checks
 
   Warns when a class or struct publicly inherits from a base whose destructor
   is neither virtual nor protected, and the derived type adds data members.
-  This pattern causes resource leaks when deleting through a base pointer.
 
 - New :doc:`modernize-use-string-view
   <clang-tidy/checks/modernize/use-string-view>` check.

>From 580ddf0d58107e5e97d6de8fa1739502023aaa09 Mon Sep 17 00:00:00 2001
From: CarvedCoder <[email protected]>
Date: Thu, 26 Feb 2026 07:27:04 +0530
Subject: [PATCH 3/4] Clean up comments in forbid-non-virtual-base-dtor.rst

---
 .../clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst  | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst
 
b/clang-tools-extra/docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst
index 1c446adf224b2..1de816fe5dde6 100644
--- 
a/clang-tools-extra/docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst
+++ 
b/clang-tools-extra/docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst
@@ -29,24 +29,21 @@ The following patterns are safe and will **not** trigger a 
warning:
 
 .. code-block:: c++
 
-  // safe: base has a virtual destructor
   class Base1 {
   public:
       virtual ~Base1() {}
   };
   class Derived1 : public Base1 { int data; };
 
-  // safe: base has a protected destructor (prevents delete-through-base)
+destructor (prevents delete-through-base)
   class Base2 {
   protected:
       ~Base2() {}
   };
   class Derived2 : public Base2 { int data; };
 
-  // safe: derived adds no data members
   class Base3 {};
   class Derived3 : public Base3 {};  // OK
 
-  // safe: private/protected inheritance (base pointer not accessible)
   class Base4 {};
   class Derived4 : private Base4 { int data; };

>From d564101c113dfc0a35a5c56a05480ed827ae9934 Mon Sep 17 00:00:00 2001
From: CarvedCoder <[email protected]>
Date: Thu, 26 Feb 2026 07:31:41 +0530
Subject: [PATCH 4/4] Removed destructor note in forbid-non-virtual-base-dtor

---
 .../docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst | 1 -
 1 file changed, 1 deletion(-)

diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst
 
b/clang-tools-extra/docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst
index 1de816fe5dde6..bcef92608f588 100644
--- 
a/clang-tools-extra/docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst
+++ 
b/clang-tools-extra/docs/clang-tidy/checks/misc/forbid-non-virtual-base-dtor.rst
@@ -35,7 +35,6 @@ The following patterns are safe and will **not** trigger a 
warning:
   };
   class Derived1 : public Base1 { int data; };
 
-destructor (prevents delete-through-base)
   class Base2 {
   protected:
       ~Base2() {}

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

Reply via email to