================
@@ -0,0 +1,164 @@
+// 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 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.
+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.
+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>}}
+ 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_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.
----------------
steakhal wrote:
According to claude, there is this in the documentation:
Documented in clang/docs/analyzer/developer-docs/DebugChecks.md:84:
> "this functionality is currently DISABLED in inlined functions, since
> different calls to the same in
I think we could massage the test to skip `test` and inline its guts to
`entrypoint` to make these evals work as expected.
https://github.com/llvm/llvm-project/pull/224070
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits