Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -26,6 +26,7 @@ UnusedParametersCheck.cpp UnusedRAIICheck.cpp UniqueptrResetReleaseCheck.cpp + UserDefinedCopyWithoutAssignmentCheck.cpp VirtualNearMissCheck.cpp LINK_LIBS Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -34,6 +34,7 @@ #include "UnusedAliasDeclsCheck.h" #include "UnusedParametersCheck.h" #include "UnusedRAIICheck.h" +#include "UserDefinedCopyWithoutAssignmentCheck.h" #include "VirtualNearMissCheck.h" namespace clang { @@ -88,6 +89,8 @@ CheckFactories.registerCheck( "misc-unused-parameters"); CheckFactories.registerCheck("misc-unused-raii"); + CheckFactories.registerCheck( + "misc-user-defined-copy-without-assignment"); CheckFactories.registerCheck( "misc-virtual-near-miss"); } Index: clang-tidy/misc/UserDefinedCopyWithoutAssignmentCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/UserDefinedCopyWithoutAssignmentCheck.h @@ -0,0 +1,38 @@ +//===--- UserDefinedCopyWithoutAssignmentCheck.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_MISC_USER_DEFINED_COPY_WITHOUT_ASSIGNMENT_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USER_DEFINED_COPY_WITHOUT_ASSIGNMENT_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// Finds user-defined copy-constructors but no assignement operator. +/// +/// MSVC 2015 will generate an assignment operator even if the user defines a +/// copy-constructor. This check finds classes with user-defined +/// copy-constructors but no assignement operator and (defensively) defines the +/// assignment operator to be `= delete`. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-user-defined-copy-without-assignment.html +class UserDefinedCopyWithoutAssignmentCheck : public ClangTidyCheck { public: + UserDefinedCopyWithoutAssignmentCheck(StringRef Name, ClangTidyContext + *Context) : ClangTidyCheck(Name, Context) {} void + registerMatchers(ast_matchers::MatchFinder *Finder) override; void + check(const ast_matchers::MatchFinder::MatchResult &Result) override; }; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USER_DEFINED_COPY_WITHOUT_ASSIGNMENT_H Index: clang-tidy/misc/UserDefinedCopyWithoutAssignmentCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/UserDefinedCopyWithoutAssignmentCheck.cpp @@ -0,0 +1,56 @@ +//===--- UserDefinedCopyWithoutAssignmentCheck.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 "UserDefinedCopyWithoutAssignmentCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { + +void UserDefinedCopyWithoutAssignmentCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + cxxRecordDecl(isDefinition(), + hasDescendant(cxxConstructorDecl(isCopyConstructor()).bind("cctor")), + unless(hasDescendant(cxxMethodDecl(isCopyAssignmentOperator())))), + this); +} + +void UserDefinedCopyWithoutAssignmentCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedDecl = Result.Nodes.getNodeAs("cctor"); + + if (MatchedDecl->isImplicit()) + return; + + StringRef ClassName = MatchedDecl->getParent()->getName(); + + DiagnosticBuilder Diag = diag(MatchedDecl->getLocation(), "class '%0' defines a copy-constructor but not an assignment operator") + << ClassName; + + SourceLocation CCtorEnd = Lexer::getLocForEndOfToken(MatchedDecl->getLocEnd(), 0, + *Result.SourceManager, + Result.Context->getLangOpts()); + CCtorEnd = CCtorEnd.getLocWithOffset(1); + if (CCtorEnd.isInvalid()) + return; + + std::string S; + llvm::raw_string_ostream Insertion(S); + Insertion << "\n" << ClassName << "& operator = (const " << ClassName << "&) = delete;"; + + Diag << FixItHint::CreateInsertion(CCtorEnd, Insertion.str()); +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -65,6 +65,7 @@ misc-unused-alias-decls misc-unused-parameters misc-unused-raii + misc-user-defined-copy-without-assignment misc-virtual-near-miss modernize-loop-convert modernize-make-unique Index: docs/clang-tidy/checks/misc-user-defined-copy-without-assignment.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-user-defined-copy-without-assignment.rst @@ -0,0 +1,26 @@ +.. title:: clang-tidy - misc-user-defined-copy-without-assignment + +misc-user-defined-copy-without-assignment +========================================= + +MSVC 2015 will generate an assignment operator even if the user defines a copy constructor. +This check finds classes with a user-defined (including deleted) +copy-constructor but no assignment operator. + + .. code:: c++ + class A { + A(const A&); + }; + +Will be matched and fixed to delete the assignment operator: + + .. code:: c++ + class A { + A(const A&); + A& operator = (const A&) = delete; + }; + +The fix is defensive. Incorrect compiler-generated assignement can cause +unexpected behaviour. An explicitly deleted assigneemnt operator will cause a +compiler error if it is used. + Index: test/clang-tidy/misc-user-defined-copy-without-assignment.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-user-defined-copy-without-assignment.cpp @@ -0,0 +1,38 @@ +// RUN: %check_clang_tidy %s misc-user-defined-copy-without-assignment %t +class A { + A(const A&); +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: class 'A' defines a copy-constructor but not an assignment operator [misc-user-defined-copy-without-assignment] +}; + +// CHECK-FIXES: class A { +// CHECK-FIXES-NEXT: A(const A&); +// CHECK-FIXES-NEXT: A& operator = (const A&) = delete; +// CHECK-FIXES-NEXT: // +// CHECK-FIXES-NEXT: }; + +class B { + B(const B&); + B& operator = (const B&); +}; + +class C { + C(const C&) = default; +// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: class 'C' defines a copy-constructor but not an assignment operator [misc-user-defined-copy-without-assignment] +}; + +// CHECK-FIXES: class C { +// CHECK-FIXES-NEXT: C(const C&) = default; +// CHECK-FIXES-NEXT: C& operator = (const C&) = delete; +// CHECK-FIXES-NEXT: // +// CHECK-FIXES-NEXT: }; + +class D { + D(const D&); + D& operator = (const D&) = default; +}; + +class E { + E(const E&); + E& operator = (const E&) = delete; +}; +