https://github.com/mugiwaraluffy56 updated https://github.com/llvm/llvm-project/pull/178654
>From cff4182a6478799d7b7ef4bf595b2146db2359b2 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 | 71 ++++++++++++++++++- 2 files changed, 85 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..b4207561b0cb1 100644 --- a/clang/test/Analysis/virtualcall.cpp +++ b/clang/test/Analysis/virtualcall.cpp @@ -84,6 +84,71 @@ 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 that we still warn for non-final classes even +// when the virtual call happens through a helper function. +class GH178643NestedNonFinal { +public: + virtual void virtualMethod() {} + void helper() { + // Called from destructor via nested call - should warn because + // this class is not final and could have derived classes. + virtualMethod(); // impure-warning {{Call to virtual method 'GH178643NestedNonFinal::virtualMethod' during destruction bypasses virtual dispatch}} + } + virtual ~GH178643NestedNonFinal() { + 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 +240,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
