clang-tools  7.0.0svn
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 
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 
33  Options.store(Opts, "AllowMissingMoveFunctions", AllowMissingMoveFunctions);
34  Options.store(Opts, "AllowSoleDefaultDtor", AllowSoleDefaultDtor);
35 }
36 
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
60  switch (K) {
62  return "a destructor";
65  return "a default destructor";
68  return "a non-default destructor";
70  return "a copy constructor";
72  return "a copy assignment operator";
74  return "a move constructor";
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 
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()
120  }
121 
122  std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>>
123  Matchers = {{"copy-ctor", SpecialMemberFunctionKind::CopyConstructor},
127 
128  for (const auto &KV : Matchers)
129  if (Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) {
130  StoreMember(KV.second);
131  }
132 }
133 
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 =
156  (!AllowSoleDefaultDtor &&
162 
163  bool RequireFive = (!AllowMissingMoveFunctions && RequireThree &&
164  getLangOpts().CPlusPlus11) ||
167 
168  if (RequireThree) {
171  MissingMembers.push_back(SpecialMemberFunctionKind::Destructor);
172 
175  }
176 
177  if (RequireFive) {
178  assert(RequireThree);
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
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:460
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
StringHandle Name
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
BindArgumentKind Kind
std::map< std::string, std::string > OptionMap
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static std::string join(ArrayRef< SpecialMemberFunctionsCheck::SpecialMemberFunctionKind > SMFS, llvm::StringRef AndOr)
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:427