Index: clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tidy/modernize/CMakeLists.txt +++ clang-tidy/modernize/CMakeLists.txt @@ -9,6 +9,7 @@ MakeSharedCheck.cpp MakeUniqueCheck.cpp ModernizeTidyModule.cpp + NoexceptCorrectnessCheck.cpp PassByValueCheck.cpp RawStringLiteralCheck.cpp RedundantVoidArgCheck.cpp Index: clang-tidy/modernize/ModernizeTidyModule.cpp =================================================================== --- clang-tidy/modernize/ModernizeTidyModule.cpp +++ clang-tidy/modernize/ModernizeTidyModule.cpp @@ -15,6 +15,7 @@ #include "LoopConvertCheck.h" #include "MakeSharedCheck.h" #include "MakeUniqueCheck.h" +#include "NoexceptCorrectnessCheck.h" #include "PassByValueCheck.h" #include "RawStringLiteralCheck.h" #include "RedundantVoidArgCheck.h" @@ -47,6 +48,8 @@ CheckFactories.registerCheck("modernize-loop-convert"); CheckFactories.registerCheck("modernize-make-shared"); CheckFactories.registerCheck("modernize-make-unique"); + CheckFactories.registerCheck( + "modernize-noexcept-correctness"); CheckFactories.registerCheck("modernize-pass-by-value"); CheckFactories.registerCheck( "modernize-raw-string-literal"); Index: clang-tidy/modernize/NoexceptCorrectnessCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/NoexceptCorrectnessCheck.h @@ -0,0 +1,35 @@ +//===--- NoexceptCorrectnessCheck.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_NOEXCEPT_CORRECTNESS_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_NOEXCEPT_CORRECTNESS_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// Check for the correct use of noexcept and automatically add annotation if possible. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-noexcept-correctness.html +class NoexceptCorrectnessCheck : public ClangTidyCheck { +public: + NoexceptCorrectnessCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_NOEXCEPT_CORRECTNESS_H Index: clang-tidy/modernize/NoexceptCorrectnessCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/modernize/NoexceptCorrectnessCheck.cpp @@ -0,0 +1,81 @@ +//===--- NoexceptCorrectnessCheck.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 "NoexceptCorrectnessCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +void NoexceptCorrectnessCheck::registerMatchers(MatchFinder *Finder) { + // Finds direct throw expression that can occur in functions. This + // will ignore if the throw is actually reachable. + // Whenever a function directly throws it should be marked noexcept(false) + // but must not be noexcept! + Finder->addMatcher( + functionDecl( + hasDescendant( + // hasDirectThrow + cxxThrowExpr(unless(hasAncestor(cxxTryStmt()))).bind("throw"))) + .bind("direct_throwing_decl"), + this); + + // Check if the function contains anything that could throw and if not, it + // can be annotated with noexcept. + Finder->addMatcher( + functionDecl( + unless(anyOf( + // hasIndirectThrow + anyOf(hasDescendant( + callExpr(callee(functionDecl(unless(isNoThrow()))))), + hasDescendant(cxxMemberCallExpr( + callee(functionDecl(unless(isNoThrow()))))), + hasDescendant(cxxOperatorCallExpr( + callee(functionDecl(unless(isNoThrow()))))), + hasDescendant(cxxConstructExpr( + hasDeclaration(functionDecl(unless(isNoThrow())))))), + // hasThrowingExpression + anyOf(hasDescendant(cxxDynamicCastExpr()), + hasDescendant(cxxNewExpr())), + // hasDirectThrow + hasDescendant(cxxThrowExpr(unless(hasAncestor(cxxTryStmt()))))))) + .bind("nothrow_decl"), + this); +} + +void NoexceptCorrectnessCheck::check(const MatchFinder::MatchResult &Result) { + const auto N = Result.Nodes; + if (const auto *ThrowingDecl = + N.getNodeAs("direct_throwing_decl")) { + // FIXME how is that done? i did not find a noThrow predicate + //if (ThrowingDecl->isNoThrow()) { + //diag(ThrowingDecl->getLocation(), + //"function is marked noexcept but contains a direct throw"); + //} else { + diag(ThrowingDecl->getLocation(), + "function can be marked noexcept(false)"); + //} + + const auto *ThrowStmt = N.getNodeAs("throw"); + diag(ThrowStmt->getLocStart(), "throw expression here", + DiagnosticIDs::Note); + } else if (const auto *NoexceptDecl = + N.getNodeAs("nothrow_decl")) { + // Check if the function is noexcept or not and adjust its specification. + diag(NoexceptDecl->getLocation(), "function can be marked noexcept"); + } +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -107,6 +107,7 @@ modernize-loop-convert modernize-make-shared modernize-make-unique + modernize-noexcept-correctness modernize-pass-by-value modernize-raw-string-literal modernize-redundant-void-arg Index: docs/clang-tidy/checks/modernize-noexcept-correctness.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/modernize-noexcept-correctness.rst @@ -0,0 +1,41 @@ +.. title:: clang-tidy - modernize-noexcept-correctness + +modernize-noexcept-correctness +============================== + +This check will mark functions as noexcept if that is possible. +`noexcept` is a new keyword in C++ 11 to signal, that no exception will escape from +the function annotated with it. Some examples to show how this new feature is used are below. + + +.. code-block:: c++ + + // Executing only `noexcept` operations. + int simple_calculation() { // annotate with `noexcept` + int i = 0; + i+= 42; + return i; + } + + // Calling only `noexcept` functions. + int complex_calculation() { // annotate with `noexcept` as well + int j = 10; + j+= simple_calculation(); // this call is `noexcept` + return j; + } + + // Definitly throw an exception on one path. The check will not analyze if this + // path is actually executed (in this function it wouldn't!). + int throwing_calculation() { // possibly annotate with `noexcept(false)` + int i = 0; + i+= 42; + if(i == 0) { + throw std::runtime_error{"Calculation is errorneous."}; + } + return i; + } + + +.. options:: + + - AnnotateThrow -> boolean if a function that can throw for sure will be annotated with `noexcept(false)` Index: test/clang-tidy/modernize-noexcept-correctness.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-noexcept-correctness.cpp @@ -0,0 +1,70 @@ +// RUN: %check_clang_tidy %s modernize-noexcept-correctness %t + +float throwing(float x) { + // CHECK-MESSAGES: :[[@LINE-1]]: 7: warning: function can be marked noexcept(false) + // CHECK-MESSAGES: :[[@LINE+2]]: 5: note: throw expression here + if (x == 0.f) + throw "Division by Zero!"; + else + return 42.f / x; +} + +int possibly_noexcept() { + // CHECK-MESSAGES: :[[@LINE-1]]: 5: warning: function can be marked noexcept + int i = 0; + i += 42; + return i; +} + +int actually_noexcept() noexcept { // Ok + int i = 0; + i += 42; + return i; +} + +int wrongly_noexcept() noexcept { + // CHECK-MESSAGES: :[[@LINE-1]]: 1: warning: function is marked noexcept but contains a direct throw + // CHECK-MESSAGES: :[[@LINE+4]]: 5: note: throw expression here + int i = 0; + i += 42; + if (i == 42) + throw int{15}; + return i; +} + +int complex_possibly_noexcept() { + // CHECK-MESSAGES: :[[@LINE-1]]: 1: warning: function can be marked noexcept + int k = 0; + k = actually_noexcept() + 15; + return k; +} + +// Could throw indirectly. +float complex_wrongly_noexcept(float x, float y) noexcept { + x += throwing(y); + return x; +} + +// Could be marked noexcept, but wont right now. +float complex_non_throwing(float x, float y) { + try { + if (y == 0.f) + throw float(); + else + return x / y; + } catch (float) { + return x - y; + } +} + +// This one is hard, since one must figure out if all possible exceptions are handled. +// I think that is to complex (for now) and should be let alone. So the programmer +// must ensure its correctly either marked noexcept or not. +float case_dependent_throw() { // Ok + try { + return throwing(10.f); + } catch (int) { + // handle the exception + return 42.f; + } +}