Skip to content

[clang-tidy] Make bugprone-unhandled-self-assignment check more general #147066

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,23 @@ void UnhandledSelfAssignmentCheck::registerMatchers(MatchFinder *Finder) {
const auto HasNoNestedSelfAssign =
cxxMethodDecl(unless(hasDescendant(cxxMemberCallExpr(callee(cxxMethodDecl(
hasName("operator="), ofClass(equalsBoundNode("class"))))))));


// Checking that some kind of constructor is called and followed by a `swap`:
// T& operator=(const T& other) {
// T tmp{this->internal_data(), some, other, args};
// swap(tmp);
// return *this;
// }
const auto HasCopyAndSwap = cxxMethodDecl(
ofClass(cxxRecordDecl(unless(hasAncestor(classTemplateDecl())))),
hasDescendant(
stmt(hasDescendant(
varDecl(hasType(cxxRecordDecl(equalsBoundNode("class"))))
.bind("tmp_var")),
hasDescendant(callExpr(callee(functionDecl(hasName("swap"))),
hasAnyArgument(declRefExpr(to(varDecl(
equalsBoundNode("tmp_var"))))))))));

DeclarationMatcher AdditionalMatcher = cxxMethodDecl();
if (WarnOnlyIfThisHasSuspiciousField) {
// Matcher for standard smart pointers.
Expand All @@ -94,6 +110,7 @@ void UnhandledSelfAssignmentCheck::registerMatchers(MatchFinder *Finder) {
HasReferenceParam, HasNoSelfCheck,
unless(HasNonTemplateSelfCopy),
unless(HasTemplateSelfCopy),
unless(HasCopyAndSwap),
HasNoNestedSelfAssign, AdditionalMatcher)
.bind("copyAssignmentOperator"),
this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ template <class T>
class auto_ptr {
};

namespace pmr {
template <typename TYPE = void>
class allocator {};
}

struct allocator_arg_t {} allocator_arg;

} // namespace std

void assert(int expression){};
Expand Down Expand Up @@ -540,6 +547,89 @@ class NotACopyAssignmentOperator {
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.

Expand Down
Loading