https://github.com/mugiwaraluffy56 updated 
https://github.com/llvm/llvm-project/pull/178654

>From 4bbad86067c93f1cbb27cd60ef695af50f05ea0d Mon Sep 17 00:00:00 2001
From: mugiwaraluffy56 <[email protected]>
Date: Thu, 29 Jan 2026 19:09:20 +0530
Subject: [PATCH] [clang][analyzer] Don't warn about virtual calls in final
 class destructors

When a virtual method is called during construction or destruction of an
object whose type is a final class, virtual dispatch is safe because there
can be no derived classes that could override the method.

This patch checks:
1. If the object's type is effectively final
2. If the called method's class is effectively final

Added comprehensive tests including nested/interprocedural call scenarios.

Fixes #178643.

Assisted-by: claude
---
 .../Checkers/VirtualCallChecker.cpp           | 15 ++++
 clang/test/Analysis/virtualcall.cpp           | 75 ++++++++++++++++++-
 2 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
index 6c27f58d308aa..b6a55edb62666 100644
--- a/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -123,6 +123,21 @@ void VirtualCallChecker::checkPreCall(const CallEvent 
&Call,
   if (!ObState)
     return;
 
+  // If we're directly inside the constructor/destructor of a final class,
+  // virtual dispatch is safe because there can be no derived classes.
+  // We only suppress when directly in the ctor/dtor, not in helper functions
+  // (which could be called from non-final classes), and not in base class
+  // ctors/dtors (which have broken vtables even when constructing/destroying
+  // a final derived class).
+  const auto *LCtx = C.getLocationContext();
+  if (const auto *CD = dyn_cast<CXXConstructorDecl>(LCtx->getDecl())) {
+    if (CD->getParent()->isEffectivelyFinal())
+      return;
+  } else if (const auto *DD = dyn_cast<CXXDestructorDecl>(LCtx->getDecl())) {
+    if (DD->getParent()->isEffectivelyFinal())
+      return;
+  }
+
   bool IsPure = MD->isPureVirtual();
 
   // At this point we're sure that we're calling a virtual method
diff --git a/clang/test/Analysis/virtualcall.cpp 
b/clang/test/Analysis/virtualcall.cpp
index 82285b6d12844..0a7f9fd1df971 100644
--- a/clang/test/Analysis/virtualcall.cpp
+++ b/clang/test/Analysis/virtualcall.cpp
@@ -84,6 +84,75 @@ class E final : public B {
   int foo() override;
 };
 
+// GH#178643: Virtual calls in destructor of a final class should not warn.
+class GH178643Base {
+public:
+  virtual void virtualMethod() {}
+  virtual ~GH178643Base() {
+    // Base class destructor should still warn even when destructing a final
+    // derived class, because the vtable points to the base class at this 
point.
+    virtualMethod(); // impure-warning {{Call to virtual method 
'GH178643Base::virtualMethod' during destruction bypasses virtual dispatch}}
+  }
+};
+
+class GH178643Derived final : public GH178643Base {
+public:
+  ~GH178643Derived() {
+    virtualMethod(); // no-warning: class is final, no derived classes exist
+  }
+};
+
+// Test constructor case for final class.
+class GH178643CtorBase {
+public:
+  virtual void virtualMethod() {}
+  GH178643CtorBase() {
+    virtualMethod(); // impure-warning {{Call to virtual method 
'GH178643CtorBase::virtualMethod' during construction bypasses virtual 
dispatch}}
+  }
+};
+
+class GH178643CtorDerived final : public GH178643CtorBase {
+public:
+  GH178643CtorDerived() {
+    virtualMethod(); // no-warning: class is final
+  }
+};
+
+// Test nested calls from destructor - destructor calls helper which makes
+// virtual call. This tests interprocedural behavior.
+class GH178643NestedBase {
+public:
+  virtual void virtualMethod() {}
+  void helper() {
+    // Called from destructor via nested call - should still warn because
+    // this helper could be called from non-final derived classes too.
+    virtualMethod(); // impure-warning {{Call to virtual method 
'GH178643NestedBase::virtualMethod' during destruction bypasses virtual 
dispatch}}
+  }
+  virtual ~GH178643NestedBase() = default;
+};
+
+class GH178643NestedDerived final : public GH178643NestedBase {
+public:
+  ~GH178643NestedDerived() {
+    helper(); // Calls helper() which calls virtualMethod()
+  }
+};
+
+// Test: final class with method overridden - should not warn.
+class GH178643OverrideBase {
+public:
+  virtual void virtualMethod() {}
+  virtual ~GH178643OverrideBase() = default;
+};
+
+class GH178643OverrideDerived final : public GH178643OverrideBase {
+public:
+  void virtualMethod() override {}
+  ~GH178643OverrideDerived() {
+    virtualMethod(); // no-warning: method is in final class
+  }
+};
+
 class F {
 public:
   F() {
@@ -175,12 +244,16 @@ int main() {
   G g;
   H h;
   H h1(1);
-  X x; 
+  X x;
   X x1(1);
   M m;
   Y *y = new Y;
   delete y;
   header::Z z;
+  GH178643Derived gh178643;
+  GH178643CtorDerived gh178643ctor;
+  GH178643NestedDerived gh178643nested;
+  GH178643OverrideDerived gh178643override;
 }
 
 namespace PR34451 {

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

Reply via email to