forked from OSchip/llvm-project
580 lines
12 KiB
C++
580 lines
12 KiB
C++
// RUN: %check_clang_tidy %s bugprone-unhandled-self-assignment %t
|
|
|
|
namespace std {
|
|
|
|
template <class T>
|
|
void swap(T x, T y) {
|
|
}
|
|
|
|
template <class T>
|
|
T &&move(T x) {
|
|
}
|
|
|
|
template <class T>
|
|
class unique_ptr {
|
|
};
|
|
|
|
template <class T>
|
|
class shared_ptr {
|
|
};
|
|
|
|
template <class T>
|
|
class weak_ptr {
|
|
};
|
|
|
|
template <class T>
|
|
class auto_ptr {
|
|
};
|
|
|
|
} // namespace std
|
|
|
|
void assert(int expression){};
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
/// Test cases correctly caught by the check.
|
|
|
|
class PtrField {
|
|
public:
|
|
PtrField &operator=(const PtrField &object);
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
PtrField &PtrField::operator=(const PtrField &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:21: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
// Class with an inline operator definition.
|
|
class InlineDefinition {
|
|
public:
|
|
InlineDefinition &operator=(const InlineDefinition &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:21: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
class UniquePtrField {
|
|
public:
|
|
UniquePtrField &operator=(const UniquePtrField &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:19: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<int> p;
|
|
};
|
|
|
|
class SharedPtrField {
|
|
public:
|
|
SharedPtrField &operator=(const SharedPtrField &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:19: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
std::shared_ptr<int> p;
|
|
};
|
|
|
|
class WeakPtrField {
|
|
public:
|
|
WeakPtrField &operator=(const WeakPtrField &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
std::weak_ptr<int> p;
|
|
};
|
|
|
|
class AutoPtrField {
|
|
public:
|
|
AutoPtrField &operator=(const AutoPtrField &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
std::auto_ptr<int> p;
|
|
};
|
|
|
|
// Class with C array field.
|
|
class CArrayField {
|
|
public:
|
|
CArrayField &operator=(const CArrayField &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:16: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int array[256];
|
|
};
|
|
|
|
// Make sure to not ignore cases when the operator definition calls
|
|
// a copy constructor of another class.
|
|
class CopyConstruct {
|
|
public:
|
|
CopyConstruct &operator=(const CopyConstruct &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:18: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
WeakPtrField a;
|
|
WeakPtrField b(a);
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
// Make sure to not ignore cases when the operator definition calls
|
|
// a copy assignment operator of another class.
|
|
class AssignOperator {
|
|
public:
|
|
AssignOperator &operator=(const AssignOperator &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:19: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
a.operator=(object.a);
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
WeakPtrField a;
|
|
};
|
|
|
|
class NotSelfCheck {
|
|
public:
|
|
NotSelfCheck &operator=(const NotSelfCheck &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
if (&object == this->doSomething()) {
|
|
// ...
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void *doSomething() {
|
|
return p;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
template <class T>
|
|
class TemplatePtrField {
|
|
public:
|
|
TemplatePtrField<T> &operator=(const TemplatePtrField<T> &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:24: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
T *p;
|
|
};
|
|
|
|
template <class T>
|
|
class TemplateCArrayField {
|
|
public:
|
|
TemplateCArrayField<T> &operator=(const TemplateCArrayField<T> &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:27: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
T p[256];
|
|
};
|
|
|
|
// Other template class's constructor is called inside a declaration.
|
|
template <class T>
|
|
class WrongTemplateCopyAndMove {
|
|
public:
|
|
WrongTemplateCopyAndMove<T> &operator=(const WrongTemplateCopyAndMove<T> &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:32: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
TemplatePtrField<T> temp;
|
|
TemplatePtrField<T> temp2(temp);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
T *p;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
/// Test cases correctly ignored by the check.
|
|
|
|
// Self-assignment is checked using the equality operator.
|
|
class SelfCheck1 {
|
|
public:
|
|
SelfCheck1 &operator=(const SelfCheck1 &object) {
|
|
if (this == &object)
|
|
return *this;
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
class SelfCheck2 {
|
|
public:
|
|
SelfCheck2 &operator=(const SelfCheck2 &object) {
|
|
if (&object == this)
|
|
return *this;
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
// Self-assignment is checked using the inequality operator.
|
|
class SelfCheck3 {
|
|
public:
|
|
SelfCheck3 &operator=(const SelfCheck3 &object) {
|
|
if (this != &object) {
|
|
// ...
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
class SelfCheck4 {
|
|
public:
|
|
SelfCheck4 &operator=(const SelfCheck4 &object) {
|
|
if (&object != this) {
|
|
// ...
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
template <class T>
|
|
class TemplateSelfCheck {
|
|
public:
|
|
TemplateSelfCheck<T> &operator=(const TemplateSelfCheck<T> &object) {
|
|
if (&object != this) {
|
|
// ...
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
T *p;
|
|
};
|
|
|
|
// There is no warning if the copy assignment operator gets the object by value.
|
|
class PassedByValue {
|
|
public:
|
|
PassedByValue &operator=(PassedByValue object) {
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
// User-defined swap method calling std::swap inside.
|
|
class CopyAndSwap1 {
|
|
public:
|
|
CopyAndSwap1 &operator=(const CopyAndSwap1 &object) {
|
|
CopyAndSwap1 temp(object);
|
|
doSwap(temp);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
|
|
void doSwap(CopyAndSwap1 &object) {
|
|
using std::swap;
|
|
swap(p, object.p);
|
|
}
|
|
};
|
|
|
|
// User-defined swap method used with passed-by-value parameter.
|
|
class CopyAndSwap2 {
|
|
public:
|
|
CopyAndSwap2 &operator=(CopyAndSwap2 object) {
|
|
doSwap(object);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
|
|
void doSwap(CopyAndSwap2 &object) {
|
|
using std::swap;
|
|
swap(p, object.p);
|
|
}
|
|
};
|
|
|
|
// Copy-and-swap method is used but without creating a separate method for it.
|
|
class CopyAndSwap3 {
|
|
public:
|
|
CopyAndSwap3 &operator=(const CopyAndSwap3 &object) {
|
|
CopyAndSwap3 temp(object);
|
|
std::swap(p, temp.p);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
template <class T>
|
|
class TemplateCopyAndSwap {
|
|
public:
|
|
TemplateCopyAndSwap<T> &operator=(const TemplateCopyAndSwap<T> &object) {
|
|
TemplateCopyAndSwap<T> temp(object);
|
|
std::swap(p, temp.p);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
T *p;
|
|
};
|
|
|
|
// Move semantics is used on a temporary copy of the object.
|
|
class CopyAndMove1 {
|
|
public:
|
|
CopyAndMove1 &operator=(const CopyAndMove1 &object) {
|
|
CopyAndMove1 temp(object);
|
|
*this = std::move(temp);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
// There is no local variable for the temporary copy.
|
|
class CopyAndMove2 {
|
|
public:
|
|
CopyAndMove2 &operator=(const CopyAndMove2 &object) {
|
|
*this = std::move(CopyAndMove2(object));
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
template <class T>
|
|
class TemplateCopyAndMove {
|
|
public:
|
|
TemplateCopyAndMove<T> &operator=(const TemplateCopyAndMove<T> &object) {
|
|
TemplateCopyAndMove<T> temp(object);
|
|
*this = std::move(temp);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
T *p;
|
|
};
|
|
|
|
// There is no local variable for the temporary copy.
|
|
template <class T>
|
|
class TemplateCopyAndMove2 {
|
|
public:
|
|
TemplateCopyAndMove2<T> &operator=(const TemplateCopyAndMove2<T> &object) {
|
|
*this = std::move(TemplateCopyAndMove2<T>(object));
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
T *p;
|
|
};
|
|
|
|
// We should not catch move assignment operators.
|
|
class MoveAssignOperator {
|
|
public:
|
|
MoveAssignOperator &operator=(MoveAssignOperator &&object) {
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
// We ignore copy assignment operators without user-defined implementation.
|
|
class DefaultOperator {
|
|
public:
|
|
DefaultOperator &operator=(const DefaultOperator &object) = default;
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
class DeletedOperator {
|
|
public:
|
|
DeletedOperator &operator=(const DefaultOperator &object) = delete;
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
class ImplicitOperator {
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
// Check ignores those classes which has no any pointer or array field.
|
|
class TrivialFields {
|
|
public:
|
|
TrivialFields &operator=(const TrivialFields &object) {
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int m;
|
|
float f;
|
|
double d;
|
|
bool b;
|
|
};
|
|
|
|
// There is no warning when the class calls another assignment operator on 'this'
|
|
// inside the copy assignment operator's definition.
|
|
class AssignIsForwarded {
|
|
public:
|
|
AssignIsForwarded &operator=(const AssignIsForwarded &object) {
|
|
operator=(object.p);
|
|
return *this;
|
|
}
|
|
|
|
AssignIsForwarded &operator=(int *pp) {
|
|
if (p != pp) {
|
|
delete p;
|
|
p = new int(*pp);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
// Assertion is a valid way to say that self-assignment is not expected to happen.
|
|
class AssertGuard {
|
|
public:
|
|
AssertGuard &operator=(const AssertGuard &object) {
|
|
assert(this != &object);
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *p;
|
|
};
|
|
|
|
// Make sure we don't catch this operator=() as a copy assignment operator.
|
|
// Note that RHS has swapped template arguments.
|
|
template <typename Ty, typename Uy>
|
|
class NotACopyAssignmentOperator {
|
|
Ty *Ptr1;
|
|
Uy *Ptr2;
|
|
|
|
public:
|
|
NotACopyAssignmentOperator& operator=(const NotACopyAssignmentOperator<Uy, Ty> &RHS) {
|
|
Ptr1 = RHS.getUy();
|
|
Ptr2 = RHS.getTy();
|
|
return *this;
|
|
}
|
|
|
|
Ty *getTy() const { return Ptr1; }
|
|
Uy *getUy() const { return Ptr2; }
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////
|
|
/// Test cases which should be caught by the check.
|
|
|
|
// TODO: handle custom pointers.
|
|
template <class T>
|
|
class custom_ptr {
|
|
};
|
|
|
|
class CustomPtrField {
|
|
public:
|
|
CustomPtrField &operator=(const CustomPtrField &object) {
|
|
// ...
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
custom_ptr<int> p;
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// False positives: These are self-assignment safe, but they don't use any of the three patterns.
|
|
|
|
class ArrayCopy {
|
|
public:
|
|
ArrayCopy &operator=(const ArrayCopy &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:14: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
for (int i = 0; i < 256; i++)
|
|
array[i] = object.array[i];
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int array[256];
|
|
};
|
|
|
|
class GetterSetter {
|
|
public:
|
|
GetterSetter &operator=(const GetterSetter &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:17: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
setValue(object.getValue());
|
|
return *this;
|
|
}
|
|
|
|
int *getValue() const { return value; }
|
|
|
|
void setValue(int *newPtr) {
|
|
int *pTmp(newPtr ? new int(*newPtr) : nullptr);
|
|
std::swap(value, pTmp);
|
|
delete pTmp;
|
|
}
|
|
|
|
private:
|
|
int *value;
|
|
};
|
|
|
|
class CustomSelfCheck {
|
|
public:
|
|
CustomSelfCheck &operator=(const CustomSelfCheck &object) {
|
|
// CHECK-MESSAGES: [[@LINE-1]]:20: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
|
|
if (index != object.index) {
|
|
// ...
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
int *value;
|
|
int index;
|
|
};
|