Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,79 +13,92 @@
using namespace clang::ast_matchers;

namespace clang::tidy::cppcoreguidelines {
namespace {

AST_MATCHER(FieldDecl, isMemberOfLambda) {
return Node.getParent()->isLambda();
static bool hasCopyConstructor(CXXRecordDecl const &Node) {
if (Node.needsOverloadResolutionForCopyConstructor() &&
Node.needsImplicitCopyConstructor()) {
// unresolved
for (CXXBaseSpecifier const &BS : Node.bases()) {
CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl();
if (BRD != nullptr)
if (!hasCopyConstructor(*BRD))
return false;
}
}
if (Node.hasSimpleCopyConstructor())
return true;
for (CXXConstructorDecl const *Ctor : Node.ctors())
if (Ctor->isCopyConstructor())
return !Ctor->isDeleted();
return false;
}

struct MemberFunctionInfo {
bool Declared{};
bool Deleted{};
};

struct MemberFunctionPairInfo {
MemberFunctionInfo Copy{};
MemberFunctionInfo Move{};
};

MemberFunctionPairInfo getConstructorsInfo(CXXRecordDecl const &Node) {
MemberFunctionPairInfo Constructors{};

for (CXXConstructorDecl const *Ctor : Node.ctors()) {
if (Ctor->isCopyConstructor()) {
Constructors.Copy.Declared = true;
if (Ctor->isDeleted())
Constructors.Copy.Deleted = true;
}
if (Ctor->isMoveConstructor()) {
Constructors.Move.Declared = true;
if (Ctor->isDeleted())
Constructors.Move.Deleted = true;
static bool hasMoveConstructor(CXXRecordDecl const &Node) {
if (Node.needsOverloadResolutionForMoveConstructor() &&
Node.needsImplicitMoveConstructor()) {
// unresolved
for (CXXBaseSpecifier const &BS : Node.bases()) {
CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl();
if (BRD != nullptr)
if (!hasMoveConstructor(*BRD))
return false;
}
}

return Constructors;
if (Node.hasSimpleMoveConstructor())
return true;
for (CXXConstructorDecl const *Ctor : Node.ctors())
if (Ctor->isMoveConstructor())
return !Ctor->isDeleted();
return false;
}

MemberFunctionPairInfo getAssignmentsInfo(CXXRecordDecl const &Node) {
MemberFunctionPairInfo Assignments{};

for (CXXMethodDecl const *Method : Node.methods()) {
if (Method->isCopyAssignmentOperator()) {
Assignments.Copy.Declared = true;
if (Method->isDeleted())
Assignments.Copy.Deleted = true;
static bool hasCopyAssignment(CXXRecordDecl const &Node) {
if (Node.needsOverloadResolutionForCopyAssignment() &&
Node.needsImplicitCopyAssignment()) {
// unresolved
for (CXXBaseSpecifier const &BS : Node.bases()) {
CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl();
if (BRD != nullptr)
if (!hasCopyAssignment(*BRD))
return false;
}
}
if (Node.hasSimpleCopyAssignment())
return true;
for (CXXMethodDecl const *Method : Node.methods())
if (Method->isCopyAssignmentOperator())
return !Method->isDeleted();
return false;
}

if (Method->isMoveAssignmentOperator()) {
Assignments.Move.Declared = true;
if (Method->isDeleted())
Assignments.Move.Deleted = true;
static bool hasMoveAssignment(CXXRecordDecl const &Node) {
if (Node.needsOverloadResolutionForMoveAssignment() &&
Node.needsImplicitMoveAssignment()) {
// unresolved
for (CXXBaseSpecifier const &BS : Node.bases()) {
CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl();
if (BRD != nullptr)
if (!hasMoveAssignment(*BRD))
return false;
}
}

return Assignments;
if (Node.hasSimpleMoveAssignment())
return true;
for (CXXMethodDecl const *Method : Node.methods())
if (Method->isMoveAssignmentOperator())
return !Method->isDeleted();
return false;
}

AST_MATCHER(CXXRecordDecl, isCopyableOrMovable) {
MemberFunctionPairInfo Constructors = getConstructorsInfo(Node);
MemberFunctionPairInfo Assignments = getAssignmentsInfo(Node);
namespace {

if (Node.hasSimpleCopyConstructor() ||
(Constructors.Copy.Declared && !Constructors.Copy.Deleted))
return true;
if (Node.hasSimpleMoveConstructor() ||
(Constructors.Move.Declared && !Constructors.Move.Deleted))
return true;
if (Node.hasSimpleCopyAssignment() ||
(Assignments.Copy.Declared && !Assignments.Copy.Deleted))
return true;
if (Node.hasSimpleMoveAssignment() ||
(Assignments.Move.Declared && !Assignments.Move.Deleted))
return true;
AST_MATCHER(FieldDecl, isMemberOfLambda) {
return Node.getParent()->isLambda();
}

return false;
AST_MATCHER(CXXRecordDecl, isCopyableOrMovable) {
return hasCopyConstructor(Node) || hasMoveConstructor(Node) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, a class can "have" a copy constructor, but it can be deleted, so it doesn't help here.

I think it would be more accurate to say return isCopyConstructible(Node) || ...

hasCopyAssignment(Node) || hasMoveAssignment(Node);
}

} // namespace
Expand Down
4 changes: 4 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ Changes in existing checks
fix false positive that floating point variable is only used in increment
expression.

- Improved :doc:`cppcoreguidelines-avoid-const-or-ref-data-members
<clang-tidy/checks/cppcoreguidelines/avoid-const-or-ref-data-members>` check to
avoid false positive when detecting templated class with inheritance.

- Improved :doc:`cppcoreguidelines-init-variables
<clang-tidy/checks/cppcoreguidelines/init-variables>` check by fixing the
insertion location for function pointers.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,28 @@ struct InheritBothFromNonCopyableAndNonMovable : NonCopyable, NonMovable
int& x; // OK, non copyable nor movable
};

template<class T> struct TemplateInheritFromNonCopyable : NonCopyable
{
int& x;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: member 'x' of type 'int &' is a reference
};

template<class T> struct TemplateInheritFromNonMovable : NonMovable
{
int& x;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: member 'x' of type 'int &' is a reference
};

template<class T> struct TemplateInheritFromNonCopyableNonMovable : NonCopyableNonMovable
{
int& x; // OK, non copyable nor movable
};

template<class T> struct TemplateInheritBothFromNonCopyableAndNonMovable : NonCopyable, NonMovable
{
int& x; // OK, non copyable nor movable
};

// Test composition
struct ContainsNonCopyable
{
Expand Down
Loading