blob: 53ded4990a9bfeef44555fec9bd8ca9fadfa7008 [file] [edit]
// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -verify %s
void clang_analyzer_dump(unsigned);
void clang_analyzer_eval(bool);
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
// 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_struct
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(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 * childp>},Ctrl}.c>}}
clang_analyzer_eval(p->cmd() == p->cmd());
// expected-warning@-1 {{TRUE}}
}
} // 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
// base pointer with a known dynamic type.
struct Base : Msg {};
struct Ctrl : Base {
unsigned c;
unsigned cmd() const final { return c; }
};
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 * ctrlp>},0 S64b,struct {{[0-9A-Za-z_]+}}::Ctrl}.c>}}
clang_analyzer_eval(p->cmd() == p->cmd());
// expected-warning@-1 {{TRUE}}
}
} // 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(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 * childp>},Ctrl}.c>}}
clang_analyzer_eval(p->cmd() == p->cmd());
// expected-warning@-1 {{TRUE}}
}
} // namespace nonfinal_method_on_ptr_with_dyn_type_final