clang-tools 6.0.0

SpecialMemberFunctionsCheck.cpp
Go to the documentation of this file.
1 //===--- 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
19 using namespace clang::ast_matchers;
20
21 namespace clang {
22 namespace tidy {
23 namespace cppcoreguidelines {
24
25 SpecialMemberFunctionsCheck::SpecialMemberFunctionsCheck(
26 StringRef Name, ClangTidyContext *Context)
27 : ClangTidyCheck(Name, Context),
28 AllowMissingMoveFunctions(Options.get("AllowMissingMoveFunctions", 0)),
29 AllowSoleDefaultDtor(Options.get("AllowSoleDefaultDtor", 0)) {}
30
31 void SpecialMemberFunctionsCheck::storeOptions(
32 ClangTidyOptions::OptionMap &Opts) {
33 Options.store(Opts, "AllowMissingMoveFunctions", AllowMissingMoveFunctions);
34 Options.store(Opts, "AllowSoleDefaultDtor", AllowSoleDefaultDtor);
35 }
36
37 void 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
58 static llvm::StringRef
59 toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K) {
60 switch (K) {
61 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::Destructor:
62 return "a destructor";
63 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::
64 DefaultDestructor:
65 return "a default destructor";
66 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::
67 NonDefaultDestructor:
68 return "a non-default destructor";
69 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyConstructor:
70 return "a copy constructor";
71 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyAssignment:
72 return "a copy assignment operator";
73 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveConstructor:
74 return "a move constructor";
75 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveAssignment:
76 return "a move assignment operator";
77 }
78 llvm_unreachable("Unhandled SpecialMemberFunctionKind");
79 }
80
81 static std::string
82 join(ArrayRef<SpecialMemberFunctionsCheck::SpecialMemberFunctionKind> SMFS,
83 llvm::StringRef AndOr) {
84
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
101 void SpecialMemberFunctionsCheck::check(
102 const MatchFinder::MatchResult &Result) {
103 const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXRecordDecl>("class-def");
104 if (!MatchedDecl)
105 return;
106
107 ClassDefId ID(MatchedDecl->getLocation(), MatchedDecl->getName());
108
109 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
122 std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>>
123 Matchers = {{"copy-ctor", SpecialMemberFunctionKind::CopyConstructor},
124 {"copy-assign", SpecialMemberFunctionKind::CopyAssignment},
125 {"move-ctor", SpecialMemberFunctionKind::MoveConstructor},
126 {"move-assign", SpecialMemberFunctionKind::MoveAssignment}};
127
128 for (const auto &KV : Matchers)
129 if (Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) {
130 StoreMember(KV.second);
131 }
132 }
133
134 void SpecialMemberFunctionsCheck::onEndOfTranslationUnit() {
135 for (const auto &C : ClassWithSpecialMembers) {
136 checkForMissingMembers(C.first, C.second);
137 }
138 }
139
140 void 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")
185 << ID.second << cppcoreguidelines::join(DefinedMembers, " and ")
186 << cppcoreguidelines::join(MissingMembers, " or ");
187 }
188
189 } // namespace cppcoreguidelines
190 } // namespace tidy
191 } // namespace clang
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::onEndOfTranslationUnit
void onEndOfTranslationUnit() override
Definition: SpecialMemberFunctionsCheck.cpp:134
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyConstructor
clang::tidy::OptionsView::store
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidy.cpp:449
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: SpecialMemberFunctionsCheck.cpp:31
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::SpecialMemberFunctionKind
SpecialMemberFunctionKind
Definition: SpecialMemberFunctionsCheck.h:34
Name
StringHandle Name
Definition: PreprocessorTracker.cpp:525
clang::tidy::ClangTidyCheck::getLangOpts
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
SpecialMemberFunctionsCheck.h
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::Destructor
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveConstructor
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: SpecialMemberFunctionsCheck.cpp:37
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: SpecialMemberFunctionsCheck.cpp:101
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:59
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidy.h:183
Kind
BindArgumentKind Kind
Definition: AvoidBindCheck.cpp:41
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveAssignment
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyAssignment
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:99
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::DefaultDestructor
clang
Definition: AndroidTidyModule.cpp:28
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::NonDefaultDestructor
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:104
clang::tidy::cppcoreguidelines::join
static std::string join(ArrayRef< SpecialMemberFunctionsCheck::SpecialMemberFunctionKind > SMFS, llvm::StringRef AndOr)
Definition: SpecialMemberFunctionsCheck.cpp:82
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:416
clang::tidy::cppcoreguidelines::SpecialMemberFunctionsCheck::ClassDefId
std::pair< SourceLocation, std::string > ClassDefId
Definition: SpecialMemberFunctionsCheck.h:44