Files
TheRock/compiler/amd-llvm/clang-tools-extra/test/clang-tidy/checkers/bugprone/unhandled-self-assignment.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

795 lines
18 KiB
C++

// RUN: %check_clang_tidy %s bugprone-unhandled-self-assignment %t -- -- -fno-delayed-template-parsing
#include <utility>
#include <memory>
namespace std {
namespace pmr {
template <typename TYPE = void>
class allocator {};
}
struct allocator_arg_t {} allocator_arg;
} // 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;
};
// https://bugs.llvm.org/show_bug.cgi?id=44499
class Foo2;
template <int a>
bool operator!=(Foo2 &, Foo2 &) {
class Bar2 {
Bar2 &operator=(const Bar2 &other) {
// CHECK-MESSAGES: [[@LINE-1]]:11: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
p = other.p;
return *this;
}
int *p;
};
}
// Missing call to `swap` function
class AllocatorAwareClassNoSwap {
// pointer member to trigger bugprone-unhandled-self-assignment
void *foo = nullptr;
public:
using allocator_type = std::pmr::allocator<>;
AllocatorAwareClassNoSwap(const AllocatorAwareClassNoSwap& other) {
}
AllocatorAwareClassNoSwap(const AllocatorAwareClassNoSwap& other, const allocator_type& alloc) {
}
AllocatorAwareClassNoSwap& operator=(const AllocatorAwareClassNoSwap& other) {
// CHECK-MESSAGES: [[@LINE-1]]:32: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
AllocatorAwareClassNoSwap tmp(other, get_allocator());
return *this;
}
allocator_type get_allocator() const {
return allocator_type();
}
};
// "Wrong" type is passed to member `swap` function
class AllocatorAwareClassSwapWrongArgType {
// pointer member to trigger bugprone-unhandled-self-assignment
void *foo = nullptr;
public:
using allocator_type = std::pmr::allocator<>;
AllocatorAwareClassSwapWrongArgType(const AllocatorAwareClassSwapWrongArgType& other) {
}
AllocatorAwareClassSwapWrongArgType(const AllocatorAwareClassSwapWrongArgType& other, const allocator_type& alloc) {
}
AllocatorAwareClassSwapWrongArgType& operator=(const AllocatorAwareClassSwapWrongArgType& other) {
// CHECK-MESSAGES: [[@LINE-1]]:42: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
int tmp = 0;
swap(tmp);
return *this;
}
void swap(int&) noexcept {
}
allocator_type get_allocator() const {
return allocator_type();
}
};
// Making ADL `swap` call but with wrong argument type
class AllocatorAwareClassAdlSwapWrongArgType {
// pointer member to trigger bugprone-unhandled-self-assignment
void *foo = nullptr;
public:
using allocator_type = std::pmr::allocator<>;
AllocatorAwareClassAdlSwapWrongArgType(const AllocatorAwareClassAdlSwapWrongArgType& other) {
}
AllocatorAwareClassAdlSwapWrongArgType(const AllocatorAwareClassAdlSwapWrongArgType& other, const allocator_type& alloc) {
}
AllocatorAwareClassAdlSwapWrongArgType& operator=(const AllocatorAwareClassAdlSwapWrongArgType& other) {
// CHECK-MESSAGES: [[@LINE-1]]:45: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
int tmp = 0;
swap(*this, tmp);
return *this;
}
allocator_type get_allocator() const {
return allocator_type();
}
friend void swap(AllocatorAwareClassAdlSwapWrongArgType&, int&) {
}
};
// `this` isn't passed to `swap`
class AllocatorAwareClassAdlSwapWrongArgs {
// pointer member to trigger bugprone-unhandled-self-assignment
void *foo = nullptr;
public:
using allocator_type = std::pmr::allocator<>;
AllocatorAwareClassAdlSwapWrongArgs(const AllocatorAwareClassAdlSwapWrongArgs& other) {
}
AllocatorAwareClassAdlSwapWrongArgs(const AllocatorAwareClassAdlSwapWrongArgs& other, const allocator_type& alloc) {
}
AllocatorAwareClassAdlSwapWrongArgs& operator=(const AllocatorAwareClassAdlSwapWrongArgs& other) {
// CHECK-MESSAGES: [[@LINE-1]]:42: warning: operator=() does not handle self-assignment properly [bugprone-unhandled-self-assignment]
AllocatorAwareClassAdlSwapWrongArgs tmp1(other, get_allocator());
AllocatorAwareClassAdlSwapWrongArgs tmp2(*this, get_allocator());
swap(tmp1, tmp2);
return *this;
}
allocator_type get_allocator() const {
return allocator_type();
}
friend void swap(AllocatorAwareClassAdlSwapWrongArgs&, AllocatorAwareClassAdlSwapWrongArgs&) {
}
};
///////////////////////////////////////////////////////////////////
/// 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;
};
// https://bugs.llvm.org/show_bug.cgi?id=44499
class Foo;
template <int a>
bool operator!=(Foo &, Foo &) {
class Bar {
Bar &operator=(const Bar &other) {
if (this != &other) {
}
return *this;
}
int *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 = 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; }
};
// Support "extended" copy/move constructors
class AllocatorAwareClass {
// pointer member to trigger bugprone-unhandled-self-assignment
void *foo = nullptr;
public:
using allocator_type = std::pmr::allocator<>;
AllocatorAwareClass(const AllocatorAwareClass& other) {
}
AllocatorAwareClass(const AllocatorAwareClass& other, const allocator_type& alloc) {
}
AllocatorAwareClass& operator=(const AllocatorAwareClass& other) {
AllocatorAwareClass tmp(other, get_allocator());
swap(tmp);
return *this;
}
void swap(AllocatorAwareClass& other) noexcept {
}
allocator_type get_allocator() const {
return allocator_type();
}
};
// Support "extended" copy/move constructors + std::swap
class AllocatorAwareClassStdSwap {
// pointer member to trigger bugprone-unhandled-self-assignment
void *foo = nullptr;
public:
using allocator_type = std::pmr::allocator<>;
AllocatorAwareClassStdSwap(const AllocatorAwareClassStdSwap& other) {
}
AllocatorAwareClassStdSwap(const AllocatorAwareClassStdSwap& other, const allocator_type& alloc) {
}
AllocatorAwareClassStdSwap& operator=(const AllocatorAwareClassStdSwap& other) {
using std::swap;
AllocatorAwareClassStdSwap tmp(other, get_allocator());
swap(*this, tmp);
return *this;
}
allocator_type get_allocator() const {
return allocator_type();
}
};
// Support "extended" copy/move constructors + ADL swap
class AllocatorAwareClassAdlSwap {
// pointer member to trigger bugprone-unhandled-self-assignment
void *foo = nullptr;
public:
using allocator_type = std::pmr::allocator<>;
AllocatorAwareClassAdlSwap(const AllocatorAwareClassAdlSwap& other) {
}
AllocatorAwareClassAdlSwap(const AllocatorAwareClassAdlSwap& other, const allocator_type& alloc) {
}
AllocatorAwareClassAdlSwap& operator=(const AllocatorAwareClassAdlSwap& other) {
AllocatorAwareClassAdlSwap tmp(other, get_allocator());
swap(*this, tmp);
return *this;
}
allocator_type get_allocator() const {
return allocator_type();
}
friend void swap(AllocatorAwareClassAdlSwap&, AllocatorAwareClassAdlSwap&) {
}
};
///////////////////////////////////////////////////////////////////
/// 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;
};