Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 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 | |
Aaron Ballman | 9dd8caa | 2017-03-13 21:39:00 +0000 | [diff] [blame] | 25 | SpecialMemberFunctionsCheck::SpecialMemberFunctionsCheck( |
| 26 | StringRef Name, ClangTidyContext *Context) |
| 27 | : ClangTidyCheck(Name, Context), |
| 28 | AllowMissingMoveFunctions(Options.get("AllowMissingMoveFunctions", 0)), |
| 29 | AllowSoleDefaultDtor(Options.get("AllowSoleDefaultDtor", 0)) {} |
| 30 | |
| 31 | void SpecialMemberFunctionsCheck::storeOptions( |
| 32 | ClangTidyOptions::OptionMap &Opts) { |
| 33 | Options.store(Opts, "AllowMissingMoveFunctions", AllowMissingMoveFunctions); |
| 34 | Options.store(Opts, "AllowSoleDefaultDtor", AllowSoleDefaultDtor); |
| 35 | } |
| 36 | |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 37 | void SpecialMemberFunctionsCheck::registerMatchers(MatchFinder *Finder) { |
| 38 | if (!getLangOpts().CPlusPlus) |
| 39 | return; |
| 40 | Finder->addMatcher( |
| 41 | cxxRecordDecl( |
| 42 | eachOf( |
| 43 | has(cxxDestructorDecl(unless(isImplicit())).bind("dtor")), |
| 44 | has(cxxConstructorDecl(isCopyConstructor(), unless(isImplicit())) |
| 45 | .bind("copy-ctor")), |
| 46 | has(cxxMethodDecl(isCopyAssignmentOperator(), |
| 47 | unless(isImplicit())) |
| 48 | .bind("copy-assign")), |
| 49 | has(cxxConstructorDecl(isMoveConstructor(), unless(isImplicit())) |
| 50 | .bind("move-ctor")), |
| 51 | has(cxxMethodDecl(isMoveAssignmentOperator(), |
| 52 | unless(isImplicit())) |
| 53 | .bind("move-assign")))) |
| 54 | .bind("class-def"), |
| 55 | this); |
| 56 | } |
| 57 | |
Jonathan Coe | 77ec263 | 2016-08-02 21:18:37 +0000 | [diff] [blame] | 58 | static llvm::StringRef |
| 59 | toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K) { |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 60 | switch (K) { |
Jonathan Coe | 77ec263 | 2016-08-02 21:18:37 +0000 | [diff] [blame] | 61 | case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::Destructor: |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 62 | return "a destructor"; |
Aaron Ballman | 9dd8caa | 2017-03-13 21:39:00 +0000 | [diff] [blame] | 63 | case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind:: |
| 64 | DefaultDestructor: |
| 65 | return "a default destructor"; |
| 66 | case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind:: |
| 67 | NonDefaultDestructor: |
| 68 | return "a non-default destructor"; |
Jonathan Coe | 77ec263 | 2016-08-02 21:18:37 +0000 | [diff] [blame] | 69 | case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyConstructor: |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 70 | return "a copy constructor"; |
Jonathan Coe | 77ec263 | 2016-08-02 21:18:37 +0000 | [diff] [blame] | 71 | case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyAssignment: |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 72 | return "a copy assignment operator"; |
Jonathan Coe | 77ec263 | 2016-08-02 21:18:37 +0000 | [diff] [blame] | 73 | case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveConstructor: |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 74 | return "a move constructor"; |
Jonathan Coe | 77ec263 | 2016-08-02 21:18:37 +0000 | [diff] [blame] | 75 | case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveAssignment: |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 76 | return "a move assignment operator"; |
| 77 | } |
| 78 | llvm_unreachable("Unhandled SpecialMemberFunctionKind"); |
| 79 | } |
| 80 | |
Jonathan Coe | 77ec263 | 2016-08-02 21:18:37 +0000 | [diff] [blame] | 81 | static std::string |
| 82 | join(ArrayRef<SpecialMemberFunctionsCheck::SpecialMemberFunctionKind> SMFS, |
| 83 | llvm::StringRef AndOr) { |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 84 | |
| 85 | assert(!SMFS.empty() && |
| 86 | "List of defined or undefined members should never be empty."); |
| 87 | std::string Buffer; |
| 88 | llvm::raw_string_ostream Stream(Buffer); |
| 89 | |
| 90 | Stream << toString(SMFS[0]); |
| 91 | size_t LastIndex = SMFS.size() - 1; |
| 92 | for (size_t i = 1; i < LastIndex; ++i) { |
| 93 | Stream << ", " << toString(SMFS[i]); |
| 94 | } |
| 95 | if (LastIndex != 0) { |
| 96 | Stream << AndOr << toString(SMFS[LastIndex]); |
| 97 | } |
| 98 | return Stream.str(); |
| 99 | } |
| 100 | |
| 101 | void SpecialMemberFunctionsCheck::check( |
| 102 | const MatchFinder::MatchResult &Result) { |
Piotr Padlewski | 08124b1 | 2016-12-14 15:29:23 +0000 | [diff] [blame] | 103 | const auto *MatchedDecl = Result.Nodes.getNodeAs<CXXRecordDecl>("class-def"); |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 104 | if (!MatchedDecl) |
| 105 | return; |
| 106 | |
| 107 | ClassDefId ID(MatchedDecl->getLocation(), MatchedDecl->getName()); |
| 108 | |
Aaron Ballman | 9dd8caa | 2017-03-13 21:39:00 +0000 | [diff] [blame] | 109 | auto StoreMember = [this, &ID](SpecialMemberFunctionKind Kind) { |
| 110 | llvm::SmallVectorImpl<SpecialMemberFunctionKind> &Members = |
| 111 | ClassWithSpecialMembers[ID]; |
| 112 | if (!llvm::is_contained(Members, Kind)) |
| 113 | Members.push_back(Kind); |
| 114 | }; |
| 115 | |
| 116 | if (const auto *Dtor = Result.Nodes.getNodeAs<CXXMethodDecl>("dtor")) { |
| 117 | StoreMember(Dtor->isDefaulted() |
| 118 | ? SpecialMemberFunctionKind::DefaultDestructor |
| 119 | : SpecialMemberFunctionKind::NonDefaultDestructor); |
| 120 | } |
| 121 | |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 122 | std::initializer_list<std::pair<std::string, SpecialMemberFunctionKind>> |
Aaron Ballman | 9dd8caa | 2017-03-13 21:39:00 +0000 | [diff] [blame] | 123 | Matchers = {{"copy-ctor", SpecialMemberFunctionKind::CopyConstructor}, |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 124 | {"copy-assign", SpecialMemberFunctionKind::CopyAssignment}, |
| 125 | {"move-ctor", SpecialMemberFunctionKind::MoveConstructor}, |
| 126 | {"move-assign", SpecialMemberFunctionKind::MoveAssignment}}; |
| 127 | |
| 128 | for (const auto &KV : Matchers) |
Justin Lebar | ae90ad2 | 2016-10-21 20:13:39 +0000 | [diff] [blame] | 129 | if (Result.Nodes.getNodeAs<CXXMethodDecl>(KV.first)) { |
Aaron Ballman | 9dd8caa | 2017-03-13 21:39:00 +0000 | [diff] [blame] | 130 | StoreMember(KV.second); |
Justin Lebar | ae90ad2 | 2016-10-21 20:13:39 +0000 | [diff] [blame] | 131 | } |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 132 | } |
| 133 | |
| 134 | void SpecialMemberFunctionsCheck::onEndOfTranslationUnit() { |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 135 | for (const auto &C : ClassWithSpecialMembers) { |
Aaron Ballman | 9dd8caa | 2017-03-13 21:39:00 +0000 | [diff] [blame] | 136 | checkForMissingMembers(C.first, C.second); |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 137 | } |
| 138 | } |
Aaron Ballman | 9dd8caa | 2017-03-13 21:39:00 +0000 | [diff] [blame] | 139 | |
| 140 | void SpecialMemberFunctionsCheck::checkForMissingMembers( |
| 141 | const ClassDefId &ID, |
| 142 | llvm::ArrayRef<SpecialMemberFunctionKind> DefinedMembers) { |
| 143 | llvm::SmallVector<SpecialMemberFunctionKind, 5> MissingMembers; |
| 144 | |
| 145 | auto HasMember = [&](SpecialMemberFunctionKind Kind) { |
| 146 | return llvm::is_contained(DefinedMembers, Kind); |
| 147 | }; |
| 148 | |
| 149 | auto RequireMember = [&](SpecialMemberFunctionKind Kind) { |
| 150 | if (!HasMember(Kind)) |
| 151 | MissingMembers.push_back(Kind); |
| 152 | }; |
| 153 | |
| 154 | bool RequireThree = |
| 155 | HasMember(SpecialMemberFunctionKind::NonDefaultDestructor) || |
| 156 | (!AllowSoleDefaultDtor && |
| 157 | HasMember(SpecialMemberFunctionKind::DefaultDestructor)) || |
| 158 | HasMember(SpecialMemberFunctionKind::CopyConstructor) || |
| 159 | HasMember(SpecialMemberFunctionKind::CopyAssignment) || |
| 160 | HasMember(SpecialMemberFunctionKind::MoveConstructor) || |
| 161 | HasMember(SpecialMemberFunctionKind::MoveAssignment); |
| 162 | |
| 163 | bool RequireFive = (!AllowMissingMoveFunctions && RequireThree && |
| 164 | getLangOpts().CPlusPlus11) || |
| 165 | HasMember(SpecialMemberFunctionKind::MoveConstructor) || |
| 166 | HasMember(SpecialMemberFunctionKind::MoveAssignment); |
| 167 | |
| 168 | if (RequireThree) { |
| 169 | if (!HasMember(SpecialMemberFunctionKind::DefaultDestructor) && |
| 170 | !HasMember(SpecialMemberFunctionKind::NonDefaultDestructor)) |
| 171 | MissingMembers.push_back(SpecialMemberFunctionKind::Destructor); |
| 172 | |
| 173 | RequireMember(SpecialMemberFunctionKind::CopyConstructor); |
| 174 | RequireMember(SpecialMemberFunctionKind::CopyAssignment); |
| 175 | } |
| 176 | |
| 177 | if (RequireFive) { |
| 178 | assert(RequireThree); |
| 179 | RequireMember(SpecialMemberFunctionKind::MoveConstructor); |
| 180 | RequireMember(SpecialMemberFunctionKind::MoveAssignment); |
| 181 | } |
| 182 | |
| 183 | if (!MissingMembers.empty()) |
| 184 | diag(ID.first, "class '%0' defines %1 but does not define %2") |
Chandler Carruth | 08781dc | 2017-03-21 20:15:42 +0000 | [diff] [blame] | 185 | << ID.second << cppcoreguidelines::join(DefinedMembers, " and ") |
| 186 | << cppcoreguidelines::join(MissingMembers, " or "); |
Aaron Ballman | 9dd8caa | 2017-03-13 21:39:00 +0000 | [diff] [blame] | 187 | } |
| 188 | |
Jonathan Coe | 5d304b2 | 2016-07-30 08:58:54 +0000 | [diff] [blame] | 189 | } // namespace cppcoreguidelines |
| 190 | } // namespace tidy |
| 191 | } // namespace clang |