Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -38,6 +38,7 @@ SuspiciousStringCompareCheck.cpp SwappedArgumentsCheck.cpp ThrowByValueCatchByReferenceCheck.cpp + ThrowWithNoexceptCheck.cpp UndelegatedConstructor.cpp UniqueptrResetReleaseCheck.cpp UnusedAliasDeclsCheck.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -46,6 +46,7 @@ #include "SwappedArgumentsCheck.h" #include "ThrowByValueCatchByReferenceCheck.h" #include "UnconventionalAssignOperatorCheck.h" +#include "ThrowWithNoexceptCheck.h" #include "UndelegatedConstructor.h" #include "UniqueptrResetReleaseCheck.h" #include "UnusedAliasDeclsCheck.h" @@ -126,6 +127,8 @@ "misc-swapped-arguments"); CheckFactories.registerCheck( "misc-throw-by-value-catch-by-reference"); + CheckFactories.registerCheck( + "misc-throw-with-noexcept"); CheckFactories.registerCheck( "misc-undelegated-constructor"); CheckFactories.registerCheck( Index: clang-tidy/misc/ThrowWithNoexceptCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/ThrowWithNoexceptCheck.h @@ -0,0 +1,33 @@ +//===--- ThrowWithNoexceptCheck.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_THROW_WITH_NOEXCEPT_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_THROW_WITH_NOEXCEPT_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +///\brief Warns about using throw in function declared as noexcept. +/// It complains about every throw, even if it is caught later. +class ThrowWithNoexceptCheck : public ClangTidyCheck { +public: + ThrowWithNoexceptCheck(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_THROW_WITH_NOEXCEPT_H Index: clang-tidy/misc/ThrowWithNoexceptCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/ThrowWithNoexceptCheck.cpp @@ -0,0 +1,61 @@ +//===--- ThrowWithNoexceptCheck.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 "ThrowWithNoexceptCheck.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 ThrowWithNoexceptCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus11) + return; + Finder->addMatcher( + cxxThrowExpr(stmt(forFunction(functionDecl(isNoThrow()).bind("func")))) + .bind("throw"), + this); +} + +void ThrowWithNoexceptCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Function = Result.Nodes.getNodeAs("func"); + const auto *Throw = Result.Nodes.getNodeAs("throw"); + + diag(Throw->getLocStart(), "'throw' expression in a function declared with a " + "non-throwing exception specification"); + + llvm::SmallVector NoExceptRanges; + for (const auto *Redecl : Function->redecls()) { + SourceRange NoExceptRange = + Redecl->getExceptionSpecSourceRange(); + + if (NoExceptRange.isValid()) { + NoExceptRanges.push_back(NoExceptRange); + } else { + // If a single one is not valid, we cannot apply the fix as we need to + // remove noexcept in all declarations for the fix to be effective. + NoExceptRanges.clear(); + break; + } + } + + for (const auto &NoExceptRange : NoExceptRanges) { + // FIXME use DiagnosticIDs::Level::Note + diag(NoExceptRange.getBegin(), "in a function declared no-throw here:", DiagnosticIDs::Note) + << FixItHint::CreateRemoval(NoExceptRange); + } +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -57,7 +57,10 @@ Improvements to clang-tidy -------------------------- -The improvements are... +- New `misc-throw-with-noexcept + `_ check + + Flags ``throw`` statements in functions marked as no-throw. Improvements to include-fixer ----------------------------- Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -91,6 +91,7 @@ misc-suspicious-string-compare misc-swapped-arguments misc-throw-by-value-catch-by-reference + misc-throw-with-noexcept misc-unconventional-assign-operator misc-undelegated-constructor misc-uniqueptr-reset-release Index: docs/clang-tidy/checks/misc-throw-with-noexcept.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-throw-with-noexcept.rst @@ -0,0 +1,24 @@ +.. title:: clang-tidy - misc-throw-with-noexcept + +misc-throw-with-noexcept +======================== + +This check finds cases of using ``throw`` in a function declared +with a non-throwing exception specification. + +Please note that the warning is issued even if the exception is caught within +the same function, as that would be probably a bad style anyway. + +It removes the exception specification as a fix. + + + .. code-block:: c++ + + void f() noexcept { + throw 42; + } + + // Will be changed to + void f() { + throw 42; + } Index: test/clang-tidy/misc-throw-with-noexcept.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-throw-with-noexcept.cpp @@ -0,0 +1,111 @@ +// RUN: %check_clang_tidy %s misc-throw-with-noexcept %t + +void f_throw_with_ne() noexcept(true) { + { + throw 5; + } +} +// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: 'throw' expression in a function declared with a non-throwing exception specification [misc-throw-with-noexcept] +// CHECK-MESSAGES: :[[@LINE-6]]:24: note: in a function declared no-throw here: +// CHECK-FIXES: void f_throw_with_ne() { + +void f_noexcept_false() noexcept(false) { + throw 5; +} + +void f_decl_only() noexcept; + +// Controversial example +void throw_and_catch() noexcept(true) { + try { + throw 5; + } catch (...) { + } +} +// CHECK-MESSAGES: :[[@LINE-4]]:5: warning: 'throw' expression in a function declared with a non-throwing exception specification [misc-throw-with-noexcept] +// CHECK-MESSAGES: :[[@LINE-7]]:24: note: in a function declared no-throw here: +// CHECK-FIXES: void throw_and_catch() { + +void with_parens() noexcept((true)) { + throw 42; +} +// CHECK-MESSAGES: :[[@LINE-2]]:2: warning: 'throw' expression in a function declared with a non-throwing exception specification [misc-throw-with-noexcept] +// CHECK-MESSAGES: :[[@LINE-4]]:20: note: in a function declared no-throw here: +// CHECK-FIXES: void with_parens() { + + +void with_parens_and_spaces() noexcept ( (true) ) { + throw 42; +} +// CHECK-MESSAGES: :[[@LINE-2]]:2: warning: 'throw' expression in a function declared with a non-throwing exception specification [misc-throw-with-noexcept] +// CHECK-MESSAGES: :[[@LINE-4]]:31: note: in a function declared no-throw here: +// CHECK-FIXES: void with_parens_and_spaces() { + +class Class { + void InClass() const noexcept(true) { + throw 42; + } +}; +// CHECK-MESSAGES: :[[@LINE-3]]:5: warning: 'throw' expression in a function declared with a non-throwing exception specification [misc-throw-with-noexcept] +// CHECK-MESSAGES: :[[@LINE-5]]:24: note: in a function declared no-throw here: +// CHECK-FIXES: void InClass() const { + + +void forward_declared() noexcept; + +void forward_declared() noexcept { + throw 42; +} +// CHECK-MESSAGES: :[[@LINE-2]]:2: warning: 'throw' expression in a function declared with a non-throwing exception specification [misc-throw-with-noexcept] +// CHECK-MESSAGES: :[[@LINE-4]]:25: note: in a function declared no-throw here: +// CHECK-MESSAGES: :[[@LINE-7]]:25: note: in a function declared no-throw here: +// CHECK-FIXES: void forward_declared() ; +// CHECK-FIXES: void forward_declared() { + +void getFunction() noexcept { + struct X { + static void inner() + { + throw 42; + } + }; +} + +void dynamic_exception_spec() throw() { + throw 42; +} +// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: 'throw' expression in a function declared with a non-throwing exception specification [misc-throw-with-noexcept] +// CHECK-MESSAGES: :[[@LINE-4]]:31: note: in a function declared no-throw here: +// CHECK-FIXES: void dynamic_exception_spec() { + +#define NOEXCEPT noexcept + +void with_macro() NOEXCEPT { + throw 42; +} +// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: 'throw' expression in a function declared with a non-throwing exception specification [misc-throw-with-noexcept] +// CHECK-MESSAGES: :[[@LINE-4]]:19: note: in a function declared no-throw here: +// CHECK-FIXES: void with_macro() { + +template int template_function() noexcept { + throw 42; +} +// CHECK-MESSAGES: :[[@LINE-2]]:3: warning: 'throw' expression in a function declared with a non-throwing exception specification [misc-throw-with-noexcept] +// CHECK-MESSAGES: :[[@LINE-4]]:46: note: in a function declared no-throw here: +// CHECK-FIXES: template int template_function() { + +template class template_class { + int throw_in_member() noexcept { + throw 42; + } + // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: 'throw' expression in a function declared with a non-throwing exception specification [misc-throw-with-noexcept] + // CHECK-MESSAGES: :[[@LINE-4]]:25: note: in a function declared no-throw here: + // CHECK-FIXES: int throw_in_member() { +}; + +void instantiations() { + template_function(); + template_function(); + template_class c1; + template_class c2; +}