Index: clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tidy/modernize/CMakeLists.txt +++ clang-tidy/modernize/CMakeLists.txt @@ -20,6 +20,7 @@ UseEmplaceCheck.cpp UseEqualsDefaultCheck.cpp UseEqualsDeleteCheck.cpp + UseNoexceptCheck.cpp UseNullptrCheck.cpp UseOverrideCheck.cpp UseTransparentFunctorsCheck.cpp Index: clang-tidy/modernize/ModernizeTidyModule.cpp =================================================================== --- clang-tidy/modernize/ModernizeTidyModule.cpp +++ clang-tidy/modernize/ModernizeTidyModule.cpp @@ -26,6 +26,7 @@ #include "UseEmplaceCheck.h" #include "UseEqualsDefaultCheck.h" #include "UseEqualsDeleteCheck.h" +#include "UseNoexceptCheck.h" #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" #include "UseTransparentFunctorsCheck.h" @@ -63,6 +64,7 @@ CheckFactories.registerCheck("modernize-use-equals-default"); CheckFactories.registerCheck( "modernize-use-equals-delete"); + CheckFactories.registerCheck("modernize-use-noexcept"); CheckFactories.registerCheck("modernize-use-nullptr"); CheckFactories.registerCheck("modernize-use-override"); CheckFactories.registerCheck( Index: clang-tidy/modernize/UseNoexceptCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/UseNoexceptCheck.h @@ -0,0 +1,50 @@ +//===--- UseNoexceptCheck.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_MODERNIZE_USE_NOEXCEPT_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NOEXCEPT_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +using CandidateSet = llvm::StringSet; + +/// \brief Replace dynamic exception specifications, with +/// `noexcept` (or user-defined macro) or `noexcept(false)`. +/// \code +/// void foo() throw(); +/// void bar() throw(int); +/// \endcode +/// Is converted to: +/// \code +/// void foo() noexcept; +// void bar() noexcept(false); +/// \endcode +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-noexcept.html +class UseNoexceptCheck : public ClangTidyCheck { +public: + UseNoexceptCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + const std::string DefaultReplacement; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NOEXCEPT_H Index: clang-tidy/modernize/UseNoexceptCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/modernize/UseNoexceptCheck.cpp @@ -0,0 +1,102 @@ +//===--- UseNoexceptCheck.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 "UseNoexceptCheck.h" +#include "clang/AST/ASTContext.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +static StringRef +makeDynamicExceptionString(const SourceManager &SM, + const CharSourceRange &FileMoveRange) { + std::pair BeginInfo = + SM.getDecomposedLoc(FileMoveRange.getBegin()); + std::pair EndInfo = + SM.getDecomposedLoc(FileMoveRange.getEnd()); + + // Add 1 which represents the size of the trailing ')'. + auto Len = EndInfo.second - BeginInfo.second + 1; + return StringRef(SM.getCharacterData(FileMoveRange.getBegin()), Len); +} + +UseNoexceptCheck::UseNoexceptCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + DefaultReplacement(Options.get("ReplacementString", "noexcept")) {} + +void UseNoexceptCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "ReplacementString", DefaultReplacement); +} + +void UseNoexceptCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + + Finder->addMatcher( + functionDecl(hasTypeLoc(loc(functionProtoType(hasDynamicExceptionSpec())))) + .bind("funcDecl"), + this); + Finder->addMatcher( + parmVarDecl(hasType(pointerType(pointee(parenType( + innerType(functionProtoType(hasDynamicExceptionSpec()))))))) + .bind("parmVarDecl"), + this); +} + +void UseNoexceptCheck::check(const MatchFinder::MatchResult &Result) { + bool IsNoThrow = false; + SourceRange Range; + + if (const auto *FuncDecl = Result.Nodes.getNodeAs("funcDecl")) { + if (auto const *FnTy = FuncDecl->getType()->getAs()) + IsNoThrow = FnTy->isNothrow(*Result.Context); + if (const auto *Info = FuncDecl->getTypeSourceInfo()) + Range = + Info->getTypeLoc().castAs().getExceptionSpecRange(); + } + if (const auto *ParmDecl = + Result.Nodes.getNodeAs("parmVarDecl")) { + if (const auto *PtrTy = ParmDecl->getType()->getAs()) + if (const auto *FnTy = + PtrTy->getPointeeType()->getAs()) + IsNoThrow = FnTy->isNothrow(*Result.Context); + + if (const TypeSourceInfo *TSI = ParmDecl->getTypeSourceInfo()) { + TypeLoc TL = TSI->getTypeLoc(); + if (TL.getType()->isPointerType()) { + TL = TL.getNextTypeLoc().IgnoreParens(); + if (auto FPTL = TL.getAs()) { + Range = FPTL.getExceptionSpecRange(); + } + } + } + } + if (!Range.isValid()) + return; + + StringRef SpecificReplacement = DefaultReplacement; + FixItHint FixIt; + if (!IsNoThrow) + SpecificReplacement = "noexcept(false)"; + if (IsNoThrow || DefaultReplacement == "noexcept") + FixIt = FixItHint::CreateReplacement(CharSourceRange(Range, true), + SpecificReplacement); + + diag(Range.getBegin(), "found dynamic exception specification '%0'") + << makeDynamicExceptionString(*Result.SourceManager, + CharSourceRange(Range, true)) + << FixIt; +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -125,6 +125,11 @@ Adds ``= delete`` to unimplemented private special member functions. + - New `modernize-use-noexcept + `_ check + + Replaces dynamic exception specifications with ``noexcept`` or a user defined macro. + - New `modernize-use-transparent-functors `_ check Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -116,6 +116,7 @@ modernize-use-emplace modernize-use-equals-default modernize-use-equals-delete + modernize-use-noexcept modernize-use-nullptr modernize-use-override modernize-use-transparent-functors Index: docs/clang-tidy/checks/modernize-use-noexcept.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/modernize-use-noexcept.rst @@ -0,0 +1,55 @@ +.. title:: clang-tidy - modernize-use-noexcept + +modernize-use-noexcept +====================== + +The check converts dynamic exception specifications, e.g., ``throw()``, +``throw([,...])``, or ``throw(...)`` to ``noexcept``, ``noexcept(false)``, +or a user defined macro. + +Example +------- + +.. code-block:: c++ + + void foo() throw(); + void bar() throw(int) {} + +transforms to: + +.. code-block:: c++ + + void foo() noexcept; + void bar() noexcept(false) {} + + +User defined macros +------------------- + +By default this check will only replace ``throw()`` with ``noexcept``, +and ``throw([,...])`` or ``throw(...)`` with +``noexcept(false)``. Additionally, users can also use +:option:`ReplacementString` to specify a macro to use instead of +``noexcept``. This is useful when maintaining source code that must +be compiled with older compilers that don't support the ``noexcept`` +keyword. Users can define the macro to be ``noexcept`` or ``throw()`` +depending on whether or not noexcept is supported. + +Please note that since ``throw(int)`` is equivelent to +``noexcept(false)`` not ``noexcept``, this check will detect, but not +provide a FixItHint in that case. + +Example +^^^^^^^ + +.. code-block:: c++ + + void foo() throw() {} + +transforms to: + +.. code-block:: c++ + + void foo() NOEXCEPT {} + +if the :option:`ReplacementString` option is set to `NOEXCEPT`. Index: test/clang-tidy/modernize-use-noexcept-macro.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-noexcept-macro.cpp @@ -0,0 +1,24 @@ +// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-noexcept.ReplacementString, value: 'NOEXCEPT'}]}" \ +// RUN: -- -std=c++11 + +// Example definition of NOEXCEPT -- simplified test to see if noexcept is supported. +#if (__has_feature(cxx_noexcept)) +#define NOEXCEPT noexcept +#else +#define NOEXCEPT throw() +#endif + +void bar() throw() {} +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept] +// CHECK-FIXES: void bar() NOEXCEPT {} + +// Should not trigger a FixItHint, since macros only support noexcept, and this +// case throws. +class A {}; +class B {}; +void foobar() throw(A, B); +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: found dynamic exception specification 'throw(A, B)' [modernize-use-noexcept] + +// Should not trigger a replacement. +void foo() noexcept(true); Index: test/clang-tidy/modernize-use-noexcept.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-noexcept.cpp @@ -0,0 +1,56 @@ +// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \ +// RUN: -- -std=c++11 + +class A {}; +class B {}; + +void foo() throw(); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept] +// CHECK-FIXES: void foo() noexcept; + +void bar() throw(...); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: found dynamic exception specification 'throw(...)' [modernize-use-noexcept] +// CHECK-FIXES: void bar() noexcept(false); + +void k() throw(int(int)); +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: found dynamic exception specification 'throw(int(int))' [modernize-use-noexcept] +// CHECK-FIXES: void k() noexcept(false); + +void foobar() throw(A, B) +{} +// CHECK-MESSAGES: :[[@LINE-2]]:15: warning: found dynamic exception specification 'throw(A, B)' [modernize-use-noexcept] +// CHECK-FIXES: void foobar() noexcept(false) + +void baz(int = (throw A(), 0)) throw(A, B) {} +// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: found dynamic exception specification 'throw(A, B)' [modernize-use-noexcept] +// CHECK-FIXES: void baz(int = (throw A(), 0)) noexcept(false) {} + +void g(void (*fp)(void) throw()); +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept] +// CHECK-FIXES: void g(void (*fp)(void) noexcept); + +void f(void (*fp)(void) throw(int)) throw(char); +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: found dynamic exception specification 'throw(int)' [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: found dynamic exception specification 'throw(char)' [modernize-use-noexcept] +// CHECK-FIXES: void f(void (*fp)(void) noexcept(false)) noexcept(false); + +void j() throw(int(int) throw(void(void) throw(int))); +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: found dynamic exception specification 'throw(int(int) throw(void(void) throw(int)))' [modernize-use-noexcept] +// CHECK-FIXES: void j() noexcept(false); + +class Y { + Y() throw() = default; +}; +// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: found dynamic exception specification 'throw()' [modernize-use-noexcept] +// CHECK-FIXES: Y() noexcept = default; + +// Should not trigger a replacement. +void titi() noexcept {} +void toto() noexcept(true) {} + +// Should not trigger a replacement. +void bad() +#if !__has_feature(cxx_noexcept) + throw() +#endif + ;