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