Files
codeql/cpp/ql/test/library-tests/dataflow/fields/simple.cpp
2025-11-25 12:36:29 +00:00

171 lines
3.3 KiB
C++

namespace Simple
{
int user_input()
{
return 42;
}
void sink(int x)
{
}
class Foo
{
int a_;
int b_;
public:
int a() { return a_; }
int b() { return b_; }
void setA(int a) { a_ = a; }
void setB(int b) { b_ = b; }
Foo(int a, int b) : a_(a), b_(b){};
};
void bar(Foo &f)
{
sink(f.a()); // $ ast=39:12 ast=41:12 ir=39:12 ir=41:12
sink(f.b()); // $ ast=40:12 ast=42:12 ir=40:12 ir=42:12
}
void foo()
{
Foo f(0, 0);
Foo g(0, 0);
Foo h(0, 0);
Foo i(0, 0);
f.setA(user_input());
g.setB(user_input());
h.setA(user_input());
h.setB(user_input());
// Only a() should alert
bar(f);
// Only b() should alert
bar(g);
// Both a() and b() should alert
bar(h);
// Nothing should alert
bar(i);
}
struct A
{
int i;
};
void single_field_test()
{
A a;
a.i = user_input();
A a2 = a;
sink(a2.i); //$ ast,ir
}
struct C {
int f1;
};
struct C2
{
C f2;
int getf2f1() {
return f2.f1;
}
void m() {
f2.f1 = user_input();
sink(getf2f1()); //$ ast,ir
}
};
typedef A A_typedef;
void single_field_test_typedef(A_typedef a)
{
a.i = user_input();
A_typedef a2 = a;
sink(a2.i); //$ ast,ir
}
namespace TestAdditionalCallTargets {
using TakesIntReturnsVoid = void(*)(int);
template<TakesIntReturnsVoid F>
void call_template_argument(int);
void call_sink(int x) {
sink(x); // $ ir
}
void test_additional_call_targets() {
int x = user_input();
call_template_argument<call_sink>(x);
}
}
void post_update_to_phi_input(bool b)
{
A a;
if(b) {
a.i = user_input();
}
sink(a.i); // $ ast,ir
}
void write_to_param(int* p) {
*p = user_input();
}
void alias_with_fields(bool b) {
A a;
int* q;
if(b) {
q = &a.i;
} else {
q = nullptr;
}
write_to_param(q);
sink(a.i); // $ MISSING: ast,ir
}
template<typename T>
union U_with_two_instantiations_of_different_size {
int x;
T y;
};
struct LargeStruct {
int data[64];
};
void test_union_with_two_instantiations_of_different_sizes() {
// A union's fields is partitioned into "chunks" for field-flow in order to
// improve performance (so that a write to a field of a union does not flow
// to too many reads that don't happen at runtime). The partitioning is based
// the size of the types in the union. So a write to a field of size k only
// flows to a read of size k.
// Since field-flow is based on uninstantiated types a field can have
// multiple sizes if the union is instantiated with types of
// different sizes. So to compute the partition we pick the maximum size.
// Because of this there are `Content`s corresponding to the union
// `U_with_two_instantiations_of_different_size<T>`: The one for size
// `sizeof(int)`, and the one for size `sizeof(LargeStruct)` (because
// `LargeStruct` is larger than `int`). So the write to `x` writes to the
// `Content` for size `sizeof(int)`, and the read of `y` reads from the
// `Content` for size `sizeof(LargeStruct)`.
U_with_two_instantiations_of_different_size<int> u_int;
U_with_two_instantiations_of_different_size<LargeStruct> u_very_large;
u_int.x = user_input();
sink(u_int.y); // $ MISSING: ir
}
} // namespace Simple