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);
43 eachOf(has(cxxDestructorDecl().bind(
"dtor")),
44 has(cxxConstructorDecl(isCopyConstructor()).bind(
"copy-ctor")),
45 has(cxxMethodDecl(isCopyAssignmentOperator())
46 .bind(
"copy-assign")),
47 has(cxxConstructorDecl(isMoveConstructor()).bind(
"move-ctor")),
48 has(cxxMethodDecl(isMoveAssignmentOperator())
49 .bind(
"move-assign"))))
54 static llvm::StringRef
58 return "a destructor";
61 return "a default destructor";
64 return "a non-default destructor";
66 return "a copy constructor";
68 return "a copy assignment operator";
70 return "a move constructor";
72 return "a move assignment operator";
74 llvm_unreachable(
"Unhandled SpecialMemberFunctionKind");
78 join(ArrayRef<SpecialMemberFunctionsCheck::SpecialMemberFunctionKind> SMFS,
79 llvm::StringRef AndOr) {
81 assert(!SMFS.empty() &&
82 "List of defined or undefined members should never be empty.");
84 llvm::raw_string_ostream Stream(Buffer);
87 size_t LastIndex = SMFS.size() - 1;
88 for (
size_t I = 1; I < LastIndex; ++I) {
92 Stream << AndOr <<
toString(SMFS[LastIndex]);
98 const MatchFinder::MatchResult &Result) {
99 const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXRecordDecl>(
"class-def");
103 ClassDefId ID(MatchedDecl->getLocation(), std::string(MatchedDecl->getName()));
107 ClassWithSpecialMembers[
ID];
108 if (!llvm::is_contained(Members, Data))
109 Members.push_back(std::move(Data));
112 if (
const auto *Dtor = Result.Nodes.getNodeAs<CXXMethodDecl>(
"dtor")) {
113 StoreMember({Dtor->isDefaulted()
119 std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>>
125 for (
const auto &KV : Matchers)
126 if (
const auto *MethodDecl =
127 Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) {
128 StoreMember({KV.second, MethodDecl->isDeleted()});
133 for (
const auto &
C : ClassWithSpecialMembers) {
134 checkForMissingMembers(
C.first,
C.second);
138 void SpecialMemberFunctionsCheck::checkForMissingMembers(
139 const ClassDefId &
ID,
140 llvm::ArrayRef<SpecialMemberFunctionData> DefinedMembers) {
141 llvm::SmallVector<SpecialMemberFunctionKind, 5> MissingMembers;
144 return llvm::any_of(DefinedMembers, [
Kind](
const auto &Data) {
145 return Data.FunctionKind ==
Kind;
150 return llvm::any_of(DefinedMembers, [
Kind](
const auto &Data) {
151 return Data.FunctionKind ==
Kind && Data.IsDeleted;
156 if (!HasMember(
Kind))
157 MissingMembers.push_back(
Kind);
162 (!AllowSoleDefaultDtor &&
169 bool RequireFive = (!AllowMissingMoveFunctions && RequireThree &&
184 !(AllowMissingMoveFunctionsWhenCopyIsDeleted &&
187 assert(RequireThree);
192 if (!MissingMembers.empty()) {
193 llvm::SmallVector<SpecialMemberFunctionKind, 5> DefinedMemberKinds;
194 llvm::transform(DefinedMembers, std::back_inserter(DefinedMemberKinds),
195 [](
const auto &Data) {
return Data.FunctionKind; });
196 diag(
ID.first,
"class '%0' defines %1 but does not define %2")