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
