clang-tools  8.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
llvm::StringRef Name
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...
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
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
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