Index: clang-tidy/bugprone/ExceptionEscapeCheck.h =================================================================== --- clang-tidy/bugprone/ExceptionEscapeCheck.h +++ clang-tidy/bugprone/ExceptionEscapeCheck.h @@ -1,4 +1,4 @@ -//===--- ExceptionEscapeCheck.h - clang-tidy---------------------*- C++ -*-===// +//===--- ExceptionEscapeCheck.h - clang-tidy --------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -10,7 +10,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_EXCEPTION_ESCAPE_H #include "../ClangTidy.h" - +#include "../utils/ExceptionAnalyzer.h" #include "llvm/ADT/StringSet.h" namespace clang { @@ -36,7 +36,7 @@ std::string RawIgnoredExceptions; llvm::StringSet<> FunctionsThatShouldNotThrow; - llvm::StringSet<> IgnoredExceptions; + utils::ExceptionAnalyzer Tracer; }; } // namespace bugprone Index: clang-tidy/bugprone/ExceptionEscapeCheck.cpp =================================================================== --- clang-tidy/bugprone/ExceptionEscapeCheck.cpp +++ clang-tidy/bugprone/ExceptionEscapeCheck.cpp @@ -1,4 +1,4 @@ -//===--- ExceptionEscapeCheck.cpp - clang-tidy-----------------------------===// +//===--- ExceptionEscapeCheck.cpp - clang-tidy ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -8,174 +8,41 @@ #include "ExceptionEscapeCheck.h" +#include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" - #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/StringSet.h" using namespace clang::ast_matchers; -namespace { -typedef llvm::SmallVector TypeVec; -} // namespace - namespace clang { - -static bool isBaseOf(const Type *DerivedType, const Type *BaseType) { - const auto *DerivedClass = DerivedType->getAsCXXRecordDecl(); - const auto *BaseClass = BaseType->getAsCXXRecordDecl(); - if (!DerivedClass || !BaseClass) - return false; - - return !DerivedClass->forallBases( - [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; }); -} - -static const TypeVec -throwsException(const Stmt *St, const TypeVec &Caught, - llvm::SmallSet &CallStack); - -static const TypeVec -throwsException(const FunctionDecl *Func, - llvm::SmallSet &CallStack) { - if (CallStack.count(Func)) - return TypeVec(); - - if (const Stmt *Body = Func->getBody()) { - CallStack.insert(Func); - const TypeVec Result = throwsException(Body, TypeVec(), CallStack); - CallStack.erase(Func); - return Result; - } - - TypeVec Result; - if (const auto *FPT = Func->getType()->getAs()) { - for (const QualType Ex : FPT->exceptions()) { - Result.push_back(Ex.getTypePtr()); - } - } - return Result; -} - -static const TypeVec -throwsException(const Stmt *St, const TypeVec &Caught, - llvm::SmallSet &CallStack) { - TypeVec Results; - - if (!St) - return Results; - - if (const auto *Throw = dyn_cast(St)) { - if (const auto *ThrownExpr = Throw->getSubExpr()) { - const auto *ThrownType = - ThrownExpr->getType()->getUnqualifiedDesugaredType(); - if (ThrownType->isReferenceType()) { - ThrownType = ThrownType->castAs() - ->getPointeeType() - ->getUnqualifiedDesugaredType(); - } - if (const auto *TD = ThrownType->getAsTagDecl()) { - if (TD->getDeclName().isIdentifier() && TD->getName() == "bad_alloc" - && TD->isInStdNamespace()) - return Results; - } - Results.push_back(ThrownExpr->getType()->getUnqualifiedDesugaredType()); - } else { - Results.append(Caught.begin(), Caught.end()); - } - } else if (const auto *Try = dyn_cast(St)) { - TypeVec Uncaught = throwsException(Try->getTryBlock(), Caught, CallStack); - for (unsigned i = 0; i < Try->getNumHandlers(); ++i) { - const CXXCatchStmt *Catch = Try->getHandler(i); - if (!Catch->getExceptionDecl()) { - const TypeVec Rethrown = - throwsException(Catch->getHandlerBlock(), Uncaught, CallStack); - Results.append(Rethrown.begin(), Rethrown.end()); - Uncaught.clear(); - } else { - const auto *CaughtType = - Catch->getCaughtType()->getUnqualifiedDesugaredType(); - if (CaughtType->isReferenceType()) { - CaughtType = CaughtType->castAs() - ->getPointeeType() - ->getUnqualifiedDesugaredType(); - } - auto NewEnd = - llvm::remove_if(Uncaught, [&CaughtType](const Type *ThrownType) { - return ThrownType == CaughtType || - isBaseOf(ThrownType, CaughtType); - }); - if (NewEnd != Uncaught.end()) { - Uncaught.erase(NewEnd, Uncaught.end()); - const TypeVec Rethrown = throwsException( - Catch->getHandlerBlock(), TypeVec(1, CaughtType), CallStack); - Results.append(Rethrown.begin(), Rethrown.end()); - } - } - } - Results.append(Uncaught.begin(), Uncaught.end()); - } else if (const auto *Call = dyn_cast(St)) { - if (const FunctionDecl *Func = Call->getDirectCallee()) { - TypeVec Excs = throwsException(Func, CallStack); - Results.append(Excs.begin(), Excs.end()); - } - } else { - for (const Stmt *Child : St->children()) { - TypeVec Excs = throwsException(Child, Caught, CallStack); - Results.append(Excs.begin(), Excs.end()); - } - } - return Results; -} - -static const TypeVec throwsException(const FunctionDecl *Func) { - llvm::SmallSet CallStack; - return throwsException(Func, CallStack); -} - -namespace ast_matchers { -AST_MATCHER_P(FunctionDecl, throws, internal::Matcher, InnerMatcher) { - TypeVec ExceptionList = throwsException(&Node); - auto NewEnd = llvm::remove_if( - ExceptionList, [this, Finder, Builder](const Type *Exception) { - return !InnerMatcher.matches(*Exception, Finder, Builder); - }); - ExceptionList.erase(NewEnd, ExceptionList.end()); - return ExceptionList.size(); -} - -AST_MATCHER_P(Type, isIgnored, llvm::StringSet<>, IgnoredExceptions) { - if (const auto *TD = Node.getAsTagDecl()) { - if (TD->getDeclName().isIdentifier()) - return IgnoredExceptions.count(TD->getName()) > 0; - } - return false; -} - +namespace { AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>, FunctionsThatShouldNotThrow) { return FunctionsThatShouldNotThrow.count(Node.getNameAsString()) > 0; } -} // namespace ast_matchers +} // namespace namespace tidy { namespace bugprone { - ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get( "FunctionsThatShouldNotThrow", "")), RawIgnoredExceptions(Options.get("IgnoredExceptions", "")) { - llvm::SmallVector FunctionsThatShouldNotThrowVec, - IgnoredExceptionsVec; - StringRef(RawFunctionsThatShouldNotThrow) - .split(FunctionsThatShouldNotThrowVec, ",", -1, false); + std::vector FunctionsThatShouldNotThrowVec = + utils::options::parseStringList(RawFunctionsThatShouldNotThrow); FunctionsThatShouldNotThrow.insert(FunctionsThatShouldNotThrowVec.begin(), FunctionsThatShouldNotThrowVec.end()); - StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec, ",", -1, false); + + std::vector IgnoredExceptionsVec = + utils::options::parseStringList(RawIgnoredExceptions); + + llvm::StringSet<> IgnoredExceptions; IgnoredExceptions.insert(IgnoredExceptionsVec.begin(), IgnoredExceptionsVec.end()); + Tracer.ignoreExceptions(std::move(IgnoredExceptions)); } void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { @@ -193,22 +60,25 @@ cxxConstructorDecl(isMoveConstructor()), cxxMethodDecl(isMoveAssignmentOperator()), hasName("main"), hasName("swap"), - isEnabled(FunctionsThatShouldNotThrow)), - throws(unless(isIgnored(IgnoredExceptions)))) + isEnabled(FunctionsThatShouldNotThrow))) .bind("thrower"), this); } void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) { - const FunctionDecl *MatchedDecl = - Result.Nodes.getNodeAs("thrower"); + const auto *MatchedDecl = Result.Nodes.getNodeAs("thrower"); + if (!MatchedDecl) return; - // FIXME: We should provide more information about the exact location where - // the exception is thrown, maybe the full path the exception escapes - diag(MatchedDecl->getLocation(), "an exception may be thrown in function %0 " - "which should not throw exceptions") << MatchedDecl; + if (Tracer.throwsException(MatchedDecl)) + // FIXME: We should provide more information about the exact location where + // the exception is thrown, maybe the full path the exception escapes + diag(MatchedDecl->getLocation(), + "an exception may be thrown in function %0 " + + "which should not throw exceptions") + << MatchedDecl; } } // namespace bugprone Index: clang-tidy/modernize/UseNoexceptCheck.h =================================================================== --- clang-tidy/modernize/UseNoexceptCheck.h +++ clang-tidy/modernize/UseNoexceptCheck.h @@ -10,6 +10,7 @@ #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_NOEXCEPT_H #include "../ClangTidy.h" +#include "../utils/ExceptionAnalyzer.h" namespace clang { namespace tidy { @@ -38,7 +39,9 @@ private: const std::string NoexceptMacro; - bool UseNoexceptFalse; + const bool UseNoexceptFalse; + const bool AddMissingNoexcept; + utils::ExceptionAnalyzer Analyzer; }; } // namespace modernize Index: clang-tidy/modernize/UseNoexceptCheck.cpp =================================================================== --- clang-tidy/modernize/UseNoexceptCheck.cpp +++ clang-tidy/modernize/UseNoexceptCheck.cpp @@ -19,11 +19,13 @@ UseNoexceptCheck::UseNoexceptCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), NoexceptMacro(Options.get("ReplacementString", "")), - UseNoexceptFalse(Options.get("UseNoexceptFalse", true)) {} + UseNoexceptFalse(Options.get("UseNoexceptFalse", true)), + AddMissingNoexcept(Options.get("AddMissingNoexcept", false)) {} void UseNoexceptCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "ReplacementString", NoexceptMacro); Options.store(Opts, "UseNoexceptFalse", UseNoexceptFalse); + Options.store(Opts, "AddMissingNoexcept", AddMissingNoexcept); } void UseNoexceptCheck::registerMatchers(MatchFinder *Finder) { @@ -56,6 +58,14 @@ functionProtoType(hasDynamicExceptionSpec())))))))) .bind("parmVarDecl"), this); + + if (AddMissingNoexcept) + Finder->addMatcher( + functionDecl(allOf(hasBody(stmt()), + unless(anyOf(isNoThrow(), hasDynamicExceptionSpec(), + isImplicit())))) + .bind("potentialNoexcept"), + this); } void UseNoexceptCheck::check(const MatchFinder::MatchResult &Result) { @@ -82,6 +92,14 @@ .IgnoreParens() .castAs() .getExceptionSpecRange(); + } else if (const auto *PotentialNoexcept = + Result.Nodes.getNodeAs("potentialNoexcept")) { + if (!Analyzer.throwsException(PotentialNoexcept) && + PotentialNoexcept->getBeginLoc().isValid()) + diag(PotentialNoexcept->getBeginLoc(), + "this function can not throw an exception, consider adding " + "'noexcept'"); + return; } CharSourceRange CRange = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(Range), *Result.SourceManager, @@ -92,10 +110,10 @@ StringRef ReplacementStr = IsNoThrow ? NoexceptMacro.empty() ? "noexcept" : NoexceptMacro.c_str() - : NoexceptMacro.empty() - ? (DtorOrOperatorDel || UseNoexceptFalse) ? "noexcept(false)" - : "" - : ""; + : NoexceptMacro.empty() ? (DtorOrOperatorDel || UseNoexceptFalse) + ? "noexcept(false)" + : "" + : ""; FixItHint FixIt; if ((IsNoThrow || NoexceptMacro.empty()) && CRange.isValid()) Index: clang-tidy/utils/CMakeLists.txt =================================================================== --- clang-tidy/utils/CMakeLists.txt +++ clang-tidy/utils/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_library(clangTidyUtils ASTUtils.cpp DeclRefExprUtils.cpp + ExceptionAnalyzer.cpp ExprSequence.cpp FixItHintUtils.cpp HeaderFileExtensionsUtils.cpp Index: clang-tidy/utils/ExceptionAnalyzer.h =================================================================== --- /dev/null +++ clang-tidy/utils/ExceptionAnalyzer.h @@ -0,0 +1,50 @@ +//===--- ExceptionAnalyzer.h - clang-tidy -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H + +#include "clang/AST/ASTContext.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/StringSet.h" + +namespace clang { +namespace tidy { +namespace utils { + +/// This class analysis if a `FunctionDecl` can in principle throw an exception, +/// either directly or indirectly. +/// It can be configured to ignore some exception types, especially +/// `std::bad_alloc` can be disabled as using dynamic memory will always +/// give the possibility of an exception. +class ExceptionAnalyzer { +public: + ExceptionAnalyzer() = default; + + bool throwsException(const FunctionDecl *Func); + void ignoreExceptions(llvm::StringSet<> ExceptionNames) { + IgnoredExceptions = std::move(ExceptionNames); + } + +private: + using TypeVec = llvm::SmallVector; + + TypeVec throwsException(const FunctionDecl *Func, + llvm::SmallSet &CallStack); + TypeVec throwsException(const Stmt *St, const TypeVec &Caught, + llvm::SmallSet &CallStack); + bool isIgnoredExceptionType(const Type *Exception); + + llvm::StringSet<> IgnoredExceptions; + llvm::DenseMap FunctionCache; +}; +} // namespace utils +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_EXCEPTION_ANALYZER_H Index: clang-tidy/utils/ExceptionAnalyzer.cpp =================================================================== --- clang-tidy/utils/ExceptionAnalyzer.cpp +++ clang-tidy/utils/ExceptionAnalyzer.cpp @@ -1,4 +1,4 @@ -//===--- ExceptionEscapeCheck.cpp - clang-tidy-----------------------------===// +//===--- ExceptionAnalyzer.cpp - clang-tidy -------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,22 +6,12 @@ // //===----------------------------------------------------------------------===// -#include "ExceptionEscapeCheck.h" +#include "ExceptionAnalyzer.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" -#include "llvm/ADT/SmallSet.h" -#include "llvm/ADT/StringSet.h" - -using namespace clang::ast_matchers; - -namespace { -typedef llvm::SmallVector TypeVec; -} // namespace - namespace clang { - static bool isBaseOf(const Type *DerivedType, const Type *BaseType) { const auto *DerivedClass = DerivedType->getAsCXXRecordDecl(); const auto *BaseClass = BaseType->getAsCXXRecordDecl(); @@ -32,13 +22,12 @@ [BaseClass](const CXXRecordDecl *Cur) { return Cur != BaseClass; }); } -static const TypeVec -throwsException(const Stmt *St, const TypeVec &Caught, - llvm::SmallSet &CallStack); +namespace tidy { +namespace utils { -static const TypeVec -throwsException(const FunctionDecl *Func, - llvm::SmallSet &CallStack) { +ExceptionAnalyzer::TypeVec ExceptionAnalyzer::throwsException( + const FunctionDecl *Func, + llvm::SmallSet &CallStack) { if (CallStack.count(Func)) return TypeVec(); @@ -58,9 +47,9 @@ return Result; } -static const TypeVec -throwsException(const Stmt *St, const TypeVec &Caught, - llvm::SmallSet &CallStack) { +ExceptionAnalyzer::TypeVec ExceptionAnalyzer::throwsException( + const Stmt *St, const TypeVec &Caught, + llvm::SmallSet &CallStack) { TypeVec Results; if (!St) @@ -76,8 +65,8 @@ ->getUnqualifiedDesugaredType(); } if (const auto *TD = ThrownType->getAsTagDecl()) { - if (TD->getDeclName().isIdentifier() && TD->getName() == "bad_alloc" - && TD->isInStdNamespace()) + if (TD->getDeclName().isIdentifier() && TD->getName() == "bad_alloc" && + TD->isInStdNamespace()) return Results; } Results.push_back(ThrownExpr->getType()->getUnqualifiedDesugaredType()); @@ -129,88 +118,37 @@ return Results; } -static const TypeVec throwsException(const FunctionDecl *Func) { +bool ExceptionAnalyzer::throwsException(const FunctionDecl *Func) { + // Check if the function has already been analyzed and reuse that result. + if (FunctionCache.count(Func) > 0) + return FunctionCache[Func]; + llvm::SmallSet CallStack; - return throwsException(Func, CallStack); -} + TypeVec ExceptionList = throwsException(Func, CallStack); -namespace ast_matchers { -AST_MATCHER_P(FunctionDecl, throws, internal::Matcher, InnerMatcher) { - TypeVec ExceptionList = throwsException(&Node); - auto NewEnd = llvm::remove_if( - ExceptionList, [this, Finder, Builder](const Type *Exception) { - return !InnerMatcher.matches(*Exception, Finder, Builder); - }); + // Remove all ignored exceptions from the list of exceptions that can be + // thrown. + auto NewEnd = llvm::remove_if(ExceptionList, [this](const Type *Exception) { + return isIgnoredExceptionType(Exception); + }); ExceptionList.erase(NewEnd, ExceptionList.end()); - return ExceptionList.size(); + + // Cache the result of the analysis. + bool FunctionThrows = ExceptionList.size() > 0; + FunctionCache.insert(std::make_pair(Func, FunctionThrows)); + + return FunctionThrows; } -AST_MATCHER_P(Type, isIgnored, llvm::StringSet<>, IgnoredExceptions) { - if (const auto *TD = Node.getAsTagDecl()) { +bool ExceptionAnalyzer::isIgnoredExceptionType(const Type *Exception) { + if (const auto *TD = Exception->getAsTagDecl()) { if (TD->getDeclName().isIdentifier()) return IgnoredExceptions.count(TD->getName()) > 0; } return false; } -AST_MATCHER_P(FunctionDecl, isEnabled, llvm::StringSet<>, - FunctionsThatShouldNotThrow) { - return FunctionsThatShouldNotThrow.count(Node.getNameAsString()) > 0; -} -} // namespace ast_matchers - -namespace tidy { -namespace bugprone { - -ExceptionEscapeCheck::ExceptionEscapeCheck(StringRef Name, - ClangTidyContext *Context) - : ClangTidyCheck(Name, Context), RawFunctionsThatShouldNotThrow(Options.get( - "FunctionsThatShouldNotThrow", "")), - RawIgnoredExceptions(Options.get("IgnoredExceptions", "")) { - llvm::SmallVector FunctionsThatShouldNotThrowVec, - IgnoredExceptionsVec; - StringRef(RawFunctionsThatShouldNotThrow) - .split(FunctionsThatShouldNotThrowVec, ",", -1, false); - FunctionsThatShouldNotThrow.insert(FunctionsThatShouldNotThrowVec.begin(), - FunctionsThatShouldNotThrowVec.end()); - StringRef(RawIgnoredExceptions).split(IgnoredExceptionsVec, ",", -1, false); - IgnoredExceptions.insert(IgnoredExceptionsVec.begin(), - IgnoredExceptionsVec.end()); -} - -void ExceptionEscapeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { - Options.store(Opts, "FunctionsThatShouldNotThrow", - RawFunctionsThatShouldNotThrow); - Options.store(Opts, "IgnoredExceptions", RawIgnoredExceptions); -} - -void ExceptionEscapeCheck::registerMatchers(MatchFinder *Finder) { - if (!getLangOpts().CPlusPlus || !getLangOpts().CXXExceptions) - return; - - Finder->addMatcher( - functionDecl(anyOf(isNoThrow(), cxxDestructorDecl(), - cxxConstructorDecl(isMoveConstructor()), - cxxMethodDecl(isMoveAssignmentOperator()), - hasName("main"), hasName("swap"), - isEnabled(FunctionsThatShouldNotThrow)), - throws(unless(isIgnored(IgnoredExceptions)))) - .bind("thrower"), - this); -} - -void ExceptionEscapeCheck::check(const MatchFinder::MatchResult &Result) { - const FunctionDecl *MatchedDecl = - Result.Nodes.getNodeAs("thrower"); - if (!MatchedDecl) - return; - - // FIXME: We should provide more information about the exact location where - // the exception is thrown, maybe the full path the exception escapes - diag(MatchedDecl->getLocation(), "an exception may be thrown in function %0 " - "which should not throw exceptions") << MatchedDecl; -} - -} // namespace bugprone +} // namespace utils } // namespace tidy + } // namespace clang Index: docs/clang-tidy/checks/bugprone-exception-escape.rst =================================================================== --- docs/clang-tidy/checks/bugprone-exception-escape.rst +++ docs/clang-tidy/checks/bugprone-exception-escape.rst @@ -28,12 +28,12 @@ .. option:: FunctionsThatShouldNotThrow - Comma separated list containing function names which should not throw. An + Semicolon separated list containing function names which should not throw. An example value for this parameter can be ``WinMain`` which adds function ``WinMain()`` in the Windows API to the list of the funcions which should not throw. Default value is an empty string. .. option:: IgnoredExceptions - Comma separated list containing type names which are not counted as thrown + Semicolon separated list containing type names which are not counted as thrown exceptions in the check. Default value is an empty string. Index: test/clang-tidy/bugprone-exception-escape.cpp =================================================================== --- test/clang-tidy/bugprone-exception-escape.cpp +++ test/clang-tidy/bugprone-exception-escape.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s bugprone-exception-escape %t -- -extra-arg=-std=c++11 -extra-arg=-fexceptions -config="{CheckOptions: [{key: bugprone-exception-escape.IgnoredExceptions, value: 'ignored1,ignored2'}, {key: bugprone-exception-escape.FunctionsThatShouldNotThrow, value: 'enabled1,enabled2,enabled3'}]}" -- +// RUN: %check_clang_tidy %s bugprone-exception-escape %t -- -extra-arg=-std=c++11 -extra-arg=-fexceptions -config="{CheckOptions: [{key: bugprone-exception-escape.IgnoredExceptions, value: 'ignored1;ignored2'}, {key: bugprone-exception-escape.FunctionsThatShouldNotThrow, value: 'enabled1;enabled2;enabled3'}]}" -- struct throwing_destructor { ~throwing_destructor() { Index: test/clang-tidy/modernize-use-noexcept-new-noexcept.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-use-noexcept-new-noexcept.cpp @@ -0,0 +1,46 @@ +// RUN: %check_clang_tidy %s modernize-use-noexcept %t -- \ +// RUN: -config="{CheckOptions: [{key: modernize-use-noexcept.AddMissingNoexcept, value: 1}]}" \ +// RUN: -- -std=c++11 -fexceptions + +void undefined(); +void undefinedNoexcept() noexcept; + +void empty() {} +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: this function can not throw an exception, consider adding 'noexcept' + +int tryCatchBody() try { + int Array[] = {1, 2, 3, 4}; + throw int(42); + return Array[0] + Array[1] + Array[2] + Array[3]; +} catch (...) { + return 42; +} +// CHECK-MESSAGES: :[[@LINE-7]]:1: warning: this function can not throw an exception, consider adding 'noexcept' + +int functionCanThrow() { + throw int(42); +} + +struct ConstructorWithNoexcept { + ConstructorWithNoexcept() = default; + int nonThrowingMember() { return 42; } + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: this function can not throw an exception, consider adding 'noexcept' + int nonThrowingButShown() noexcept { return 42; } + void undefinedMethod(); + int Member; +}; + +template +void nonThrowingTemplateUnknown(); + +template +T nonThrowingTemplateKnown() { + // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: this function can not throw an exception, consider adding 'noexcept' + return T{}; +} +void instantiate() { + // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: this function can not throw an exception, consider adding 'noexcept' + (void)nonThrowingTemplateKnown(); + (void)nonThrowingTemplateKnown(); + (void)nonThrowingTemplateKnown(); +} Index: test/clang-tidy/modernize-use-noexcept-opt.cpp =================================================================== --- test/clang-tidy/modernize-use-noexcept-opt.cpp +++ test/clang-tidy/modernize-use-noexcept-opt.cpp @@ -65,11 +65,11 @@ struct S { void f() throw(); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] +// CHECK-FIXES: void f() noexcept; }; 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-MESSAGES: :[[@LINE-1]]:22: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] // CHECK-FIXES: void f(void (S::*)() noexcept); typedef void (*fp)(void (*fp2)(int) throw()); Index: test/clang-tidy/modernize-use-noexcept.cpp =================================================================== --- test/clang-tidy/modernize-use-noexcept.cpp +++ test/clang-tidy/modernize-use-noexcept.cpp @@ -10,9 +10,9 @@ 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-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] // CHECK-FIXES: void foo() noexcept; +void footest() { foo(); foo(); } void bar() throw(...); // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw(...)' is deprecated; consider using 'noexcept(false)' instead [modernize-use-noexcept] @@ -70,21 +70,21 @@ struct S { void f() throw(); +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] }; 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-MESSAGES: :[[@LINE-1]]: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(); +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: dynamic exception specification 'throw()' is deprecated; consider using 'noexcept' instead [modernize-use-noexcept] }; 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-MESSAGES: :[[@LINE-1]]: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);