diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp --- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp +++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp @@ -119,6 +119,16 @@ CallStack.insert(Func); ExceptionInfo Result = throwsException(Body, ExceptionInfo::Throwables(), CallStack); + + // For a constructor, we also have to check the initializers. + if (const auto *Ctor = dyn_cast(Func)) { + for (const CXXCtorInitializer *Init : Ctor->inits()) { + ExceptionInfo Excs = throwsException( + Init->getInit(), ExceptionInfo::Throwables(), CallStack); + Result.merge(Excs); + } + } + CallStack.erase(Func); return Result; } @@ -195,6 +205,14 @@ ExceptionInfo Excs = throwsException(Func, CallStack); Results.merge(Excs); } + } else if (const auto *Construct = dyn_cast(St)) { + ExceptionInfo Excs = + throwsException(Construct->getConstructor(), CallStack); + Results.merge(Excs); + } else if (const auto *DefaultInit = dyn_cast(St)) { + ExceptionInfo Excs = + throwsException(DefaultInit->getExpr(), Caught, CallStack); + Results.merge(Excs); } else { for (const Stmt *Child : St->children()) { ExceptionInfo Excs = throwsException(Child, Caught, CallStack); diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-exception-escape.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-exception-escape.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone-exception-escape.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-exception-escape.cpp @@ -288,6 +288,49 @@ return recursion_helper(n); } +struct super_throws { + super_throws() noexcept(false) { throw 42; } +}; + +struct sub_throws : super_throws { + sub_throws() noexcept : super_throws() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'sub_throws' which should not throw exceptions +}; + +struct super_throws_again { + super_throws_again() throw(int); +}; + +struct sub_throws_again : super_throws_again { + sub_throws_again() noexcept : super_throws_again() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'sub_throws_again' which should not throw exceptions +}; + +struct init_member_throws { + super_throws s; + + init_member_throws() noexcept : s() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'init_member_throws' which should not throw exceptions +}; + +struct implicit_init_member_throws { + super_throws s; + + implicit_init_member_throws() noexcept {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'implicit_init_member_throws' which should not throw exceptions +}; + +struct init { + explicit init(int, int) noexcept(false) { throw 42; } +}; + +struct in_class_init_throws { + init i{1, 2}; + + in_class_init_throws() noexcept {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: an exception may be thrown in function 'in_class_init_throws' which should not throw exceptions +}; + int main() { // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: an exception may be thrown in function 'main' which should not throw exceptions throw 1;