https://github.com/NagyDonat updated 
https://github.com/llvm/llvm-project/pull/224070

From f437b536322b21820e962b8f45dd77204fbb9751 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]>
Date: Wed, 16 Sep 2026 15:26:00 +0200
Subject: [PATCH 01/10] Add failing reproducer from gh#222960

---
 clang/test/Analysis/PR222960.cpp | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
 create mode 100644 clang/test/Analysis/PR222960.cpp

diff --git a/clang/test/Analysis/PR222960.cpp b/clang/test/Analysis/PR222960.cpp
new file mode 100644
index 0000000000000..356594af424b9
--- /dev/null
+++ b/clang/test/Analysis/PR222960.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -verify %s
+
+struct Msg
+{
+  virtual ~Msg() {}
+  virtual unsigned cmd() const = 0;
+};
+
+struct Ctrl : Msg
+{
+  unsigned c;
+  unsigned cmd() const final { return c; }  // final: no override can exist
+};
+
+void clang_analyzer_dump(unsigned);
+void clang_analyzer_eval(bool);
+
+void test(Ctrl* p)
+{
+  clang_analyzer_dump(p->cmd());
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Element{SymRegion{reg_${{[0-9]+}}<Ctrl * p>},0 S64b,struct Ctrl}.c>}}
+  clang_analyzer_eval(p->cmd() == p->cmd()); // expected-warning {{TRUE}}
+}

From 604aa82c591536da87ec80bc5cfe82b4f7a31ffc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]>
Date: Wed, 16 Sep 2026 15:29:26 +0200
Subject: [PATCH 02/10] Inline 'final' methods directly

---
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp 
b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 2338c06d5f992..1ab1852507e8b 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -798,6 +798,10 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() 
const {
   if (!MD->isVirtual())
     return AnyFunctionCall::getRuntimeDefinition();
 
+  // If the method is final or declared in a final class, we can inline it.
+  if (MD->hasAttr<FinalAttr>() || MD->getParent()->hasAttr<FinalAttr>())
+    return AnyFunctionCall::getRuntimeDefinition();
+
   auto [RD, CanBeSubClass] = getDeclForDynamicType();
   if (!RD || !RD->hasDefinition())
     return {};

From 58b39aeb666d3a374bfbfd4f01d9be18c49b6565 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]>
Date: Wed, 16 Sep 2026 17:43:26 +0200
Subject: [PATCH 03/10] Add more tests

---
 clang/test/Analysis/PR222960.cpp              |  23 ----
 .../test/Analysis/inlining-final-methods.cpp  | 126 ++++++++++++++++++
 2 files changed, 126 insertions(+), 23 deletions(-)
 delete mode 100644 clang/test/Analysis/PR222960.cpp
 create mode 100644 clang/test/Analysis/inlining-final-methods.cpp

diff --git a/clang/test/Analysis/PR222960.cpp b/clang/test/Analysis/PR222960.cpp
deleted file mode 100644
index 356594af424b9..0000000000000
--- a/clang/test/Analysis/PR222960.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -verify %s
-
-struct Msg
-{
-  virtual ~Msg() {}
-  virtual unsigned cmd() const = 0;
-};
-
-struct Ctrl : Msg
-{
-  unsigned c;
-  unsigned cmd() const final { return c; }  // final: no override can exist
-};
-
-void clang_analyzer_dump(unsigned);
-void clang_analyzer_eval(bool);
-
-void test(Ctrl* p)
-{
-  clang_analyzer_dump(p->cmd());
-  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Element{SymRegion{reg_${{[0-9]+}}<Ctrl * p>},0 S64b,struct Ctrl}.c>}}
-  clang_analyzer_eval(p->cmd() == p->cmd()); // expected-warning {{TRUE}}
-}
diff --git a/clang/test/Analysis/inlining-final-methods.cpp 
b/clang/test/Analysis/inlining-final-methods.cpp
new file mode 100644
index 0000000000000..689cffed97f5c
--- /dev/null
+++ b/clang/test/Analysis/inlining-final-methods.cpp
@@ -0,0 +1,126 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -verify %s
+
+void clang_analyzer_dump(unsigned);
+void clang_analyzer_eval(bool);
+void clang_analyzer_warnIfReached();
+
+struct Msg {
+  virtual unsigned cmd() const = 0;
+};
+
+namespace gh222960 {
+// Ctrl::cmd() is final, the analyzer should not split off a "maybe dynamic
+// dispatch invokes a different overriding method" execution path, and only
+// follow the path where the method body is inlined.
+struct Ctrl : Msg {
+  unsigned c;
+  unsigned cmd() const final { return c; }
+};
+
+void test(Ctrl* p) {
+  clang_analyzer_dump(p->cmd());
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Element{SymRegion{reg_${{[0-9]+}}<Ctrl * p>},0 S64b,struct 
{{[0-9A-Za-z_]+}}::Ctrl}.c>}}
+  clang_analyzer_eval(p->cmd() == p->cmd()); // expected-warning {{TRUE}}
+}
+} // namespace gh222960
+
+namespace final_struct {
+// The analyzer should also confidently inline the method of a final class.
+struct Ctrl final : Msg {
+  unsigned c;
+  unsigned cmd() const override { return c; }
+};
+
+void test(Ctrl* p)
+{
+  clang_analyzer_dump(p->cmd());
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Element{SymRegion{reg_${{[0-9]+}}<Ctrl * p>},0 S64b,struct 
{{[0-9A-Za-z_]+}}::Ctrl}.c>}}
+  clang_analyzer_eval(p->cmd() == p->cmd()); // expected-warning {{TRUE}}
+}
+} // namespace final_class
+
+namespace final_method_on_child_ptr {
+// A final method should also be inlined when it is called through a pointer
+// whose (static) type is a child of the class where it was defined.
+struct Ctrl : Msg {
+  unsigned c;
+  unsigned cmd() const final { return c; }
+};
+
+struct Child : Ctrl {};
+
+void test(Child* p) {
+  clang_analyzer_dump(p->cmd());
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Base{SymRegion{reg_${{[0-9]+}}<Child * p>},Ctrl}.c>}}
+  clang_analyzer_eval(p->cmd() == p->cmd()); // expected-warning {{TRUE}}
+}
+} // namespace final_method_on_child_ptr
+
+namespace final_method_on_ptr_with_dyn_type_child {
+// A final method should also be inlined when it is called through a pointer
+// whose dynamic type is a child of the class where it was defined.
+struct Ctrl : Msg {
+  unsigned c;
+  unsigned cmd() const final { return c; }
+};
+
+struct Child : Ctrl {};
+
+void test(Ctrl* p) {
+  clang_analyzer_dump(p->cmd());
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Base{SymRegion{reg_${{[0-9]+}}<Child * p>},Ctrl}.c>}}
+  clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  clang_analyzer_eval(p->cmd() == p->cmd());
+  // FIXME: For unclear reasons, this clang_analyzer_eval call is not reached.
+}
+
+void entrypoint(Child *p) {
+  test(p);
+}
+} // namespace final_method_on_ptr_with_dyn_type_child
+
+namespace final_method_on_base_ptr_with_known_dyn_type {
+// A final method should also be inlined when it is called through a pointer
+// whose dynamic type is a child of the class where it was defined.
+// FIXME: This is not yet implemented, 'final' is only checked on the method
+// and class declaration corresponding to the static type of the pointee.
+struct Base : Msg {};
+
+struct Ctrl : Base {
+  unsigned c;
+  unsigned cmd() const final { return c; }
+};
+
+void test(Base* p) {
+  clang_analyzer_dump(p->cmd());
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Element{SymRegion{reg_${{[0-9]+}}<Ctrl * p>},0 S64b,struct 
{{[0-9A-Za-z_]+}}::Ctrl}.c>}}
+  // expected-warning@-2 {{conj_$}}
+  clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  clang_analyzer_eval(p->cmd() == p->cmd());
+  // FIXME: For unclear reasons, this clang_analyzer_eval call is not reached.
+}
+
+void entrypoint(Ctrl *p) {
+  test(p);
+}
+} // namespace final_method_on_base_ptr_with_known_dyn_type
+
+namespace nonfinal_bifurcates {
+// When the method is non-final and the dynamic type is unclear, the analysis
+// should bifurcate, with one branch inlining the method and the other branch
+// doing a conservative evaluation (which represents that another overriding
+// method is called).
+struct Ctrl : Msg {
+  unsigned c;
+  unsigned cmd() const override { return c; }
+};
+
+void test(Ctrl* p) {
+  clang_analyzer_dump(p->cmd());
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Element{SymRegion{reg_${{[0-9]+}}<Ctrl * p>},0 S64b,struct 
{{[0-9A-Za-z_]+}}::Ctrl}.c>}}
+  // expected-warning@-2 {{conj_$}}
+  clang_analyzer_eval(p->cmd() == p->cmd());
+  // expected-warning@-1 {{TRUE}}
+  // expected-warning@-2 {{FALSE}}
+}
+} // namespace nonfinal_bifurcate

From 2841f4209f1d8ecb9a2f909198b23e169a9dc4e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]>
Date: Wed, 16 Sep 2026 18:18:21 +0200
Subject: [PATCH 04/10] Also cover final methods of the dynamic type

---
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp    | 5 +++++
 clang/test/Analysis/inlining-final-methods.cpp | 3 ---
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp 
b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 1ab1852507e8b..6598750c27ebf 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -826,6 +826,11 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() 
const {
     return {};
   }
 
+  // A final method or a method of a final class cannot be overriden in a
+  // subclass.
+  if (Result->hasAttr<FinalAttr>() || 
Result->getParent()->hasAttr<FinalAttr>())
+    CanBeSubClass = false;
+
   // Does the decl that we found have an implementation?
   const FunctionDecl *Definition;
   if (!Result->hasBody(Definition)) {
diff --git a/clang/test/Analysis/inlining-final-methods.cpp 
b/clang/test/Analysis/inlining-final-methods.cpp
index 689cffed97f5c..f8cf2efdf403a 100644
--- a/clang/test/Analysis/inlining-final-methods.cpp
+++ b/clang/test/Analysis/inlining-final-methods.cpp
@@ -82,8 +82,6 @@ void entrypoint(Child *p) {
 namespace final_method_on_base_ptr_with_known_dyn_type {
 // A final method should also be inlined when it is called through a pointer
 // whose dynamic type is a child of the class where it was defined.
-// FIXME: This is not yet implemented, 'final' is only checked on the method
-// and class declaration corresponding to the static type of the pointee.
 struct Base : Msg {};
 
 struct Ctrl : Base {
@@ -94,7 +92,6 @@ struct Ctrl : Base {
 void test(Base* p) {
   clang_analyzer_dump(p->cmd());
   // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Element{SymRegion{reg_${{[0-9]+}}<Ctrl * p>},0 S64b,struct 
{{[0-9A-Za-z_]+}}::Ctrl}.c>}}
-  // expected-warning@-2 {{conj_$}}
   clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
   clang_analyzer_eval(p->cmd() == p->cmd());
   // FIXME: For unclear reasons, this clang_analyzer_eval call is not reached.

From b729d8fb5a60a2b7e1690d00aa56023521548033 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]>
Date: Wed, 16 Sep 2026 20:36:24 +0200
Subject: [PATCH 05/10] Also cover inherited nonfinal method on an object with
 a final type

---
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp   |  7 ++-
 .../test/Analysis/inlining-final-methods.cpp  | 43 ++++++++++++++++++-
 2 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp 
b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 6598750c27ebf..58e0ce636da53 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -806,6 +806,10 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() 
const {
   if (!RD || !RD->hasDefinition())
     return {};
 
+  // We can confidently inline a method called on an object with final type.
+  if (RD->hasAttr<FinalAttr>())
+    CanBeSubClass = false;
+
   // Find the decl for this method in that class.
   const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD, true);
   if (!Result) {
@@ -826,8 +830,7 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() 
const {
     return {};
   }
 
-  // A final method or a method of a final class cannot be overriden in a
-  // subclass.
+  // A final method cannot be overriden in a subclass.
   if (Result->hasAttr<FinalAttr>() || 
Result->getParent()->hasAttr<FinalAttr>())
     CanBeSubClass = false;
 
diff --git a/clang/test/Analysis/inlining-final-methods.cpp 
b/clang/test/Analysis/inlining-final-methods.cpp
index f8cf2efdf403a..abd916fb79454 100644
--- a/clang/test/Analysis/inlining-final-methods.cpp
+++ b/clang/test/Analysis/inlining-final-methods.cpp
@@ -56,6 +56,24 @@ void test(Child* p) {
 }
 } // namespace final_method_on_child_ptr
 
+namespace nonfinal_method_on_final_child_ptr {
+// We can confidently inline even a non-final method of a non-final class if it
+// is called on an object whose type is final and does not override it.
+struct Ctrl : Msg {
+  unsigned c;
+  unsigned cmd() const override { return c; }
+};
+
+struct Child final : Ctrl {};
+
+void test(Child* p) {
+  clang_analyzer_dump(p->cmd());
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Base{SymRegion{reg_${{[0-9]+}}<Child * p>},Ctrl}.c>}}
+  clang_analyzer_eval(p->cmd() == p->cmd()); // expected-warning {{TRUE}}
+}
+} // namespace nonfinal_method_on_final_child_ptr
+
+
 namespace final_method_on_ptr_with_dyn_type_child {
 // A final method should also be inlined when it is called through a pointer
 // whose dynamic type is a child of the class where it was defined.
@@ -102,6 +120,29 @@ void entrypoint(Ctrl *p) {
 }
 } // namespace final_method_on_base_ptr_with_known_dyn_type
 
+namespace nonfinal_method_on_ptr_with_dyn_type_final {
+// We can confidently inline even a non-final method of a non-final class if it
+// is called on an object whose dynamic type is final and does not override it.
+struct Ctrl : Msg {
+  unsigned c;
+  unsigned cmd() const override { return c; }
+};
+
+struct Child final : Ctrl {};
+
+void test(Ctrl* p) {
+  clang_analyzer_dump(p->cmd());
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Base{SymRegion{reg_${{[0-9]+}}<Child * p>},Ctrl}.c>}}
+  clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  clang_analyzer_eval(p->cmd() == p->cmd());
+  // FIXME: For unclear reasons, this clang_analyzer_eval call is not reached.
+}
+
+void entrypoint(Child *p) {
+  test(p);
+}
+} // namespace nonfinal_method_on_ptr_with_dyn_type_final
+
 namespace nonfinal_bifurcates {
 // When the method is non-final and the dynamic type is unclear, the analysis
 // should bifurcate, with one branch inlining the method and the other branch
@@ -120,4 +161,4 @@ void test(Ctrl* p) {
   // expected-warning@-1 {{TRUE}}
   // expected-warning@-2 {{FALSE}}
 }
-} // namespace nonfinal_bifurcate
+} // namespace nonfinal_bifurcates

From e7bb1015eb0315edce0d1153a089e4bee784f942 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]>
Date: Thu, 17 Sep 2026 15:03:55 +0200
Subject: [PATCH 06/10] Workaround for clang_analyzer_eval quirk

Apparently it does not emit anything when it is encountered in an
inlined function.
---
 .../test/Analysis/inlining-final-methods.cpp  | 37 ++++++-------------
 1 file changed, 12 insertions(+), 25 deletions(-)

diff --git a/clang/test/Analysis/inlining-final-methods.cpp 
b/clang/test/Analysis/inlining-final-methods.cpp
index abd916fb79454..3f86700558ed9 100644
--- a/clang/test/Analysis/inlining-final-methods.cpp
+++ b/clang/test/Analysis/inlining-final-methods.cpp
@@ -2,7 +2,6 @@
 
 void clang_analyzer_dump(unsigned);
 void clang_analyzer_eval(bool);
-void clang_analyzer_warnIfReached();
 
 struct Msg {
   virtual unsigned cmd() const = 0;
@@ -84,16 +83,12 @@ struct Ctrl : Msg {
 
 struct Child : Ctrl {};
 
-void test(Ctrl* p) {
+void test(Child* childp) {
+  Ctrl *p = childp;
   clang_analyzer_dump(p->cmd());
-  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Base{SymRegion{reg_${{[0-9]+}}<Child * p>},Ctrl}.c>}}
-  clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Base{SymRegion{reg_${{[0-9]+}}<Child * childp>},Ctrl}.c>}}
   clang_analyzer_eval(p->cmd() == p->cmd());
-  // FIXME: For unclear reasons, this clang_analyzer_eval call is not reached.
-}
-
-void entrypoint(Child *p) {
-  test(p);
+  // expected-warning@-1 {{TRUE}}
 }
 } // namespace final_method_on_ptr_with_dyn_type_child
 
@@ -107,16 +102,12 @@ struct Ctrl : Base {
   unsigned cmd() const final { return c; }
 };
 
-void test(Base* p) {
+void test(Ctrl* ctrlp) {
+  Base *p = ctrlp;
   clang_analyzer_dump(p->cmd());
-  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Element{SymRegion{reg_${{[0-9]+}}<Ctrl * p>},0 S64b,struct 
{{[0-9A-Za-z_]+}}::Ctrl}.c>}}
-  clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Element{SymRegion{reg_${{[0-9]+}}<Ctrl * ctrlp>},0 S64b,struct 
{{[0-9A-Za-z_]+}}::Ctrl}.c>}}
   clang_analyzer_eval(p->cmd() == p->cmd());
-  // FIXME: For unclear reasons, this clang_analyzer_eval call is not reached.
-}
-
-void entrypoint(Ctrl *p) {
-  test(p);
+  // expected-warning@-1 {{TRUE}}
 }
 } // namespace final_method_on_base_ptr_with_known_dyn_type
 
@@ -130,16 +121,12 @@ struct Ctrl : Msg {
 
 struct Child final : Ctrl {};
 
-void test(Ctrl* p) {
+void test(Child* childp) {
+  Ctrl *p = childp;
   clang_analyzer_dump(p->cmd());
-  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Base{SymRegion{reg_${{[0-9]+}}<Child * p>},Ctrl}.c>}}
-  clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Base{SymRegion{reg_${{[0-9]+}}<Child * childp>},Ctrl}.c>}}
   clang_analyzer_eval(p->cmd() == p->cmd());
-  // FIXME: For unclear reasons, this clang_analyzer_eval call is not reached.
-}
-
-void entrypoint(Child *p) {
-  test(p);
+  // expected-warning@-1 {{TRUE}}
 }
 } // namespace nonfinal_method_on_ptr_with_dyn_type_final
 

From db70b1b4189560f01c9af886b83acc4ccf69a179 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]>
Date: Thu, 17 Sep 2026 15:05:06 +0200
Subject: [PATCH 07/10] Fix an end namespace comment in a test

---
 clang/test/Analysis/inlining-final-methods.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Analysis/inlining-final-methods.cpp 
b/clang/test/Analysis/inlining-final-methods.cpp
index 3f86700558ed9..0a8e1653d64c8 100644
--- a/clang/test/Analysis/inlining-final-methods.cpp
+++ b/clang/test/Analysis/inlining-final-methods.cpp
@@ -36,7 +36,7 @@ void test(Ctrl* p)
   // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Element{SymRegion{reg_${{[0-9]+}}<Ctrl * p>},0 S64b,struct 
{{[0-9A-Za-z_]+}}::Ctrl}.c>}}
   clang_analyzer_eval(p->cmd() == p->cmd()); // expected-warning {{TRUE}}
 }
-} // namespace final_class
+} // namespace final_struct
 
 namespace final_method_on_child_ptr {
 // A final method should also be inlined when it is called through a pointer

From d1ccdb1fdb739582149f38b01dd9784901e71cca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]>
Date: Fri, 18 Sep 2026 18:51:14 +0200
Subject: [PATCH 08/10] Remove a redundant check

This `Result->getParent()->hasAttr<FinalAttr>()` check could be
satisfied only in the case when `RD->hasAttr<FinalAttr>()` was also
true (because if `Result->getParent()` differs from `RD`, it is
necessarily a superclass of `RD` and cannot be `final`).
---
 clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp 
b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 58e0ce636da53..d0b53a24e61ad 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -831,7 +831,7 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() 
const {
   }
 
   // A final method cannot be overriden in a subclass.
-  if (Result->hasAttr<FinalAttr>() || 
Result->getParent()->hasAttr<FinalAttr>())
+  if (Result->hasAttr<FinalAttr>())
     CanBeSubClass = false;
 
   // Does the decl that we found have an implementation?

From 20e7441fbcf36f71f61d9c411dcb4a3b850011fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]>
Date: Sat, 19 Sep 2026 06:16:31 +0200
Subject: [PATCH 09/10] Fix comment copypaste issue in test file

---
 clang/test/Analysis/inlining-final-methods.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Analysis/inlining-final-methods.cpp 
b/clang/test/Analysis/inlining-final-methods.cpp
index 0a8e1653d64c8..405a959d6db8b 100644
--- a/clang/test/Analysis/inlining-final-methods.cpp
+++ b/clang/test/Analysis/inlining-final-methods.cpp
@@ -94,7 +94,7 @@ void test(Child* childp) {
 
 namespace final_method_on_base_ptr_with_known_dyn_type {
 // A final method should also be inlined when it is called through a pointer
-// whose dynamic type is a child of the class where it was defined.
+// base pointer with a known dynamic type.
 struct Base : Msg {};
 
 struct Ctrl : Base {

From ce5e3b55040aba48dac4a78c453dadd538949884 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Don=C3=A1t=20Nagy?= <[email protected]>
Date: Sat, 19 Sep 2026 06:19:08 +0200
Subject: [PATCH 10/10] Move the baseline test forward

---
 .../test/Analysis/inlining-final-methods.cpp  | 40 +++++++++----------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/clang/test/Analysis/inlining-final-methods.cpp 
b/clang/test/Analysis/inlining-final-methods.cpp
index 405a959d6db8b..53ded4990a9bf 100644
--- a/clang/test/Analysis/inlining-final-methods.cpp
+++ b/clang/test/Analysis/inlining-final-methods.cpp
@@ -7,6 +7,26 @@ struct Msg {
   virtual unsigned cmd() const = 0;
 };
 
+namespace nonfinal_bifurcates {
+// When the method is non-final and the dynamic type is unclear, the analysis
+// should bifurcate, with one branch inlining the method and the other branch
+// doing a conservative evaluation (which represents that another overriding
+// method is called). (This is the baseline which is disabled in some cases.)
+struct Ctrl : Msg {
+  unsigned c;
+  unsigned cmd() const override { return c; }
+};
+
+void test(Ctrl* p) {
+  clang_analyzer_dump(p->cmd());
+  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Element{SymRegion{reg_${{[0-9]+}}<Ctrl * p>},0 S64b,struct 
{{[0-9A-Za-z_]+}}::Ctrl}.c>}}
+  // expected-warning@-2 {{conj_$}}
+  clang_analyzer_eval(p->cmd() == p->cmd());
+  // expected-warning@-1 {{TRUE}}
+  // expected-warning@-2 {{FALSE}}
+}
+} // namespace nonfinal_bifurcates
+
 namespace gh222960 {
 // Ctrl::cmd() is final, the analyzer should not split off a "maybe dynamic
 // dispatch invokes a different overriding method" execution path, and only
@@ -129,23 +149,3 @@ void test(Child* childp) {
   // expected-warning@-1 {{TRUE}}
 }
 } // namespace nonfinal_method_on_ptr_with_dyn_type_final
-
-namespace nonfinal_bifurcates {
-// When the method is non-final and the dynamic type is unclear, the analysis
-// should bifurcate, with one branch inlining the method and the other branch
-// doing a conservative evaluation (which represents that another overriding
-// method is called).
-struct Ctrl : Msg {
-  unsigned c;
-  unsigned cmd() const override { return c; }
-};
-
-void test(Ctrl* p) {
-  clang_analyzer_dump(p->cmd());
-  // expected-warning-re@-1 {{reg_${{[0-9]+}}<unsigned int 
Element{SymRegion{reg_${{[0-9]+}}<Ctrl * p>},0 S64b,struct 
{{[0-9A-Za-z_]+}}::Ctrl}.c>}}
-  // expected-warning@-2 {{conj_$}}
-  clang_analyzer_eval(p->cmd() == p->cmd());
-  // expected-warning@-1 {{TRUE}}
-  // expected-warning@-2 {{FALSE}}
-}
-} // namespace nonfinal_bifurcates

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

Reply via email to