mirror of
https://github.com/github/codeql.git
synced 2025-12-20 10:46:30 +01:00
230 lines
4.8 KiB
C++
230 lines
4.8 KiB
C++
struct Obj {
|
|
Obj& operator=(const Obj& other) {
|
|
this->val = other.val;
|
|
return *this; // GOOD (common case)
|
|
}
|
|
|
|
private:
|
|
int val;
|
|
};
|
|
|
|
class Container {
|
|
/* NB: Has generated operator= */
|
|
Obj m_x;
|
|
Obj m_y;
|
|
};
|
|
|
|
struct Bad1 {
|
|
Bad1& operator=(const Bad1& other) {
|
|
return const_cast<Bad1&>(other); // BAD (does not return a reference to *this)
|
|
}
|
|
};
|
|
|
|
struct Bad2 {
|
|
Bad2 operator=(const Bad2& other) {
|
|
return *this; // BAD (return type is not a reference)
|
|
}
|
|
};
|
|
|
|
struct External {
|
|
External& operator=(const External& other); // GOOD (assume correct)
|
|
};
|
|
|
|
class ReturnAssignment {
|
|
public:
|
|
ReturnAssignment(int _val) : val(_val) {}
|
|
|
|
ReturnAssignment &operator=(const ReturnAssignment &other) {
|
|
this->val = other.val;
|
|
return *this; // GOOD
|
|
}
|
|
|
|
ReturnAssignment &operator=(int _val) {
|
|
return *this = ReturnAssignment(_val); // GOOD (calls above `operator=`)
|
|
}
|
|
|
|
int val;
|
|
};
|
|
|
|
template<class T>
|
|
class TemplateReturnAssignment {
|
|
public:
|
|
TemplateReturnAssignment(T _val) : val(_val) {}
|
|
|
|
TemplateReturnAssignment &operator=(const TemplateReturnAssignment &other) {
|
|
this->val = other.val;
|
|
return *this; // GOOD
|
|
}
|
|
|
|
TemplateReturnAssignment &operator=(T _val) {
|
|
return *this = TemplateReturnAssignment(_val); // GOOD (calls above `operator=`)
|
|
}
|
|
|
|
TemplateReturnAssignment &operator=(bool b) {
|
|
return *(new TemplateReturnAssignment(0)); // BAD (does not return a reference to *this)
|
|
}
|
|
|
|
T val;
|
|
};
|
|
|
|
class ReturnBuiltInAssign {
|
|
public:
|
|
ReturnBuiltInAssign(int _val) : val(_val) {}
|
|
|
|
ReturnBuiltInAssign &operator=(int _val) {
|
|
return *this = ReturnBuiltInAssign(_val); // GOOD (uses built-in `AssignExpr`)
|
|
}
|
|
|
|
int val;
|
|
};
|
|
|
|
class Obj2 {
|
|
public:
|
|
Obj2 &getThis() {
|
|
return *this;
|
|
}
|
|
|
|
Obj2 *getThisPtr() {
|
|
return this;
|
|
}
|
|
|
|
Obj2 &operator=(int _val) {
|
|
val = _val;
|
|
return getThis(); // GOOD (returns *this)
|
|
}
|
|
|
|
Obj2 &operator=(int *_val) {
|
|
val = *_val;
|
|
return this->getThis(); // GOOD (returns *this)
|
|
}
|
|
|
|
Obj2 &operator=(int **_val) {
|
|
val = **_val;
|
|
return (getThis()); // GOOD (returns *this)
|
|
}
|
|
|
|
Obj2 &operator=(int ***_val) {
|
|
val = ***_val;
|
|
return *(this->getThisPtr()); // GOOD (returns *this)
|
|
}
|
|
|
|
private:
|
|
int val;
|
|
};
|
|
|
|
struct Exception {
|
|
virtual ~Exception();
|
|
};
|
|
|
|
class AlwaysThrows {
|
|
public:
|
|
AlwaysThrows &operator=(int _val) { // GOOD (always throws)
|
|
throw Exception();
|
|
// No `return` statement is generated by the C++ front end because it can
|
|
// statically see that the end of the function is unreachable.
|
|
}
|
|
|
|
AlwaysThrows &operator=(int *_val) { // GOOD (always throws)
|
|
int one = 1;
|
|
if (one)
|
|
throw Exception();
|
|
// A `return` statement is generated by the C++ front end, but the
|
|
// control-flow pruning in QL will establish that this is unreachable.
|
|
}
|
|
};
|
|
|
|
class Reachability {
|
|
Reachability &operator=(Reachability &that) { // GOOD
|
|
int one = 1;
|
|
if (one)
|
|
return *this;
|
|
else
|
|
return that; // unreachable
|
|
}
|
|
|
|
// helper function that always returns a reference to `*this`.
|
|
Reachability &returnThisReference() {
|
|
int one = 1;
|
|
if (one)
|
|
return *this;
|
|
else
|
|
return staticInstance; // unreachable
|
|
}
|
|
|
|
// helper function that always returns `this`.
|
|
Reachability *const returnThisPointer() {
|
|
int one = 1;
|
|
if (one)
|
|
return this;
|
|
else
|
|
return &staticInstance; // unreachable
|
|
}
|
|
|
|
Reachability &operator=(int _val) { // GOOD
|
|
return returnThisReference();
|
|
}
|
|
|
|
Reachability &operator=(short _val) { // GOOD
|
|
return *returnThisPointer();
|
|
}
|
|
|
|
static Reachability staticInstance;
|
|
};
|
|
|
|
class Forgivable {
|
|
// GOOD: wrong return type but that doesn't matter on a deleted function.
|
|
Forgivable operator=(const Forgivable &_val) = delete;
|
|
|
|
private:
|
|
// GOOD: wrong return type but that doesn't matter because this operator is
|
|
// private and probably also unimplemented, so no code can call it.
|
|
Forgivable operator=(int *_val);
|
|
};
|
|
|
|
// This template has structure similar to `std::enable_if`.
|
|
template<typename S, typename T>
|
|
struct second {
|
|
typedef T type;
|
|
};
|
|
|
|
struct TemplatedAssignmentGood {
|
|
template<typename T>
|
|
typename second<T, TemplatedAssignmentGood &>::type operator=(T val) { // GOOD
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
struct TemplatedAssignmentBad {
|
|
template<typename T>
|
|
typename second<T, TemplatedAssignmentBad>::type operator=(T val) { // BAD (missing &)
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
template<class T>
|
|
class Obj3 {
|
|
public:
|
|
Obj3<T> &subFunc(const Obj3<T> &other) {
|
|
return *this;
|
|
}
|
|
|
|
Obj3<T> &operator=(const Obj3<T> &other) {
|
|
return subFunc(other); // GOOD (returns *this)
|
|
}
|
|
};
|
|
|
|
int main() {
|
|
Container c;
|
|
c = c;
|
|
TemplateReturnAssignment<int> tra(1);
|
|
tra = 2;
|
|
tra = true;
|
|
TemplatedAssignmentGood taGood;
|
|
taGood = 3;
|
|
TemplatedAssignmentBad taBad;
|
|
taBad = 4;
|
|
Obj3<int> obj3_a, obj3_b;
|
|
obj3_a = obj3_b;
|
|
return 0;
|
|
}
|