11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "llvm/ADT/DenseMapInfo.h"
14 #include "llvm/ADT/StringExtras.h"
16 #define DEBUG_TYPE "clang-tidy"
22 namespace cppcoreguidelines {
24 SpecialMemberFunctionsCheck::SpecialMemberFunctionsCheck(
27 "AllowMissingMoveFunctions", false)),
28 AllowSoleDefaultDtor(Options.get(
"AllowSoleDefaultDtor", false)),
29 AllowMissingMoveFunctionsWhenCopyIsDeleted(
30 Options.get(
"AllowMissingMoveFunctionsWhenCopyIsDeleted", false)) {}
34 Options.
store(Opts,
"AllowMissingMoveFunctions", AllowMissingMoveFunctions);
35 Options.
store(Opts,
"AllowSoleDefaultDtor", AllowSoleDefaultDtor);
36 Options.
store(Opts,
"AllowMissingMoveFunctionsWhenCopyIsDeleted",
37 AllowMissingMoveFunctionsWhenCopyIsDeleted);
44 has(cxxDestructorDecl(unless(isImplicit())).bind(
"dtor")),
45 has(cxxConstructorDecl(isCopyConstructor(), unless(isImplicit()))
47 has(cxxMethodDecl(isCopyAssignmentOperator(),
49 .bind(
"copy-assign")),
50 has(cxxConstructorDecl(isMoveConstructor(), unless(isImplicit()))
52 has(cxxMethodDecl(isMoveAssignmentOperator(),
54 .bind(
"move-assign"))))
59 static llvm::StringRef
63 return "a destructor";
66 return "a default destructor";
69 return "a non-default destructor";
71 return "a copy constructor";
73 return "a copy assignment operator";
75 return "a move constructor";
77 return "a move assignment operator";
79 llvm_unreachable(
"Unhandled SpecialMemberFunctionKind");
83 join(ArrayRef<SpecialMemberFunctionsCheck::SpecialMemberFunctionKind> SMFS,
84 llvm::StringRef AndOr) {
86 assert(!SMFS.empty() &&
87 "List of defined or undefined members should never be empty.");
89 llvm::raw_string_ostream Stream(Buffer);
92 size_t LastIndex = SMFS.size() - 1;
93 for (
size_t i = 1; i < LastIndex; ++i) {
97 Stream << AndOr <<
toString(SMFS[LastIndex]);
103 const MatchFinder::MatchResult &Result) {
104 const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXRecordDecl>(
"class-def");
108 ClassDefId ID(MatchedDecl->getLocation(), std::string(MatchedDecl->getName()));
111 llvm::SmallVectorImpl<SpecialMemberFunctionData> &Members =
112 ClassWithSpecialMembers[ID];
113 if (!llvm::is_contained(Members, data))
114 Members.push_back(std::move(data));
117 if (
const auto *Dtor = Result.Nodes.getNodeAs<CXXMethodDecl>(
"dtor")) {
118 StoreMember({Dtor->isDefaulted()
124 std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>>
130 for (
const auto &KV : Matchers)
131 if (
const auto *MethodDecl =
132 Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) {
133 StoreMember({KV.second, MethodDecl->isDeleted()});
138 for (
const auto &C : ClassWithSpecialMembers) {
139 checkForMissingMembers(C.first, C.second);
143 void SpecialMemberFunctionsCheck::checkForMissingMembers(
144 const ClassDefId &ID,
145 llvm::ArrayRef<SpecialMemberFunctionData> DefinedMembers) {
146 llvm::SmallVector<SpecialMemberFunctionKind, 5> MissingMembers;
149 return llvm::any_of(DefinedMembers, [
Kind](
const auto &data) {
150 return data.FunctionKind ==
Kind;
155 return llvm::any_of(DefinedMembers, [
Kind](
const auto &data) {
156 return data.FunctionKind ==
Kind && data.IsDeleted;
161 if (!HasMember(
Kind))
162 MissingMembers.push_back(
Kind);
167 (!AllowSoleDefaultDtor &&
174 bool RequireFive = (!AllowMissingMoveFunctions && RequireThree &&
189 !(AllowMissingMoveFunctionsWhenCopyIsDeleted &&
192 assert(RequireThree);
197 if (!MissingMembers.empty()) {
198 llvm::SmallVector<SpecialMemberFunctionKind, 5> DefinedMemberKinds;
199 llvm::transform(DefinedMembers, std::back_inserter(DefinedMemberKinds),
200 [](
const auto &data) {
return data.FunctionKind; });
201 diag(ID.first,
"class '%0' defines %1 but does not define %2")