1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
//===--- SpecialMemberFunctionsCheck.cpp - clang-tidy----------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "SpecialMemberFunctionsCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/StringExtras.h"
#define DEBUG_TYPE "clang-tidy"
using namespace clang::ast_matchers;
namespace clang {
namespace tidy {
namespace cppcoreguidelines {
void SpecialMemberFunctionsCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus)
return;
Finder->addMatcher(
cxxRecordDecl(
eachOf(
has(cxxDestructorDecl(unless(isImplicit())).bind("dtor")),
has(cxxConstructorDecl(isCopyConstructor(), unless(isImplicit()))
.bind("copy-ctor")),
has(cxxMethodDecl(isCopyAssignmentOperator(),
unless(isImplicit()))
.bind("copy-assign")),
has(cxxConstructorDecl(isMoveConstructor(), unless(isImplicit()))
.bind("move-ctor")),
has(cxxMethodDecl(isMoveAssignmentOperator(),
unless(isImplicit()))
.bind("move-assign"))))
.bind("class-def"),
this);
}
static llvm::StringRef
toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K) {
switch (K) {
case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::Destructor:
return "a destructor";
case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyConstructor:
return "a copy constructor";
case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyAssignment:
return "a copy assignment operator";
case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveConstructor:
return "a move constructor";
case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveAssignment:
return "a move assignment operator";
}
llvm_unreachable("Unhandled SpecialMemberFunctionKind");
}
static std::string
join(ArrayRef<SpecialMemberFunctionsCheck::SpecialMemberFunctionKind> SMFS,
llvm::StringRef AndOr) {
assert(!SMFS.empty() &&
"List of defined or undefined members should never be empty.");
std::string Buffer;
llvm::raw_string_ostream Stream(Buffer);
Stream << toString(SMFS[0]);
size_t LastIndex = SMFS.size() - 1;
for (size_t i = 1; i < LastIndex; ++i) {
Stream << ", " << toString(SMFS[i]);
}
if (LastIndex != 0) {
Stream << AndOr << toString(SMFS[LastIndex]);
}
return Stream.str();
}
void SpecialMemberFunctionsCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXRecordDecl>("class-def");
if (!MatchedDecl)
return;
ClassDefId ID(MatchedDecl->getLocation(), MatchedDecl->getName());
std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>>
Matchers = {{"dtor", SpecialMemberFunctionKind::Destructor},
{"copy-ctor", SpecialMemberFunctionKind::CopyConstructor},
{"copy-assign", SpecialMemberFunctionKind::CopyAssignment},
{"move-ctor", SpecialMemberFunctionKind::MoveConstructor},
{"move-assign", SpecialMemberFunctionKind::MoveAssignment}};
for (const auto &KV : Matchers)
if (Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) {
SpecialMemberFunctionKind Kind = KV.second;
llvm::SmallVectorImpl<SpecialMemberFunctionKind> &Members =
ClassWithSpecialMembers[ID];
if (find(Members, Kind) == Members.end())
Members.push_back(Kind);
}
}
void SpecialMemberFunctionsCheck::onEndOfTranslationUnit() {
llvm::SmallVector<SpecialMemberFunctionKind, 5> AllSpecialMembers = {
SpecialMemberFunctionKind::Destructor,
SpecialMemberFunctionKind::CopyConstructor,
SpecialMemberFunctionKind::CopyAssignment};
if (getLangOpts().CPlusPlus11) {
AllSpecialMembers.push_back(SpecialMemberFunctionKind::MoveConstructor);
AllSpecialMembers.push_back(SpecialMemberFunctionKind::MoveAssignment);
}
for (const auto &C : ClassWithSpecialMembers) {
const auto &DefinedSpecialMembers = C.second;
if (DefinedSpecialMembers.size() == AllSpecialMembers.size())
continue;
llvm::SmallVector<SpecialMemberFunctionKind, 5> UndefinedSpecialMembers;
std::set_difference(AllSpecialMembers.begin(), AllSpecialMembers.end(),
DefinedSpecialMembers.begin(),
DefinedSpecialMembers.end(),
std::back_inserter(UndefinedSpecialMembers));
diag(C.first.first, "class '%0' defines %1 but does not define %2")
<< C.first.second << join(DefinedSpecialMembers, " and ")
<< join(UndefinedSpecialMembers, " or ");
}
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang
|