Skip to content

Commit 428283c

Browse files
committed
[clang-tidy] fix false positive when detecting templated classes with inheritance
`hasSimpleCopyConstructor` series of functions are not reliable when these functions are not resolved. We need to manually resolve the status of these functions from its base classes. Fixes: #111985.
1 parent 5d39e0c commit 428283c

File tree

3 files changed

+93
-55
lines changed

3 files changed

+93
-55
lines changed

clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidConstOrRefDataMembersCheck.cpp

Lines changed: 67 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -19,75 +19,87 @@ AST_MATCHER(FieldDecl, isMemberOfLambda) {
1919
return Node.getParent()->isLambda();
2020
}
2121

22-
struct MemberFunctionInfo {
23-
bool Declared{};
24-
bool Deleted{};
25-
};
26-
27-
struct MemberFunctionPairInfo {
28-
MemberFunctionInfo Copy{};
29-
MemberFunctionInfo Move{};
30-
};
31-
32-
MemberFunctionPairInfo getConstructorsInfo(CXXRecordDecl const &Node) {
33-
MemberFunctionPairInfo Constructors{};
34-
35-
for (CXXConstructorDecl const *Ctor : Node.ctors()) {
36-
if (Ctor->isCopyConstructor()) {
37-
Constructors.Copy.Declared = true;
38-
if (Ctor->isDeleted())
39-
Constructors.Copy.Deleted = true;
40-
}
41-
if (Ctor->isMoveConstructor()) {
42-
Constructors.Move.Declared = true;
43-
if (Ctor->isDeleted())
44-
Constructors.Move.Deleted = true;
22+
bool hasCopyConstructor(CXXRecordDecl const &Node) {
23+
if (Node.needsOverloadResolutionForCopyConstructor() &&
24+
Node.needsImplicitCopyConstructor()) {
25+
// unresolved
26+
for (CXXBaseSpecifier const &BS : Node.bases()) {
27+
CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl();
28+
if (BRD != nullptr)
29+
if (!hasCopyConstructor(*BRD))
30+
return false;
4531
}
4632
}
47-
48-
return Constructors;
33+
if (Node.hasSimpleCopyConstructor())
34+
return true;
35+
for (CXXConstructorDecl const *Ctor : Node.ctors())
36+
if (Ctor->isCopyConstructor())
37+
return !Ctor->isDeleted();
38+
return false;
4939
}
5040

51-
MemberFunctionPairInfo getAssignmentsInfo(CXXRecordDecl const &Node) {
52-
MemberFunctionPairInfo Assignments{};
53-
54-
for (CXXMethodDecl const *Method : Node.methods()) {
55-
if (Method->isCopyAssignmentOperator()) {
56-
Assignments.Copy.Declared = true;
57-
if (Method->isDeleted())
58-
Assignments.Copy.Deleted = true;
41+
bool hasMoveConstructor(CXXRecordDecl const &Node) {
42+
if (Node.needsOverloadResolutionForMoveConstructor() &&
43+
Node.needsImplicitMoveConstructor()) {
44+
// unresolved
45+
for (CXXBaseSpecifier const &BS : Node.bases()) {
46+
CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl();
47+
if (BRD != nullptr)
48+
if (!hasMoveConstructor(*BRD))
49+
return false;
5950
}
51+
}
52+
if (Node.hasSimpleMoveConstructor())
53+
return true;
54+
for (CXXConstructorDecl const *Ctor : Node.ctors())
55+
if (Ctor->isMoveConstructor())
56+
return !Ctor->isDeleted();
57+
return false;
58+
}
6059

61-
if (Method->isMoveAssignmentOperator()) {
62-
Assignments.Move.Declared = true;
63-
if (Method->isDeleted())
64-
Assignments.Move.Deleted = true;
60+
bool hasCopyAssignment(CXXRecordDecl const &Node) {
61+
if (Node.needsOverloadResolutionForCopyAssignment() &&
62+
Node.needsImplicitCopyAssignment()) {
63+
// unresolved
64+
for (CXXBaseSpecifier const &BS : Node.bases()) {
65+
CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl();
66+
if (BRD != nullptr)
67+
if (!hasCopyAssignment(*BRD))
68+
return false;
6569
}
6670
}
67-
68-
return Assignments;
71+
if (Node.hasSimpleCopyAssignment())
72+
return true;
73+
for (CXXMethodDecl const *Method : Node.methods())
74+
if (Method->isCopyAssignmentOperator())
75+
return !Method->isDeleted();
76+
return false;
6977
}
7078

71-
AST_MATCHER(CXXRecordDecl, isCopyableOrMovable) {
72-
MemberFunctionPairInfo Constructors = getConstructorsInfo(Node);
73-
MemberFunctionPairInfo Assignments = getAssignmentsInfo(Node);
74-
75-
if (Node.hasSimpleCopyConstructor() ||
76-
(Constructors.Copy.Declared && !Constructors.Copy.Deleted))
77-
return true;
78-
if (Node.hasSimpleMoveConstructor() ||
79-
(Constructors.Move.Declared && !Constructors.Move.Deleted))
80-
return true;
81-
if (Node.hasSimpleCopyAssignment() ||
82-
(Assignments.Copy.Declared && !Assignments.Copy.Deleted))
83-
return true;
84-
if (Node.hasSimpleMoveAssignment() ||
85-
(Assignments.Move.Declared && !Assignments.Move.Deleted))
79+
bool hasMoveAssignment(CXXRecordDecl const &Node) {
80+
if (Node.needsOverloadResolutionForMoveAssignment() &&
81+
Node.needsImplicitMoveAssignment()) {
82+
// unresolved
83+
for (CXXBaseSpecifier const &BS : Node.bases()) {
84+
CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl();
85+
if (BRD != nullptr)
86+
if (!hasMoveAssignment(*BRD))
87+
return false;
88+
}
89+
}
90+
if (Node.hasSimpleMoveAssignment())
8691
return true;
87-
92+
for (CXXMethodDecl const *Method : Node.methods())
93+
if (Method->isMoveAssignmentOperator())
94+
return !Method->isDeleted();
8895
return false;
8996
}
9097

98+
AST_MATCHER(CXXRecordDecl, isCopyableOrMovable) {
99+
return hasCopyConstructor(Node) || hasMoveConstructor(Node) ||
100+
hasCopyAssignment(Node) || hasMoveAssignment(Node);
101+
}
102+
91103
} // namespace
92104

93105
void AvoidConstOrRefDataMembersCheck::registerMatchers(MatchFinder *Finder) {

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ Changes in existing checks
190190
fix false positive that floating point variable is only used in increment
191191
expression.
192192

193+
- Improved :doc:`cppcoreguidelines-avoid-const-or-ref-data-members
194+
<clang-tidy/checks/cppcoreguidelines/avoid-const-or-ref-data-members>` check to
195+
avoid false positive when detecting templated class with inheritance.
196+
193197
- Improved :doc:`cppcoreguidelines-prefer-member-initializer
194198
<clang-tidy/checks/cppcoreguidelines/prefer-member-initializer>` check to
195199
avoid false positive when member initialization depends on a structured

clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-const-or-ref-data-members.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,28 @@ struct InheritBothFromNonCopyableAndNonMovable : NonCopyable, NonMovable
285285
int& x; // OK, non copyable nor movable
286286
};
287287

288+
template<class T> struct TemplateInheritFromNonCopyable : NonCopyable
289+
{
290+
int& x;
291+
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: member 'x' of type 'int &' is a reference
292+
};
293+
294+
template<class T> struct TemplateInheritFromNonMovable : NonMovable
295+
{
296+
int& x;
297+
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: member 'x' of type 'int &' is a reference
298+
};
299+
300+
template<class T> struct TemplateInheritFromNonCopyableNonMovable : NonCopyableNonMovable
301+
{
302+
int& x; // OK, non copyable nor movable
303+
};
304+
305+
template<class T> struct TemplateInheritBothFromNonCopyableAndNonMovable : NonCopyable, NonMovable
306+
{
307+
int& x; // OK, non copyable nor movable
308+
};
309+
288310
// Test composition
289311
struct ContainsNonCopyable
290312
{

0 commit comments

Comments
 (0)