Index: clang-tools-extra/trunk/clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/readability/CMakeLists.txt +++ clang-tools-extra/trunk/clang-tidy/readability/CMakeLists.txt @@ -4,6 +4,7 @@ AvoidConstParamsInDecls.cpp BracesAroundStatementsCheck.cpp ContainerSizeEmptyCheck.cpp + DeletedDefaultCheck.cpp ElseAfterReturnCheck.cpp FunctionSizeCheck.cpp IdentifierNamingCheck.cpp Index: clang-tools-extra/trunk/clang-tidy/readability/DeletedDefaultCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/readability/DeletedDefaultCheck.h +++ clang-tools-extra/trunk/clang-tidy/readability/DeletedDefaultCheck.h @@ -0,0 +1,36 @@ +//===--- DeletedDefaultCheck.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_READABILITY_DELETED_DEFAULT_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETED_DEFAULT_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace readability { + +/// Checks when a constructor or an assignment operator is marked as '= default' +/// but is actually deleted by the compiler. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability-deleted-default.html +class DeletedDefaultCheck : public ClangTidyCheck { +public: + DeletedDefaultCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_DELETED_DEFAULT_H Index: clang-tools-extra/trunk/clang-tidy/readability/DeletedDefaultCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/readability/DeletedDefaultCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/readability/DeletedDefaultCheck.cpp @@ -0,0 +1,69 @@ +//===--- DeletedDefaultCheck.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 "DeletedDefaultCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace readability { + +void DeletedDefaultCheck::registerMatchers(MatchFinder *Finder) { + // We match constructors/assignment operators that are: + // - explicitly marked '= default' + // - actually deleted + // - not in template instantiation. + // We bind the declaration to "method-decl" and also to "constructor" when + // it is a constructor. + + Finder->addMatcher( + cxxMethodDecl(anyOf(cxxConstructorDecl().bind("constructor"), + isCopyAssignmentOperator(), + isMoveAssignmentOperator()), + isDefaulted(), unless(isImplicit()), isDeleted(), + unless(isInstantiated())) + .bind("method-decl"), + this); +} + +void DeletedDefaultCheck::check(const MatchFinder::MatchResult &Result) { + const StringRef Message = "%0 is explicitly defaulted but implicitly " + "deleted, probably because %1; definition can " + "either be removed or explicitly deleted"; + if (const auto *Constructor = + Result.Nodes.getNodeAs("constructor")) { + auto Diag = diag(Constructor->getLocStart(), Message); + if (Constructor->isDefaultConstructor()) { + Diag << "default constructor" + << "a non-static data member or a base class is lacking a default " + "constructor"; + } else if (Constructor->isCopyConstructor()) { + Diag << "copy constructor" + << "a non-static data member or a base class is not copyable"; + } else if (Constructor->isMoveConstructor()) { + Diag << "move constructor" + << "a non-static data member or a base class is neither copyable " + "nor movable"; + } + } else if (const auto *Assignment = + Result.Nodes.getNodeAs("method-decl")) { + diag(Assignment->getLocStart(), Message) + << (Assignment->isCopyAssignmentOperator() ? "copy assignment operator" + : "move assignment operator") + << "a base class or a non-static data member is not assignable, e.g. " + "because the latter is marked 'const'"; + } +} + +} // namespace readability +} // namespace tidy +} // namespace clang Index: clang-tools-extra/trunk/clang-tidy/readability/ReadabilityTidyModule.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/readability/ReadabilityTidyModule.cpp +++ clang-tools-extra/trunk/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -13,6 +13,7 @@ #include "AvoidConstParamsInDecls.h" #include "BracesAroundStatementsCheck.h" #include "ContainerSizeEmptyCheck.h" +#include "DeletedDefaultCheck.h" #include "ElseAfterReturnCheck.h" #include "FunctionSizeCheck.h" #include "IdentifierNamingCheck.h" @@ -40,6 +41,8 @@ "readability-braces-around-statements"); CheckFactories.registerCheck( "readability-container-size-empty"); + CheckFactories.registerCheck( + "readability-deleted-default"); CheckFactories.registerCheck( "readability-else-after-return"); CheckFactories.registerCheck( Index: clang-tools-extra/trunk/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/trunk/docs/ReleaseNotes.rst +++ clang-tools-extra/trunk/docs/ReleaseNotes.rst @@ -154,6 +154,12 @@ Warns about top-level const parameters in function declarations. +- New `readability-deleted-default + `_ check + + Warns about defaulted constructors and assignment operators that are actually + deleted. + - New `readability-redundant-control-flow `_ check Index: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst @@ -98,6 +98,7 @@ readability-avoid-const-params-in-decls readability-braces-around-statements readability-container-size-empty + readability-deleted-default readability-else-after-return readability-function-size readability-identifier-naming Index: clang-tools-extra/trunk/docs/clang-tidy/checks/readability-deleted-default.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/readability-deleted-default.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/readability-deleted-default.rst @@ -0,0 +1,21 @@ +.. title:: clang-tidy - readability-deleted-default + +readability-deleted-default +=========================== + +Checks that constructors and assignment operators marked as ``= default`` are +not actually deleted by the compiler. + +.. code:: c++ + class Example { + public: + // This constructor is deleted because I is missing a default value. + Example() = default; + // This is fine. + Example(const Example& Other) = default; + // This operator is deleted because I cannot be assigned (it is const). + Example& operator=(const Example& Other) = default; + private: + const int I; + }; + Index: clang-tools-extra/trunk/test/clang-tidy/readability-deleted-default.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/readability-deleted-default.cpp +++ clang-tools-extra/trunk/test/clang-tidy/readability-deleted-default.cpp @@ -0,0 +1,127 @@ +// RUN: %check_clang_tidy %s readability-deleted-default %t + +class NoDefault { +public: + NoDefault() = delete; + NoDefault(NoDefault &&Other) = delete; + NoDefault(const NoDefault &Other) = delete; +}; + +class MissingEverything { +public: + MissingEverything() = default; + // CHECK-MESSAGES: warning: default constructor is explicitly defaulted but implicitly deleted, probably because a non-static data member or a base class is lacking a default constructor; definition can either be removed or explicitly deleted [readability-deleted-default] + MissingEverything(MissingEverything &&Other) = default; + // CHECK-MESSAGES: warning: move constructor is explicitly defaulted but implicitly deleted, probably because a non-static data member or a base class is neither copyable nor movable; definition can either be removed or explicitly deleted [readability-deleted-default] + MissingEverything(const MissingEverything &Other) = default; + // CHECK-MESSAGES: warning: copy constructor is explicitly defaulted but implicitly deleted, probably because a non-static data member or a base class is not copyable; definition can either be removed or explicitly deleted [readability-deleted-default] + MissingEverything &operator=(MissingEverything &&Other) = default; + // CHECK-MESSAGES: warning: move assignment operator is explicitly defaulted but implicitly deleted, probably because a base class or a non-static data member is not assignable, e.g. because the latter is marked 'const'; definition can either be removed or explicitly deleted [readability-deleted-default] + MissingEverything &operator=(const MissingEverything &Other) = default; + // CHECK-MESSAGES: warning: copy assignment operator is explicitly defaulted but implicitly deleted, probably because a base class or a non-static data member is not assignable, e.g. because the latter is marked 'const'; definition can either be removed or explicitly deleted [readability-deleted-default] + +private: + NoDefault ND; +}; + +class NotAssignable { +public: + NotAssignable(NotAssignable &&Other) = default; + NotAssignable(const NotAssignable &Other) = default; + NotAssignable &operator=(NotAssignable &&Other) = default; + // CHECK-MESSAGES: warning: move assignment operator is explicitly defaulted but implicitly deleted + NotAssignable &operator=(const NotAssignable &Other) = default; + // CHECK-MESSAGES: warning: copy assignment operator is explicitly defaulted but implicitly deleted + +private: + const int I = 0; +}; + +class Movable { +public: + Movable() = default; + Movable(Movable &&Other) = default; + Movable(const Movable &Other) = delete; + Movable &operator=(Movable &&Other) = default; + Movable &operator=(const Movable &Other) = delete; +}; + +class NotCopyable { +public: + NotCopyable(NotCopyable &&Other) = default; + NotCopyable(const NotCopyable &Other) = default; + // CHECK-MESSAGES: warning: copy constructor is explicitly defaulted but implicitly deleted + NotCopyable &operator=(NotCopyable &&Other) = default; + NotCopyable &operator=(const NotCopyable &Other) = default; + // CHECK-MESSAGES: warning: copy assignment operator is explicitly defaulted but implicitly deleted +private: + Movable M; +}; + +template class Templated { +public: + // No warning here, it is a templated class. + Templated() = default; + Templated(Templated &&Other) = default; + Templated(const Templated &Other) = default; + Templated &operator=(Templated &&Other) = default; + Templated &operator=(const Templated &Other) = default; + + class InnerTemplated { + public: + // This class is not in itself templated, but we still don't have warning. + InnerTemplated() = default; + InnerTemplated(InnerTemplated &&Other) = default; + InnerTemplated(const InnerTemplated &Other) = default; + InnerTemplated &operator=(InnerTemplated &&Other) = default; + InnerTemplated &operator=(const InnerTemplated &Other) = default; + + private: + T TVar; + }; + + class InnerNotTemplated { + public: + // This one could technically have warnings, but currently doesn't. + InnerNotTemplated() = default; + InnerNotTemplated(InnerNotTemplated &&Other) = default; + InnerNotTemplated(const InnerNotTemplated &Other) = default; + InnerNotTemplated &operator=(InnerNotTemplated &&Other) = default; + InnerNotTemplated &operator=(const InnerNotTemplated &Other) = default; + + private: + int I; + }; + +private: + const T TVar{}; +}; + +int FunctionWithInnerClass() { + class InnerNotAssignable { + public: + InnerNotAssignable &operator=(InnerNotAssignable &&Other) = default; + // CHECK-MESSAGES: warning: move assignment operator is explicitly defaulted but implicitly deleted + private: + const int I = 0; + }; + return 1; +}; + +template +int TemplateFunctionWithInnerClass() { + class InnerNotAssignable { + public: + InnerNotAssignable &operator=(InnerNotAssignable &&Other) = default; + private: + const T TVar{}; + }; + return 1; +}; + +void Foo() { + Templated V1; + Templated::InnerTemplated V2; + Templated::InnerNotTemplated V3; + TemplateFunctionWithInnerClass(); +}