clang-tools  9.0.0svn
SpecialMemberFunctionsCheck.cpp
Go to the documentation of this file.
1 //===--- SpecialMemberFunctionsCheck.cpp - clang-tidy----------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
10 
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "llvm/ADT/DenseMapInfo.h"
14 #include "llvm/ADT/StringExtras.h"
15 
16 #define DEBUG_TYPE "clang-tidy"
17 
18 using namespace clang::ast_matchers;
19 
20 namespace clang {
21 namespace tidy {
22 namespace cppcoreguidelines {
23 
24 SpecialMemberFunctionsCheck::SpecialMemberFunctionsCheck(
25  StringRef Name, ClangTidyContext *Context)
26  : ClangTidyCheck(Name, Context),
27  AllowMissingMoveFunctions(Options.get("AllowMissingMoveFunctions", 0)),
28  AllowSoleDefaultDtor(Options.get("AllowSoleDefaultDtor", 0)) {}
29 
32  Options.store(Opts, "AllowMissingMoveFunctions", AllowMissingMoveFunctions);
33  Options.store(Opts, "AllowSoleDefaultDtor", AllowSoleDefaultDtor);
34 }
35 
37  if (!getLangOpts().CPlusPlus)
38  return;
39  Finder->addMatcher(
40  cxxRecordDecl(
41  eachOf(
42  has(cxxDestructorDecl(unless(isImplicit())).bind("dtor")),
43  has(cxxConstructorDecl(isCopyConstructor(), unless(isImplicit()))
44  .bind("copy-ctor")),
45  has(cxxMethodDecl(isCopyAssignmentOperator(),
46  unless(isImplicit()))
47  .bind("copy-assign")),
48  has(cxxConstructorDecl(isMoveConstructor(), unless(isImplicit()))
49  .bind("move-ctor")),
50  has(cxxMethodDecl(isMoveAssignmentOperator(),
51  unless(isImplicit()))
52  .bind("move-assign"))))
53  .bind("class-def"),
54  this);
55 }
56 
57 static llvm::StringRef
59  switch (K) {
61  return "a destructor";
64  return "a default destructor";
67  return "a non-default destructor";
69  return "a copy constructor";
71  return "a copy assignment operator";
73  return "a move constructor";
75  return "a move assignment operator";
76  }
77  llvm_unreachable("Unhandled SpecialMemberFunctionKind");
78 }
79 
80 static std::string
81 join(ArrayRef<SpecialMemberFunctionsCheck::SpecialMemberFunctionKind> SMFS,
82  llvm::StringRef AndOr) {
83 
84  assert(!SMFS.empty() &&
85  "List of defined or undefined members should never be empty.");
86  std::string Buffer;
87  llvm::raw_string_ostream Stream(Buffer);
88 
89  Stream << toString(SMFS[0]);
90  size_t LastIndex = SMFS.size() - 1;
91  for (size_t i = 1; i < LastIndex; ++i) {
92  Stream << ", " << toString(SMFS[i]);
93  }
94  if (LastIndex != 0) {
95  Stream << AndOr << toString(SMFS[LastIndex]);
96  }
97  return Stream.str();
98 }
99 
101  const MatchFinder::MatchResult &Result) {
102  const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXRecordDecl>("class-def");
103  if (!MatchedDecl)
104  return;
105 
106  ClassDefId ID(MatchedDecl->getLocation(), MatchedDecl->getName());
107 
108  auto StoreMember = [this, &ID](SpecialMemberFunctionKind Kind) {
109  llvm::SmallVectorImpl<SpecialMemberFunctionKind> &Members =
110  ClassWithSpecialMembers[ID];
111  if (!llvm::is_contained(Members, Kind))
112  Members.push_back(Kind);
113  };
114 
115  if (const auto *Dtor = Result.Nodes.getNodeAs<CXXMethodDecl>("dtor")) {
116  StoreMember(Dtor->isDefaulted()
119  }
120 
121  std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>>
122  Matchers = {{"copy-ctor", SpecialMemberFunctionKind::CopyConstructor},
126 
127  for (const auto &KV : Matchers)
128  if (Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) {
129  StoreMember(KV.second);
130  }
131 }
132 
134  for (const auto &C : ClassWithSpecialMembers) {
135  checkForMissingMembers(C.first, C.second);
136  }
137 }
138 
139 void SpecialMemberFunctionsCheck::checkForMissingMembers(
140  const ClassDefId &ID,
141  llvm::ArrayRef<SpecialMemberFunctionKind> DefinedMembers) {
142  llvm::SmallVector<SpecialMemberFunctionKind, 5> MissingMembers;
143 
144  auto HasMember = [&](SpecialMemberFunctionKind Kind) {
145  return llvm::is_contained(DefinedMembers, Kind);
146  };
147 
148  auto RequireMember = [&](SpecialMemberFunctionKind Kind) {
149  if (!HasMember(Kind))
150  MissingMembers.push_back(Kind);
151  };
152 
153  bool RequireThree =
155  (!AllowSoleDefaultDtor &&
161 
162  bool RequireFive = (!AllowMissingMoveFunctions && RequireThree &&
163  getLangOpts().CPlusPlus11) ||
166 
167  if (RequireThree) {
170  MissingMembers.push_back(SpecialMemberFunctionKind::Destructor);
171 
174  }
175 
176  if (RequireFive) {
177  assert(RequireThree);
180  }
181 
182  if (!MissingMembers.empty())
183  diag(ID.first, "class '%0' defines %1 but does not define %2")
184  << ID.second << cppcoreguidelines::join(DefinedMembers, " and ")
185  << cppcoreguidelines::join(MissingMembers, " or ");
186 }
187 
188 } // namespace cppcoreguidelines
189 } // namespace tidy
190 } // 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:472
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:186
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:126
BindArgumentKind Kind
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
===– 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:437