Index: clang-tidy/cppcoreguidelines/CMakeLists.txt =================================================================== --- clang-tidy/cppcoreguidelines/CMakeLists.txt +++ clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -13,6 +13,7 @@ ProTypeStaticCastDowncastCheck.cpp ProTypeUnionAccessCheck.cpp ProTypeVarargCheck.cpp + RuleOfFiveAndZeroCheck.cpp LINK_LIBS clangAST Index: clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp =================================================================== --- clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -22,6 +22,7 @@ #include "ProTypeStaticCastDowncastCheck.h" #include "ProTypeUnionAccessCheck.h" #include "ProTypeVarargCheck.h" +#include "RuleOfFiveAndZeroCheck.h" namespace clang { namespace tidy { @@ -53,6 +54,8 @@ "cppcoreguidelines-pro-type-union-access"); CheckFactories.registerCheck( "cppcoreguidelines-pro-type-vararg"); + CheckFactories.registerCheck( + "cppcoreguidelines-rule-of-five-and-zero"); CheckFactories.registerCheck( "cppcoreguidelines-c-copy-assignment-signature"); } Index: clang-tidy/cppcoreguidelines/RuleOfFiveAndZeroCheck.h =================================================================== --- /dev/null +++ clang-tidy/cppcoreguidelines/RuleOfFiveAndZeroCheck.h @@ -0,0 +1,41 @@ +//===--- RuleOfFiveAndZeroCheck.h - clang-tidy-------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_RULE_OF_FIVE_AND_ZERO_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_RULE_OF_FIVE_AND_ZERO_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +/// Checks for classes where some, but not all, of the special member functions +/// are defined. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-rule-of-five-and-zero.html +class RuleOfFiveAndZeroCheck : public ClangTidyCheck { +public: + RuleOfFiveAndZeroCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + void + checkRuleOfFiveViolation(const ast_matchers::MatchFinder::MatchResult &Result, + llvm::StringRef Match, llvm::StringRef FunctionName); +}; + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_RULE_OF_FIVE_AND_ZERO_H Index: clang-tidy/cppcoreguidelines/RuleOfFiveAndZeroCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/cppcoreguidelines/RuleOfFiveAndZeroCheck.cpp @@ -0,0 +1,82 @@ +//===--- RuleOfFiveAndZeroCheck.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 "RuleOfFiveAndZeroCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +void RuleOfFiveAndZeroCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus11) + return; + + auto DefinesDestructor = has(cxxDestructorDecl(unless(isImplicit()))); + auto DefinesCopyConstructor = + has(cxxConstructorDecl(isCopyConstructor(), unless(isImplicit()))); + auto DefinesCopyAssignment = + has(cxxMethodDecl(isCopyAssignmentOperator(), unless(isImplicit()))); + auto DefinesMoveConstructor = + has(cxxConstructorDecl(isMoveConstructor(), unless(isImplicit()))); + auto DefinesMoveAssignment = + has(cxxMethodDecl(isMoveAssignmentOperator(), unless(isImplicit()))); + auto DefinesAllSpecialMembers = + allOf(DefinesDestructor, DefinesCopyAssignment, DefinesCopyAssignment, + DefinesMoveConstructor, DefinesMoveAssignment); + + Finder->addMatcher( + cxxRecordDecl(allOf(DefinesDestructor, unless(DefinesAllSpecialMembers))) + .bind("dtor"), + this); + Finder->addMatcher(cxxRecordDecl(allOf(DefinesCopyConstructor, + unless(DefinesAllSpecialMembers))) + .bind("copy-ctor"), + this); + Finder->addMatcher(cxxRecordDecl(allOf(DefinesCopyAssignment, + unless(DefinesAllSpecialMembers))) + .bind("copy-assign"), + this); + Finder->addMatcher(cxxRecordDecl(allOf(DefinesMoveConstructor, + unless(DefinesAllSpecialMembers))) + .bind("move-ctor"), + this); + Finder->addMatcher(cxxRecordDecl(allOf(DefinesMoveAssignment, + unless(DefinesAllSpecialMembers))) + .bind("move-assign"), + this); +} + +void RuleOfFiveAndZeroCheck::checkRuleOfFiveViolation( + const MatchFinder::MatchResult &Result, llvm::StringRef Match, + llvm::StringRef FunctionName) { + if (const auto *MatchedDecl = Result.Nodes.getNodeAs(Match)) { + + diag(MatchedDecl->getLocation(), "class %0 defines a %1 but does not " + "define or delete all other special " + "member functions") + << MatchedDecl << FunctionName; + } +} + +void RuleOfFiveAndZeroCheck::check(const MatchFinder::MatchResult &Result) { + + checkRuleOfFiveViolation(Result, "dtor", "destructor"); + checkRuleOfFiveViolation(Result, "copy-ctor", "copy constructor"); + checkRuleOfFiveViolation(Result, "copy-assign", "copy assignment operator"); + checkRuleOfFiveViolation(Result, "move-ctor", "move constructor"); + checkRuleOfFiveViolation(Result, "move-assign", "move assignment operator"); +} + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang Index: docs/clang-tidy/checks/cppcoreguidelines-rule-of-five-and-zero.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/cppcoreguidelines-rule-of-five-and-zero.rst @@ -0,0 +1,21 @@ +.. title:: clang-tidy - cppcoreguidelines-rule-of-five-and-zero + +cppcoreguidelines-rule-of-five-and-zero +======================================= + +The check finds classes where some but not all of the special member functions +are defined. + +By default the compiler defines a copy constructor, copy assignment operator, +move constructor, move assignment operator and destructor. The default can be +supressed by explciti user-definitions. The relationship between which +functions will be supressed by definitions of other functions is complicated +and it is advised that all five are defaulted or explicitly defined. + +Note that defining a function with ``=delete`` is considered to be a +definition. + +This rule is part of the "Constructors, assignments, and destructors" profile of the C++ Core +Guidelines, corresponding to rule C.21. See + +https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#c21-if-you-define-or-delete-any-default-operation-define-or-delete-them-all. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -29,6 +29,7 @@ cppcoreguidelines-pro-type-static-cast-downcast cppcoreguidelines-pro-type-union-access cppcoreguidelines-pro-type-vararg + cppcoreguidelines-rule-of-five-and-zero google-build-explicit-make-pair google-build-namespaces google-build-using-namespace Index: test/clang-tidy/cppcoreguidelines-rule-of-five-and-zero.cpp =================================================================== --- /dev/null +++ test/clang-tidy/cppcoreguidelines-rule-of-five-and-zero.cpp @@ -0,0 +1,44 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-rule-of-five-and-zero %t + +class DefinesDestructor { + ~DefinesDestructor(); +}; +// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesDestructor' defines a destructor but does not define or delete all other special member functions [cppcoreguidelines-rule-of-five-and-zero] + +class DefinesCopyConstructor { + DefinesCopyConstructor(const DefinesCopyConstructor &); +}; +// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyConstructor' defines a copy constructor but does not define or delete all other special member functions [cppcoreguidelines-rule-of-five-and-zero] +class DefinesCopyAssignment { + DefinesCopyAssignment &operator=(const DefinesCopyAssignment &); +}; +// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesCopyAssignment' defines a copy assignment operator but does not define or delete all other special member functions [cppcoreguidelines-rule-of-five-and-zero] + +class DefinesMoveConstructor { + DefinesMoveConstructor(DefinesMoveConstructor &&); +}; +// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveConstructor' defines a move constructor but does not define or delete all other special member functions [cppcoreguidelines-rule-of-five-and-zero] + +class DefinesMoveAssignment { + DefinesMoveAssignment &operator=(DefinesMoveAssignment &&); +}; +// CHECK-MESSAGES: [[@LINE-3]]:7: warning: class 'DefinesMoveAssignment' defines a move assignment operator but does not define or delete all other special member functions [cppcoreguidelines-rule-of-five-and-zero] + +class DefinesNothing { +}; + +class DefinesEverything { + DefinesEverything(const DefinesEverything &); + DefinesEverything &operator=(const DefinesEverything &); + DefinesEverything(DefinesEverything &&); + DefinesEverything &operator=(DefinesEverything &&); + ~DefinesEverything(); +}; + +class DeletesEverything { + DeletesEverything(const DeletesEverything &); + DeletesEverything &operator=(const DeletesEverything &); + DeletesEverything(DeletesEverything &&); + DeletesEverything &operator=(DeletesEverything &&); + ~DeletesEverything(); +};