11#include "clang/AST/ASTContext.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "llvm/ADT/DenseMapInfo.h"
14#include "llvm/ADT/StringExtras.h"
16#define DEBUG_TYPE "clang-tidy"
25 "AllowMissingMoveFunctions", false)),
26 AllowSoleDefaultDtor(Options.get(
"AllowSoleDefaultDtor", false)),
27 AllowMissingMoveFunctionsWhenCopyIsDeleted(
28 Options.get(
"AllowMissingMoveFunctionsWhenCopyIsDeleted", false)) {}
32 Options.
store(Opts,
"AllowMissingMoveFunctions", AllowMissingMoveFunctions);
33 Options.
store(Opts,
"AllowSoleDefaultDtor", AllowSoleDefaultDtor);
34 Options.
store(Opts,
"AllowMissingMoveFunctionsWhenCopyIsDeleted",
35 AllowMissingMoveFunctionsWhenCopyIsDeleted);
41 eachOf(has(cxxDestructorDecl().bind(
"dtor")),
42 has(cxxConstructorDecl(isCopyConstructor()).bind(
"copy-ctor")),
43 has(cxxMethodDecl(isCopyAssignmentOperator())
44 .bind(
"copy-assign")),
45 has(cxxConstructorDecl(isMoveConstructor()).bind(
"move-ctor")),
46 has(cxxMethodDecl(isMoveAssignmentOperator())
47 .bind(
"move-assign"))))
56 return "a destructor";
59 return "a default destructor";
62 return "a non-default destructor";
64 return "a copy constructor";
66 return "a copy assignment operator";
68 return "a move constructor";
70 return "a move assignment operator";
72 llvm_unreachable(
"Unhandled SpecialMemberFunctionKind");
76join(ArrayRef<SpecialMemberFunctionsCheck::SpecialMemberFunctionKind> SMFS,
77 llvm::StringRef AndOr) {
79 assert(!SMFS.empty() &&
80 "List of defined or undefined members should never be empty.");
82 llvm::raw_string_ostream Stream(Buffer);
85 size_t LastIndex = SMFS.size() - 1;
86 for (
size_t I = 1; I < LastIndex; ++I) {
90 Stream << AndOr <<
toString(SMFS[LastIndex]);
96 const MatchFinder::MatchResult &Result) {
97 const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXRecordDecl>(
"class-def");
101 ClassDefId ID(MatchedDecl->getLocation(), std::string(MatchedDecl->getName()));
105 ClassWithSpecialMembers[
ID];
106 if (!llvm::is_contained(Members, Data))
107 Members.push_back(std::move(Data));
110 if (
const auto *Dtor = Result.Nodes.getNodeAs<CXXMethodDecl>(
"dtor")) {
113 if (Dtor->isDefined()) {
114 DestructorType = Dtor->getDefinition()->isDefaulted()
118 StoreMember({DestructorType, Dtor->isDeleted()});
121 std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>>
127 for (
const auto &KV : Matchers)
128 if (
const auto *MethodDecl =
129 Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) {
130 StoreMember({KV.second, MethodDecl->isDeleted()});
135 for (
const auto &
C : ClassWithSpecialMembers) {
136 checkForMissingMembers(
C.first,
C.second);
140void SpecialMemberFunctionsCheck::checkForMissingMembers(
141 const ClassDefId &
ID,
142 llvm::ArrayRef<SpecialMemberFunctionData> DefinedMembers) {
143 llvm::SmallVector<SpecialMemberFunctionKind, 5> MissingMembers;
146 return llvm::any_of(DefinedMembers, [Kind](
const auto &Data) {
147 return Data.FunctionKind ==
Kind;
152 return llvm::any_of(DefinedMembers, [Kind](
const auto &Data) {
153 return Data.FunctionKind ==
Kind && Data.IsDeleted;
158 if (!HasMember(Kind))
159 MissingMembers.push_back(Kind);
164 (!AllowSoleDefaultDtor &&
172 bool RequireFive = (!AllowMissingMoveFunctions && RequireThree &&
188 !(AllowMissingMoveFunctionsWhenCopyIsDeleted &&
191 assert(RequireThree);
196 if (!MissingMembers.empty()) {
197 llvm::SmallVector<SpecialMemberFunctionKind, 5> DefinedMemberKinds;
198 llvm::transform(DefinedMembers, std::back_inserter(DefinedMemberKinds),
199 [](
const auto &Data) {
return Data.FunctionKind; });
200 diag(
ID.first,
"class '%0' defines %1 but does not define %2")
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.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void onEndOfTranslationUnit() override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
std::pair< SourceLocation, std::string > ClassDefId
SpecialMemberFunctionKind
SpecialMemberFunctionsCheck(StringRef Name, ClangTidyContext *Context)
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
static std::string join(ArrayRef< SpecialMemberFunctionsCheck::SpecialMemberFunctionKind > SMFS, llvm::StringRef AndOr)
llvm::StringMap< ClangTidyValue > OptionMap