1 | //===--- AvoidConstOrRefDataMembersCheck.cpp - clang-tidy -----------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "AvoidConstOrRefDataMembersCheck.h" |
10 | #include "clang/AST/ASTContext.h" |
11 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 | |
13 | using namespace clang::ast_matchers; |
14 | |
15 | namespace clang::tidy::cppcoreguidelines { |
16 | |
17 | static bool isCopyConstructible(CXXRecordDecl const &Node) { |
18 | if (Node.needsOverloadResolutionForCopyConstructor() && |
19 | Node.needsImplicitCopyConstructor()) { |
20 | // unresolved |
21 | for (CXXBaseSpecifier const &BS : Node.bases()) { |
22 | CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); |
23 | if (BRD != nullptr && !isCopyConstructible(Node: *BRD)) |
24 | return false; |
25 | } |
26 | } |
27 | if (Node.hasSimpleCopyConstructor()) |
28 | return true; |
29 | for (CXXConstructorDecl const *Ctor : Node.ctors()) |
30 | if (Ctor->isCopyConstructor()) |
31 | return !Ctor->isDeleted(); |
32 | return false; |
33 | } |
34 | |
35 | static bool isMoveConstructible(CXXRecordDecl const &Node) { |
36 | if (Node.needsOverloadResolutionForMoveConstructor() && |
37 | Node.needsImplicitMoveConstructor()) { |
38 | // unresolved |
39 | for (CXXBaseSpecifier const &BS : Node.bases()) { |
40 | CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); |
41 | if (BRD != nullptr && !isMoveConstructible(Node: *BRD)) |
42 | return false; |
43 | } |
44 | } |
45 | if (Node.hasSimpleMoveConstructor()) |
46 | return true; |
47 | for (CXXConstructorDecl const *Ctor : Node.ctors()) |
48 | if (Ctor->isMoveConstructor()) |
49 | return !Ctor->isDeleted(); |
50 | return false; |
51 | } |
52 | |
53 | static bool isCopyAssignable(CXXRecordDecl const &Node) { |
54 | if (Node.needsOverloadResolutionForCopyAssignment() && |
55 | Node.needsImplicitCopyAssignment()) { |
56 | // unresolved |
57 | for (CXXBaseSpecifier const &BS : Node.bases()) { |
58 | CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); |
59 | if (BRD != nullptr && !isCopyAssignable(Node: *BRD)) |
60 | return false; |
61 | } |
62 | } |
63 | if (Node.hasSimpleCopyAssignment()) |
64 | return true; |
65 | for (CXXMethodDecl const *Method : Node.methods()) |
66 | if (Method->isCopyAssignmentOperator()) |
67 | return !Method->isDeleted(); |
68 | return false; |
69 | } |
70 | |
71 | static bool isMoveAssignable(CXXRecordDecl const &Node) { |
72 | if (Node.needsOverloadResolutionForMoveAssignment() && |
73 | Node.needsImplicitMoveAssignment()) { |
74 | // unresolved |
75 | for (CXXBaseSpecifier const &BS : Node.bases()) { |
76 | CXXRecordDecl const *BRD = BS.getType()->getAsCXXRecordDecl(); |
77 | if (BRD != nullptr && !isMoveAssignable(Node: *BRD)) |
78 | return false; |
79 | } |
80 | } |
81 | if (Node.hasSimpleMoveAssignment()) |
82 | return true; |
83 | for (CXXMethodDecl const *Method : Node.methods()) |
84 | if (Method->isMoveAssignmentOperator()) |
85 | return !Method->isDeleted(); |
86 | return false; |
87 | } |
88 | |
89 | namespace { |
90 | |
91 | AST_MATCHER(FieldDecl, isMemberOfLambda) { |
92 | return Node.getParent()->isLambda(); |
93 | } |
94 | |
95 | AST_MATCHER(CXXRecordDecl, isCopyableOrMovable) { |
96 | return isCopyConstructible(Node) || isMoveConstructible(Node) || |
97 | isCopyAssignable(Node) || isMoveAssignable(Node); |
98 | } |
99 | |
100 | } // namespace |
101 | |
102 | void AvoidConstOrRefDataMembersCheck::registerMatchers(MatchFinder *Finder) { |
103 | Finder->addMatcher( |
104 | NodeMatch: fieldDecl( |
105 | unless(isMemberOfLambda()), |
106 | anyOf( |
107 | fieldDecl(hasType(InnerMatcher: hasCanonicalType(InnerMatcher: referenceType()))).bind(ID: "ref" ), |
108 | fieldDecl(hasType(InnerMatcher: qualType(isConstQualified()))).bind(ID: "const" )), |
109 | hasDeclContext(InnerMatcher: cxxRecordDecl(isCopyableOrMovable()))), |
110 | Action: this); |
111 | } |
112 | |
113 | void AvoidConstOrRefDataMembersCheck::check( |
114 | const MatchFinder::MatchResult &Result) { |
115 | if (const auto *MatchedDecl = Result.Nodes.getNodeAs<FieldDecl>(ID: "ref" )) |
116 | diag(MatchedDecl->getLocation(), "member %0 of type %1 is a reference" ) |
117 | << MatchedDecl << MatchedDecl->getType(); |
118 | if (const auto *MatchedDecl = Result.Nodes.getNodeAs<FieldDecl>(ID: "const" )) |
119 | diag(MatchedDecl->getLocation(), "member %0 of type %1 is const qualified" ) |
120 | << MatchedDecl << MatchedDecl->getType(); |
121 | } |
122 | |
123 | } // namespace clang::tidy::cppcoreguidelines |
124 | |