blob: 5a487d54e8118f6af24f82a5f66acafde95f7976 [file] [log] [blame]
Jonathan Coe5d304b22016-07-30 08:58:54 +00001//===--- 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
19using namespace clang::ast_matchers;
20
21namespace clang {
22namespace tidy {
23namespace cppcoreguidelines {
24
25void 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
Jonathan Coe77ec2632016-08-02 21:18:37 +000046static llvm::StringRef
47toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K) {
Jonathan Coe5d304b22016-07-30 08:58:54 +000048 switch (K) {
Jonathan Coe77ec2632016-08-02 21:18:37 +000049 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::Destructor:
Jonathan Coe5d304b22016-07-30 08:58:54 +000050 return "a destructor";
Jonathan Coe77ec2632016-08-02 21:18:37 +000051 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyConstructor:
Jonathan Coe5d304b22016-07-30 08:58:54 +000052 return "a copy constructor";
Jonathan Coe77ec2632016-08-02 21:18:37 +000053 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::CopyAssignment:
Jonathan Coe5d304b22016-07-30 08:58:54 +000054 return "a copy assignment operator";
Jonathan Coe77ec2632016-08-02 21:18:37 +000055 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveConstructor:
Jonathan Coe5d304b22016-07-30 08:58:54 +000056 return "a move constructor";
Jonathan Coe77ec2632016-08-02 21:18:37 +000057 case SpecialMemberFunctionsCheck::SpecialMemberFunctionKind::MoveAssignment:
Jonathan Coe5d304b22016-07-30 08:58:54 +000058 return "a move assignment operator";
59 }
60 llvm_unreachable("Unhandled SpecialMemberFunctionKind");
61}
62
Jonathan Coe77ec2632016-08-02 21:18:37 +000063static std::string
64join(ArrayRef<SpecialMemberFunctionsCheck::SpecialMemberFunctionKind> SMFS,
65 llvm::StringRef AndOr) {
Jonathan Coe5d304b22016-07-30 08:58:54 +000066
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
83void 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))
Jonathan Coe77ec2632016-08-02 21:18:37 +0000101 ClassWithSpecialMembers[ID].insert(KV.second);
Jonathan Coe5d304b22016-07-30 08:58:54 +0000102}
103
104void 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) {
Jonathan Coe77ec2632016-08-02 21:18:37 +0000116 const auto &DefinedSpecialMembers = C.second;
Jonathan Coe5d304b22016-07-30 08:58:54 +0000117
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")
Jonathan Coe77ec2632016-08-02 21:18:37 +0000128 << C.first.second << join(DefinedSpecialMembers.getArrayRef(), " and ")
Jonathan Coe5d304b22016-07-30 08:58:54 +0000129 << join(UndefinedSpecialMembers, " or ");
130 }
131}
132} // namespace cppcoreguidelines
133} // namespace tidy
134} // namespace clang