//===--- 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 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("class-def"); if (!MatchedDecl) return; ClassDefId ID(MatchedDecl->getLocation(), MatchedDecl->getName()); std::initializer_list> 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(KV.first)) { SpecialMemberFunctionKind Kind = KV.second; llvm::SmallVectorImpl &Members = ClassWithSpecialMembers[ID]; if (find(Members, Kind) == Members.end()) Members.push_back(Kind); } } void SpecialMemberFunctionsCheck::onEndOfTranslationUnit() { llvm::SmallVector 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 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