Author: Victor Chernyakin Date: 2025-11-08T02:23:32-08:00 New Revision: 577b5194bf7a0decfec1b88c5afe96426c2b45fe
URL: https://github.com/llvm/llvm-project/commit/577b5194bf7a0decfec1b88c5afe96426c2b45fe DIFF: https://github.com/llvm/llvm-project/commit/577b5194bf7a0decfec1b88c5afe96426c2b45fe.diff LOG: [clang-tidy] Fix `bugprone-exception-escape` not diagnosing throws in argument lists (#165955) Fixes #165766. Added: Modified: clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp clang-tools-extra/docs/ReleaseNotes.rst clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp index 5fd1b731707e7..8ead26407ee5d 100644 --- a/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp +++ b/clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp @@ -562,17 +562,6 @@ ExceptionAnalyzer::throwsException(const Stmt *St, } } Results.merge(Uncaught); - } else if (const auto *Call = dyn_cast<CallExpr>(St)) { - if (const FunctionDecl *Func = Call->getDirectCallee()) { - const ExceptionInfo Excs = - throwsException(Func, Caught, CallStack, Call->getBeginLoc()); - Results.merge(Excs); - } - } else if (const auto *Construct = dyn_cast<CXXConstructExpr>(St)) { - const ExceptionInfo Excs = - throwsException(Construct->getConstructor(), Caught, CallStack, - Construct->getBeginLoc()); - Results.merge(Excs); } else if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) { const ExceptionInfo Excs = throwsException(DefaultInit->getExpr(), Caught, CallStack); @@ -602,10 +591,25 @@ ExceptionAnalyzer::throwsException(const Stmt *St, Results.merge(Excs); } } else { + // Check whether any of this node's subexpressions throws. for (const Stmt *Child : St->children()) { const ExceptionInfo Excs = throwsException(Child, Caught, CallStack); Results.merge(Excs); } + + // If this node is a call to a function or constructor, also check + // whether the call itself throws. + if (const auto *Call = dyn_cast<CallExpr>(St)) { + if (const FunctionDecl *Func = Call->getDirectCallee()) { + ExceptionInfo Excs = + throwsException(Func, Caught, CallStack, Call->getBeginLoc()); + Results.merge(Excs); + } + } else if (const auto *Construct = dyn_cast<CXXConstructExpr>(St)) { + ExceptionInfo Excs = throwsException(Construct->getConstructor(), Caught, + CallStack, Construct->getBeginLoc()); + Results.merge(Excs); + } } return Results; } diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 92a2d33d8fa16..666865cfb2fcd 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -300,7 +300,9 @@ Changes in existing checks - Improved :doc:`bugprone-exception-escape <clang-tidy/checks/bugprone/exception-escape>` check's handling of lambdas: exceptions from captures are now diagnosed, exceptions in the bodies of - lambdas that aren't actually invoked are not. + lambdas that aren't actually invoked are not. Additionally, fixed an issue + where the check wouldn't diagnose throws in arguments to functions or + constructors. - Improved :doc:`bugprone-infinite-loop <clang-tidy/checks/bugprone/infinite-loop>` check by adding detection for 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 index a52bbe2246d1e..140c93f5c2536 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp @@ -948,7 +948,7 @@ const auto throw_in_noexcept_lambda = [] () noexcept { throw 42; }; // CHECK-MESSAGES: :[[@LINE-1]]:39: warning: an exception may be thrown in function 'operator()' which should not throw exceptions // CHECK-MESSAGES: :[[@LINE-2]]:56: note: frame #0: unhandled exception of type 'int' may be thrown in function 'operator()' here -void thrower() { +int thrower() { throw 42; } @@ -956,3 +956,54 @@ const auto indirect_throw_in_noexcept_lambda = [] () noexcept { thrower(); }; // CHECK-MESSAGES: :[[@LINE-1]]:48: warning: an exception may be thrown in function 'operator()' which should not throw exceptions // CHECK-MESSAGES: :[[@LINE-5]]:3: note: frame #0: unhandled exception of type 'int' may be thrown in function 'thrower' here // CHECK-MESSAGES: :[[@LINE-3]]:65: note: frame #1: function 'operator()' calls function 'thrower' here + +int f(int); +void throw_in_function_arg() noexcept { +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_in_function_arg' which should not throw exceptions + f(false ? 0 : throw 1); +} +// CHECK-MESSAGES: :[[@LINE-2]]:17: note: frame #0: unhandled exception of type 'int' may be thrown in function 'throw_in_function_arg' here + +int g(int, int, int); +void throw_in_last_function_arg() noexcept { +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_in_last_function_arg' which should not throw exceptions + g(42, 67, false ? 0 : throw 1); +} +// CHECK-MESSAGES: :[[@LINE-2]]:25: note: frame #0: unhandled exception of type 'int' may be thrown in function 'throw_in_last_function_arg' here + +void indirect_throw_in_function_arg() noexcept { +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_throw_in_function_arg' which should not throw exceptions + f(thrower()); +} +// CHECK-MESSAGES: :[[@LINE-26]]:3: note: frame #0: unhandled exception of type 'int' may be thrown in function 'thrower' here +// CHECK-MESSAGES: :[[@LINE-3]]:5: note: frame #1: function 'indirect_throw_in_function_arg' calls function 'thrower' here + +void indirect_throw_from_lambda_in_function_arg() noexcept { +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_throw_from_lambda_in_function_arg' which should not throw exceptions + f([] { throw 1; return 0; }()); +} +// CHECK-MESSAGES: :[[@LINE-2]]:10: note: frame #0: unhandled exception of type 'int' may be thrown in function 'operator()' here +// CHECK-MESSAGES: :[[@LINE-3]]:30: note: frame #1: function 'indirect_throw_from_lambda_in_function_arg' calls function 'operator()' here + +struct S { + S(int) noexcept {} +}; + +void throw_in_constructor_arg() noexcept { +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_in_constructor_arg' which should not throw exceptions + S s(false ? 0 : throw 1); +} +// CHECK-MESSAGES: :[[@LINE-2]]:19: note: frame #0: unhandled exception of type 'int' may be thrown in function 'throw_in_constructor_arg' here + +void indirect_throw_in_constructor_arg() noexcept { +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_throw_in_constructor_arg' which should not throw exceptions + S s = thrower(); +} +// CHECK-MESSAGES: :[[@LINE-50]]:3: note: frame #0: unhandled exception of type 'int' may be thrown in function 'thrower' here +// CHECK-MESSAGES: :[[@LINE-3]]:9: note: frame #1: function 'indirect_throw_in_constructor_arg' calls function 'thrower' here + +void weird_throw_in_call_subexpression() noexcept { +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'weird_throw_in_call_subexpression' which should not throw exceptions + (false ? []{} : throw 1)(); +} +// CHECK-MESSAGES: :[[@LINE-2]]:19: note: frame #0: unhandled exception of type 'int' may be thrown in function 'weird_throw_in_call_subexpression' here _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
