Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -8,6 +8,7 @@ InaccurateEraseCheck.cpp InefficientAlgorithmCheck.cpp MiscTidyModule.cpp + NoexceptMoveCtorsCheck.cpp StaticAssertCheck.cpp SwappedArgumentsCheck.cpp UndelegatedConstructor.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -16,6 +16,7 @@ #include "BoolPointerImplicitConversionCheck.h" #include "InaccurateEraseCheck.h" #include "InefficientAlgorithmCheck.h" +#include "NoexceptMoveCtorsCheck.h" #include "StaticAssertCheck.h" #include "SwappedArgumentsCheck.h" #include "UndelegatedConstructor.h" @@ -41,6 +42,8 @@ "misc-inaccurate-erase"); CheckFactories.registerCheck( "misc-inefficient-algorithm"); + CheckFactories.registerCheck( + "misc-noexcept-move-ctors"); CheckFactories.registerCheck( "misc-static-assert"); CheckFactories.registerCheck( Index: clang-tidy/misc/NoexceptMoveCtorsCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/NoexceptMoveCtorsCheck.h @@ -0,0 +1,37 @@ +//===--- NoexceptMoveCtorsCheck.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_NOEXCEPT_MOVE_CTORS_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NOEXCEPT_MOVE_CTORS_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +/// \brief The check flags move constructors and assignment operators not marked +/// with \c noexcept or marked with \c noexcept(expr) where \c expr evaluates to +/// \c false (but is not a \c false literal itself). +/// +/// Move constructors of all the types used with STL containers, for example, +/// need to be declared \c noexcept. Otherwise STL will choose copy constructors +/// instead. The same is valid for move assignment operations. +class NoexceptMoveCtorsCheck : public ClangTidyCheck { +public: + NoexceptMoveCtorsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_NOEXCEPT_MOVE_CTORS_H + Index: clang-tidy/misc/NoexceptMoveCtorsCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/NoexceptMoveCtorsCheck.cpp @@ -0,0 +1,65 @@ +//===--- NoexceptMoveCtorsCheck.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 "NoexceptMoveCtorsCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { + +void NoexceptMoveCtorsCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + methodDecl(anyOf(constructorDecl(), hasOverloadedOperatorName("="))) + .bind("decl"), + this); +} + +void NoexceptMoveCtorsCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *Decl = Result.Nodes.getNodeAs("decl")) { + StringRef MethodType = "assignment operator"; + if (const auto *Ctor = dyn_cast(Decl)) { + if (!Ctor->isMoveConstructor()) + return; + MethodType = "constructor"; + } else if (!Decl->isMoveAssignmentOperator()) { + return; + } + + const auto *ProtoType = Decl->getType()->getAs(); + switch(ProtoType->getNoexceptSpec(*Result.Context)) { + case FunctionProtoType::NR_NoNoexcept: + diag(Decl->getLocation(), "move %0s should be marked noexcept") + << MethodType; + // FIXME: Add a fixit. + break; + case FunctionProtoType::NR_Throw: + // Don't complain about nothrow(false), but complain on nothrow(expr) + // where expr evaluates to false. + if (const Expr *E = ProtoType->getNoexceptExpr()) { + if (isa(E)) + break; + diag(E->getExprLoc(), + "noexcept specifier on the move %0 evaluates to 'false'") + << MethodType; + } + break; + case FunctionProtoType::NR_Nothrow: + case FunctionProtoType::NR_Dependent: + case FunctionProtoType::NR_BadNoexcept: + break; + } + } +} + +} // namespace tidy +} // namespace clang + Index: test/clang-tidy/misc-noexcept-move-ctors.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-noexcept-move-ctors.cpp @@ -0,0 +1,37 @@ +// RUN: $(dirname %s)/check_clang_tidy.sh %s misc-noexcept-move-ctors %t +// REQUIRES: shell + +class A { + A(A &&); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: move constructors should be marked noexcept [misc-noexcept-move-ctors] + A &operator=(A &&); + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: move assignment operators should +}; + +struct B { + static constexpr bool kFalse = false; + B(B &&) noexcept(kFalse); + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: noexcept specifier on the move constructor evaluates to 'false' [misc-noexcept-move-ctors] +}; + +class OK1 { + public: + OK1(); + OK1(const OK1 &); + OK1(OK1&&) noexcept; + OK1 &operator=(OK1 &&) noexcept; + void f(); + void g() noexcept; +}; + +class OK2 { + static constexpr bool kTrue = true; + +public: + OK2(OK2 &&) noexcept(true) {} + OK2 &operator=(OK2 &&) noexcept(kTrue) { return *this; } +}; + +struct OK3 { + OK3(OK3 &&) noexcept(false) {} +};