Index: clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tidy/modernize/CMakeLists.txt +++ clang-tidy/modernize/CMakeLists.txt @@ -22,6 +22,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 @@ -28,6 +28,7 @@ #include "UseEmplaceCheck.h" #include "UseEqualsDefaultCheck.h" #include "UseEqualsDeleteCheck.h" +#include "UseNoexceptCheck.h" #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" #include "UseTransparentFunctorsCheck.h" @@ -69,6 +70,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,49 @@ +//===--- 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 { + +/// \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() ; +// 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 NoexceptMacro; + bool UseNoexceptFalse; +}; + +} // 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,114 @@ +//===--- 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" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +UseNoexceptCheck::UseNoexceptCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + NoexceptMacro(Options.get("ReplacementString", "")), + UseNoexceptFalse(Options.get("UseNoexceptFalse", true)) {} + +void UseNoexceptCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "ReplacementString", NoexceptMacro); + Options.store(Opts, "UseNoexceptFalse", UseNoexceptFalse); +} + +void UseNoexceptCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus11) + return; + + Finder->addMatcher( + functionDecl( + cxxMethodDecl( + hasTypeLoc(loc(functionProtoType(hasDynamicExceptionSpec()))), + anyOf(hasOverloadedOperatorName("delete[]"), + hasOverloadedOperatorName("delete"), cxxDestructorDecl())) + .bind("del-dtor")) + .bind("funcDecl"), + this); + + Finder->addMatcher( + functionDecl( + hasTypeLoc(loc(functionProtoType(hasDynamicExceptionSpec()))), + unless(anyOf(hasOverloadedOperatorName("delete[]"), + hasOverloadedOperatorName("delete"), + cxxDestructorDecl()))) + .bind("funcDecl"), + this); + + Finder->addMatcher( + parmVarDecl(anyOf(hasType(pointerType(pointee(parenType(innerType( + functionProtoType(hasDynamicExceptionSpec())))))), + hasType(memberPointerType(pointee(parenType(innerType( + functionProtoType(hasDynamicExceptionSpec())))))))) + .bind("parmVarDecl"), + this); +} + +void UseNoexceptCheck::check(const MatchFinder::MatchResult &Result) { + const FunctionProtoType *FnTy = nullptr; + bool DtorOrOperatorDel = false; + SourceRange Range; + + if (const auto *FuncDecl = Result.Nodes.getNodeAs("funcDecl")) { + DtorOrOperatorDel = Result.Nodes.getNodeAs("del-dtor"); + FnTy = FuncDecl->getType()->getAs(); + if (const auto *TSI = FuncDecl->getTypeSourceInfo()) + Range = + TSI->getTypeLoc().castAs().getExceptionSpecRange(); + } else if (const auto *ParmDecl = + Result.Nodes.getNodeAs("parmVarDecl")) { + FnTy = ParmDecl->getType() + ->getAs() + ->getPointeeType() + ->getAs(); + + if (const auto *TSI = ParmDecl->getTypeSourceInfo()) + Range = TSI->getTypeLoc() + .getNextTypeLoc() + .IgnoreParens() + .castAs() + .getExceptionSpecRange(); + } + CharSourceRange CRange = Lexer::makeFileCharRange( + CharSourceRange::getTokenRange(Range), *Result.SourceManager, + Result.Context->getLangOpts()); + + assert(FnTy && "FunctionProtoType is null."); + bool IsNoThrow = FnTy->isNothrow(*Result.Context); + StringRef ReplacementStr = + IsNoThrow + ? NoexceptMacro.empty() ? "noexcept" : NoexceptMacro.c_str() + : NoexceptMacro.empty() + ? (DtorOrOperatorDel || UseNoexceptFalse) ? "noexcept(false)" + : "" + : ""; + + FixItHint FixIt; + if ((IsNoThrow || NoexceptMacro.empty()) && CRange.isValid()) + FixIt = FixItHint::CreateReplacement(CRange, ReplacementStr); + + diag(Range.getBegin(), "dynamic exception specification '%0' is deprecated; " + "consider %select{using '%2'|removing it}1 instead") + << Lexer::getSourceText(CRange, *Result.SourceManager, + Result.Context->getLangOpts()) + << ReplacementStr.empty() << ReplacementStr << FixIt; +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -100,6 +100,11 @@ to remove user-defined make functions from ``push_back`` calls on containers of custom tuple-like types by providing `TupleTypes` and `TupleMakeFunctions`. + - New `modernize-use-noexcept + `_ check + + Replaces dynamic exception specifications with ``noexcept`` or a user defined macro. + - New `performance-inefficient-vector-operation `_ check Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -135,6 +135,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,90 @@ +.. title:: clang-tidy - modernize-use-noexcept + +modernize-use-noexcept +====================== + +This check replaces deprecated dynamic exception specifications with +the appropriate noexcept specification (introduced in C++11). By +default this check will replace ``throw()`` with ``noexcept``, +and ``throw([,...])`` or ``throw(...)`` with +``noexcept(false)``. + +Example +------- + +.. code-block:: c++ + + void foo() throw(); + void bar() throw(int) {} + +transforms to: + +.. code-block:: c++ + + void foo() noexcept; + void bar() noexcept(false) {} + +Options +------- + +.. option:: ReplacementString + +Users can use :option:`ReplacementString` to specify a macro to use +instead of ``noexcept``. This is useful when maintaining source code +that uses custom exception specification marking other than +``noexcept``. Fix-it hints will only be generated for non-throwing +specifications. + +Example +^^^^^^^ + +.. code-block:: c++ + + void bar() throw(int); + void foo() throw(); + +transforms to: + +.. code-block:: c++ + + void bar() throw(int); // No fix-it generated. + void foo() NOEXCEPT; + +if the :option:`ReplacementString` option is set to `NOEXCEPT`. + +.. option:: UseNoexceptFalse + +Enabled by default, disabling will generate fix-it hints that remove +throwing dynamic exception specs, e.g., ``throw()``, +completely without providing a replacement text, except for +destructors and delete operators that are ``noexcept(true)`` by +default. + +Example +^^^^^^^ + +.. code-block:: c++ + + void foo() throw(int) {} + + struct bar { + void foobar() throw(int); + void operator delete(void *ptr) throw(int); + void operator delete[](void *ptr) throw(int); + ~bar() throw(int); + } + +transforms to: + +.. code-block:: c++ + + void foo() {} + + struct bar { + void foobar(); + void operator delete(void *ptr) noexcept(false); + void operator delete[](void *ptr) noexcept(false); + ~bar() noexcept(false); + } + +if the :option:`UseNoexceptFalse` option is set to `0`. Index: test/clang-tidy/modernize-use-noexcept-macro.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-noexcept-macro.cpp @@ -0,0 +1,36 @@ +// 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: dynamic exception specification 'throw()' is deprecated; consider using 'NOEXCEPT' instead [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: dynamic exception specification 'throw(A, B)' is deprecated; consider removing it instead [modernize-use-noexcept] + +// Should not trigger a replacement. +void foo() noexcept(true); + +struct Z { + void operator delete(void *ptr) throw(); + void operator delete[](void *ptr) throw(int); + ~Z() throw(int) {} +}; +// CHECK-MESSAGES: :[[@LINE-4]]:35: warning: dynamic exception specification 'throw()' is deprecated; consider using 'NOEXCEPT' instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-4]]:37: warning: dynamic exception specification 'throw(int)' is deprecated; consider removing it instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-4]]:8: warning: dynamic exception specification 'throw(int)' is deprecated; consider removing it instead [modernize-use-noexcept] +// CHECK-FIXES: void operator delete(void *ptr) NOEXCEPT; +// CHECK-FIXES: void operator delete[](void *ptr) throw(int); +// CHECK-FIXES: ~Z() throw(int) {} Index: test/clang-tidy/modernize-use-noexcept-opt.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-noexcept-opt.cpp @@ -0,0 +1,88 @@ +// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-noexcept.UseNoexceptFalse, value: 0}]}" \ +// RUN: -- -std=c++11 + +class A {}; +class B {}; + +void foo() throw(); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-FIXES: void foo() noexcept; + +void bar() throw(...); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw(...)' is deprecated; consider removing it instead [modernize-use-noexcept] +// CHECK-FIXES: void bar() ; + +void k() throw(int(int)); +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: dynamic exception specification 'throw(int(int))' is deprecated; consider removing it instead [modernize-use-noexcept] +// CHECK-FIXES: void k() ; + +void foobar() throw(A, B) +{} +// CHECK-MESSAGES: :[[@LINE-2]]:15: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider removing it instead [modernize-use-noexcept] +// CHECK-FIXES: void foobar() + +void baz(int = (throw A(), 0)) throw(A, B) {} +// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider removing it instead [modernize-use-noexcept] +// CHECK-FIXES: void baz(int = (throw A(), 0)) {} + +void g(void (*fp)(void) throw()); +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [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: dynamic exception specification 'throw(int)' is deprecated; consider removing it instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: dynamic exception specification 'throw(char)' is deprecated; consider removing it instead [modernize-use-noexcept] +// CHECK-FIXES: void f(void (*fp)(void) ) ; + +#define THROW throw +void h(void (*fp)(void) THROW(int)) THROW(char); +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'THROW(int)' is deprecated; consider removing it instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: dynamic exception specification 'THROW(char)' is deprecated; consider removing it instead [modernize-use-noexcept] +// CHECK-FIXES: void h(void (*fp)(void) ) ; + +void j() throw(int(int) throw(void(void) throw(int))); +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: dynamic exception specification 'throw(int(int) throw(void(void) throw(int)))' is deprecated; consider removing it instead [modernize-use-noexcept] +// CHECK-FIXES: void j() ; + +class Y { + Y() throw() = default; +}; +// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-FIXES: Y() noexcept = default; + +struct Z { + void operator delete(void *ptr) throw(); + void operator delete[](void *ptr) throw(int); + ~Z() throw(int) {} +}; +// CHECK-MESSAGES: :[[@LINE-4]]:35: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-4]]:37: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-4]]:8: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] +// CHECK-FIXES: void operator delete(void *ptr) noexcept; +// CHECK-FIXES: void operator delete[](void *ptr) noexcept(false); +// CHECK-FIXES: ~Z() noexcept(false) {} + +struct S { + void f() throw(); +}; +void f(void (S::*)() throw()); +// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-FIXES: void f() noexcept; +// CHECK-FIXES: void f(void (S::*)() noexcept); + +typedef void (*fp)(void (*fp2)(int) throw()); +// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-FIXES: typedef void (*fp)(void (*fp2)(int) noexcept); + +// 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 + ; Index: test/clang-tidy/modernize-use-noexcept.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-noexcept.cpp @@ -0,0 +1,104 @@ +// 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: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-FIXES: void foo() noexcept; + +template +void foo() throw(); +void footest() { foo(); foo(); } +// CHECK-MESSAGES: :[[@LINE-2]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-FIXES: void foo() noexcept; + +void bar() throw(...); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw(...)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] +// CHECK-FIXES: void bar() noexcept(false); + +void k() throw(int(int)); +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: dynamic exception specification 'throw(int(int))' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] +// CHECK-FIXES: void k() noexcept(false); + +void foobar() throw(A, B) +{} +// CHECK-MESSAGES: :[[@LINE-2]]:15: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] +// CHECK-FIXES: void foobar() noexcept(false) + +void baz(int = (throw A(), 0)) throw(A, B) {} +// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: dynamic exception specification 'throw(A, B)' is deprecated; consider using 'noexcept(false)' instead [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: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [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: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: dynamic exception specification 'throw(char)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] +// CHECK-FIXES: void f(void (*fp)(void) noexcept(false)) noexcept(false); + +#define THROW throw +void h(void (*fp)(void) THROW(int)) THROW(char); +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: dynamic exception specification 'THROW(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-2]]:37: warning: dynamic exception specification 'THROW(char)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] +// CHECK-FIXES: void h(void (*fp)(void) noexcept(false)) noexcept(false); + +void j() throw(int(int) throw(void(void) throw(int))); +// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: dynamic exception specification 'throw(int(int) throw(void(void) throw(int)))' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] +// CHECK-FIXES: void j() noexcept(false); + +class Y { + Y() throw() = default; +}; +// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-FIXES: Y() noexcept = default; + +struct Z { + void operator delete(void *ptr) throw(); + void operator delete[](void *ptr) throw(int); + ~Z() throw(int) {} +}; +// CHECK-MESSAGES: :[[@LINE-4]]:35: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-4]]:37: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-4]]:8: warning: dynamic exception specification 'throw(int)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] +// CHECK-FIXES: void operator delete(void *ptr) noexcept; +// CHECK-FIXES: void operator delete[](void *ptr) noexcept(false); +// CHECK-FIXES: ~Z() noexcept(false) {} + +struct S { + void f() throw(); +}; +void f(void (S::*)() throw()); +// CHECK-MESSAGES: :[[@LINE-3]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-2]]:22: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-FIXES: void f() noexcept; +// CHECK-FIXES: void f(void (S::*)() noexcept); + +template +struct ST { + void foo() throw(); +}; +template +void ft(void (ST::*)() throw()); +// CHECK-MESSAGES: :[[@LINE-4]]:14: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-MESSAGES: :[[@LINE-2]]:27: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-FIXES: void foo() noexcept; +// CHECK-FIXES: void ft(void (ST::*)() noexcept); + +typedef void (*fp)(void (*fp2)(int) throw()); +// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-FIXES: typedef void (*fp)(void (*fp2)(int) noexcept); + +// 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 + ;