diff options
author | Jonathan Coe <jbcoe@me.com> | 2016-07-30 08:58:54 +0000 |
---|---|---|
committer | Jonathan Coe <jbcoe@me.com> | 2016-07-30 08:58:54 +0000 |
commit | 5d304b2456afba562855a692a5ea4d10cc5da9e7 (patch) | |
tree | 6714b10b6672049bfa6ec9d3a0676a886b092ec5 /clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp | |
parent | fcfec5fdef79cb4d96033aaabdb8286912d131ea (diff) | |
download | bcm5719-llvm-5d304b2456afba562855a692a5ea4d10cc5da9e7.tar.gz bcm5719-llvm-5d304b2456afba562855a692a5ea4d10cc5da9e7.zip |
[clang-tidy] add check cppcoreguidelines-special-member-functions
Summary:
Check for classes that violate the rule of five and zero as specified in CppCoreGuidelines:
"If a class defines or deletes a default operation then it should define or delete them all."
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c21-if-you-define-or-delete-any-default-operation-define-or-delete-them-all.
Reviewers: alexfh, sbenza, aaron.ballman
Subscribers: Prazek, Eugene.Zelenko, cfe-commits, ericLemanissier, nemanjai
Projects: #clang-tools-extra
Differential Revision: https://reviews.llvm.org/D22513
llvm-svn: 277262
Diffstat (limited to 'clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp')
-rw-r--r-- | clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp new file mode 100644 index 00000000000..9a0ffa97a86 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/SpecialMemberFunctionsCheck.cpp @@ -0,0 +1,133 @@ +//===--- 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); +} + +llvm::StringRef SpecialMemberFunctionsCheck::toString( + SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K) { + switch (K) { + case SpecialMemberFunctionKind::Destructor: + return "a destructor"; + case SpecialMemberFunctionKind::CopyConstructor: + return "a copy constructor"; + case SpecialMemberFunctionKind::CopyAssignment: + return "a copy assignment operator"; + case SpecialMemberFunctionKind::MoveConstructor: + return "a move constructor"; + case SpecialMemberFunctionKind::MoveAssignment: + return "a move assignment operator"; + } + llvm_unreachable("Unhandled SpecialMemberFunctionKind"); +} + +std::string SpecialMemberFunctionsCheck::join( + llvm::ArrayRef<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 CXXRecordDecl *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)) + ClassWithSpecialMembers[ID].push_back(KV.second); +} + +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) { + ArrayRef<SpecialMemberFunctionKind> 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 |