blob: df3c279659d389fd52549848b46d43d15f285e4d [file] [log] [blame]
Jonathan Coe5d304b22016-07-30 08:58:54 +00001//===--- SpecialMemberFunctionsCheck.cpp - clang-tidy----------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "SpecialMemberFunctionsCheck.h"
11
12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "llvm/ADT/DenseMapInfo.h"
15#include "llvm/ADT/StringExtras.h"
16
17#define DEBUG_TYPE "clang-tidy"
18
19using namespace clang::ast_matchers;
20
21namespace clang {
22namespace tidy {
23namespace cppcoreguidelines {
24
Aaron Ballman9dd8caa2017-03-13 21:39:00 +000025SpecialMemberFunctionsCheck::SpecialMemberFunctionsCheck(
26 StringRef Name, ClangTidyContext *Context)
27 : ClangTidyCheck(Name, Context),
28 AllowMissingMoveFunctions(Options.get("AllowMissingMoveFunctions", 0)),
29 AllowSoleDefaultDtor(Options.get("AllowSoleDefaultDtor", 0)) {}
30
31void SpecialMemberFunctionsCheck::storeOptions(
32 ClangTidyOptions::OptionMap &Opts) {
33 Options.store(Opts, "AllowMissingMoveFunctions", AllowMissingMoveFunctions);
34 Options.store(Opts, "AllowSoleDefaultDtor", AllowSoleDefaultDtor);
35}
36
Jonathan Coe5d304b22016-07-30 08:58:54 +000037void SpecialMemberFunctionsCheck::registerMatchers(MatchFinder *Finder) {
38 if (!getLangOpts().CPlusPlus)
39 return;
40 Finder->addMatcher(
41 cxxRecordDecl(
42 eachOf(
43 has(cxxDestructorDecl(unless(isImplicit())).bind("dtor")),
44 has(cxxConstructorDecl(isCopyConstructor(), unless(isImplicit()))
45 .bind("copy-ctor")),
46 has(cxxMethodDecl(isCopyAssignmentOperator(),
47 unless(isImplicit()))
48 .bind("copy-assign")),
49 has(cxxConstructorDecl(isMoveConstructor(), unless(isImplicit()))
50 .bind("move-ctor")),
51 has(cxxMethodDecl(isMoveAssignmentOperator(),
52 unless(isImplicit()))
53 .bind("move-assign"))))
54 .bind("class-def"),
55 this);
56}
57
Jonathan Coe77ec2632016-08-02 21:18:37 +000058static llvm::StringRef
59toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K) {
Jonathan Coe5d304b22016-07-30 08:58:54 +000060 switch (K) {
Jonathan Coe77ec2632016-08-02 21:18:37 +000061 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::Destructor:
Jonathan Coe5d304b22016-07-30 08:58:54 +000062 return "a destructor";
Aaron Ballman9dd8caa2017-03-13 21:39:00 +000063 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::
64 DefaultDestructor:
65 return "a default destructor";
66 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::
67 NonDefaultDestructor:
68 return "a non-default destructor";
Jonathan Coe77ec2632016-08-02 21:18:37 +000069 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyConstructor:
Jonathan Coe5d304b22016-07-30 08:58:54 +000070 return "a copy constructor";
Jonathan Coe77ec2632016-08-02 21:18:37 +000071 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyAssignment:
Jonathan Coe5d304b22016-07-30 08:58:54 +000072 return "a copy assignment operator";
Jonathan Coe77ec2632016-08-02 21:18:37 +000073 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveConstructor:
Jonathan Coe5d304b22016-07-30 08:58:54 +000074 return "a move constructor";
Jonathan Coe77ec2632016-08-02 21:18:37 +000075 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveAssignment:
Jonathan Coe5d304b22016-07-30 08:58:54 +000076 return "a move assignment operator";
77 }
78 llvm_unreachable("Unhandled SpecialMemberFunctionKind");
79}
80
Jonathan Coe77ec2632016-08-02 21:18:37 +000081static std::string
82join(ArrayRef<SpecialMemberFunctionsCheck::SpecialMemberFunctionKind> SMFS,
83 llvm::StringRef AndOr) {
Jonathan Coe5d304b22016-07-30 08:58:54 +000084
85 assert(!SMFS.empty() &&
86 "List of defined or undefined members should never be empty.");
87 std::string Buffer;
88 llvm::raw_string_ostream Stream(Buffer);
89
90 Stream << toString(SMFS[0]);
91 size_t LastIndex = SMFS.size() - 1;
92 for (size_t i = 1; i < LastIndex; ++i) {
93 Stream << ", " << toString(SMFS[i]);
94 }
95 if (LastIndex != 0) {
96 Stream << AndOr << toString(SMFS[LastIndex]);
97 }
98 return Stream.str();
99}
100
101void SpecialMemberFunctionsCheck::check(
102 const MatchFinder::MatchResult &Result) {
Piotr Padlewski08124b12016-12-14 15:29:23 +0000103 const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXRecordDecl>("class-def");
Jonathan Coe5d304b22016-07-30 08:58:54 +0000104 if (!MatchedDecl)
105 return;
106
107 ClassDefId ID(MatchedDecl->getLocation(), MatchedDecl->getName());
108
Aaron Ballman9dd8caa2017-03-13 21:39:00 +0000109 auto StoreMember = [this, &ID](SpecialMemberFunctionKind Kind) {
110 llvm::SmallVectorImpl<SpecialMemberFunctionKind> &Members =
111 ClassWithSpecialMembers[ID];
112 if (!llvm::is_contained(Members, Kind))
113 Members.push_back(Kind);
114 };
115
116 if (const auto *Dtor = Result.Nodes.getNodeAs<CXXMethodDecl>("dtor")) {
117 StoreMember(Dtor->isDefaulted()
118 ? SpecialMemberFunctionKind::DefaultDestructor
119 : SpecialMemberFunctionKind::NonDefaultDestructor);
120 }
121
Jonathan Coe5d304b22016-07-30 08:58:54 +0000122 std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>>
Aaron Ballman9dd8caa2017-03-13 21:39:00 +0000123 Matchers = {{"copy-ctor", SpecialMemberFunctionKind::CopyConstructor},
Jonathan Coe5d304b22016-07-30 08:58:54 +0000124 {"copy-assign", SpecialMemberFunctionKind::CopyAssignment},
125 {"move-ctor", SpecialMemberFunctionKind::MoveConstructor},
126 {"move-assign", SpecialMemberFunctionKind::MoveAssignment}};
127
128 for (const auto &KV : Matchers)
Justin Lebarae90ad22016-10-21 20:13:39 +0000129 if (Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) {
Aaron Ballman9dd8caa2017-03-13 21:39:00 +0000130 StoreMember(KV.second);
Justin Lebarae90ad22016-10-21 20:13:39 +0000131 }
Jonathan Coe5d304b22016-07-30 08:58:54 +0000132}
133
134void SpecialMemberFunctionsCheck::onEndOfTranslationUnit() {
Jonathan Coe5d304b22016-07-30 08:58:54 +0000135 for (const auto &C : ClassWithSpecialMembers) {
Aaron Ballman9dd8caa2017-03-13 21:39:00 +0000136 checkForMissingMembers(C.first, C.second);
Jonathan Coe5d304b22016-07-30 08:58:54 +0000137 }
138}
Aaron Ballman9dd8caa2017-03-13 21:39:00 +0000139
140void SpecialMemberFunctionsCheck::checkForMissingMembers(
141 const ClassDefId &ID,
142 llvm::ArrayRef<SpecialMemberFunctionKind> DefinedMembers) {
143 llvm::SmallVector<SpecialMemberFunctionKind, 5> MissingMembers;
144
145 auto HasMember = [&](SpecialMemberFunctionKind Kind) {
146 return llvm::is_contained(DefinedMembers, Kind);
147 };
148
149 auto RequireMember = [&](SpecialMemberFunctionKind Kind) {
150 if (!HasMember(Kind))
151 MissingMembers.push_back(Kind);
152 };
153
154 bool RequireThree =
155 HasMember(SpecialMemberFunctionKind::NonDefaultDestructor) ||
156 (!AllowSoleDefaultDtor &&
157 HasMember(SpecialMemberFunctionKind::DefaultDestructor)) ||
158 HasMember(SpecialMemberFunctionKind::CopyConstructor) ||
159 HasMember(SpecialMemberFunctionKind::CopyAssignment) ||
160 HasMember(SpecialMemberFunctionKind::MoveConstructor) ||
161 HasMember(SpecialMemberFunctionKind::MoveAssignment);
162
163 bool RequireFive = (!AllowMissingMoveFunctions && RequireThree &&
164 getLangOpts().CPlusPlus11) ||
165 HasMember(SpecialMemberFunctionKind::MoveConstructor) ||
166 HasMember(SpecialMemberFunctionKind::MoveAssignment);
167
168 if (RequireThree) {
169 if (!HasMember(SpecialMemberFunctionKind::DefaultDestructor) &&
170 !HasMember(SpecialMemberFunctionKind::NonDefaultDestructor))
171 MissingMembers.push_back(SpecialMemberFunctionKind::Destructor);
172
173 RequireMember(SpecialMemberFunctionKind::CopyConstructor);
174 RequireMember(SpecialMemberFunctionKind::CopyAssignment);
175 }
176
177 if (RequireFive) {
178 assert(RequireThree);
179 RequireMember(SpecialMemberFunctionKind::MoveConstructor);
180 RequireMember(SpecialMemberFunctionKind::MoveAssignment);
181 }
182
183 if (!MissingMembers.empty())
184 diag(ID.first, "class '%0' defines %1 but does not define %2")
Chandler Carruth08781dc2017-03-21 20:15:42 +0000185 << ID.second << cppcoreguidelines::join(DefinedMembers, " and ")
186 << cppcoreguidelines::join(MissingMembers, " or ");
Aaron Ballman9dd8caa2017-03-13 21:39:00 +0000187}
188
Jonathan Coe5d304b22016-07-30 08:58:54 +0000189} // namespace cppcoreguidelines
190} // namespace tidy
191} // namespace clang