Skip to content

Commit 577b519

Browse files
authored
[clang-tidy] Fix bugprone-exception-escape not diagnosing throws in argument lists (#165955)
Fixes #165766.
1 parent 8d950d2 commit 577b519

File tree

3 files changed

+70
-13
lines changed

3 files changed

+70
-13
lines changed

clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -562,17 +562,6 @@ ExceptionAnalyzer::throwsException(const Stmt *St,
562562
}
563563
}
564564
Results.merge(Uncaught);
565-
} else if (const auto *Call = dyn_cast<CallExpr>(St)) {
566-
if (const FunctionDecl *Func = Call->getDirectCallee()) {
567-
const ExceptionInfo Excs =
568-
throwsException(Func, Caught, CallStack, Call->getBeginLoc());
569-
Results.merge(Excs);
570-
}
571-
} else if (const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
572-
const ExceptionInfo Excs =
573-
throwsException(Construct->getConstructor(), Caught, CallStack,
574-
Construct->getBeginLoc());
575-
Results.merge(Excs);
576565
} else if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
577566
const ExceptionInfo Excs =
578567
throwsException(DefaultInit->getExpr(), Caught, CallStack);
@@ -602,10 +591,25 @@ ExceptionAnalyzer::throwsException(const Stmt *St,
602591
Results.merge(Excs);
603592
}
604593
} else {
594+
// Check whether any of this node's subexpressions throws.
605595
for (const Stmt *Child : St->children()) {
606596
const ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
607597
Results.merge(Excs);
608598
}
599+
600+
// If this node is a call to a function or constructor, also check
601+
// whether the call itself throws.
602+
if (const auto *Call = dyn_cast<CallExpr>(St)) {
603+
if (const FunctionDecl *Func = Call->getDirectCallee()) {
604+
ExceptionInfo Excs =
605+
throwsException(Func, Caught, CallStack, Call->getBeginLoc());
606+
Results.merge(Excs);
607+
}
608+
} else if (const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
609+
ExceptionInfo Excs = throwsException(Construct->getConstructor(), Caught,
610+
CallStack, Construct->getBeginLoc());
611+
Results.merge(Excs);
612+
}
609613
}
610614
return Results;
611615
}

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,9 @@ Changes in existing checks
300300
- Improved :doc:`bugprone-exception-escape
301301
<clang-tidy/checks/bugprone/exception-escape>` check's handling of lambdas:
302302
exceptions from captures are now diagnosed, exceptions in the bodies of
303-
lambdas that aren't actually invoked are not.
303+
lambdas that aren't actually invoked are not. Additionally, fixed an issue
304+
where the check wouldn't diagnose throws in arguments to functions or
305+
constructors.
304306

305307
- Improved :doc:`bugprone-infinite-loop
306308
<clang-tidy/checks/bugprone/infinite-loop>` check by adding detection for

clang-tools-extra/test/clang-tidy/checkers/bugprone/exception-escape.cpp

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -948,11 +948,62 @@ const auto throw_in_noexcept_lambda = [] () noexcept { throw 42; };
948948
// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: an exception may be thrown in function 'operator()' which should not throw exceptions
949949
// CHECK-MESSAGES: :[[@LINE-2]]:56: note: frame #0: unhandled exception of type 'int' may be thrown in function 'operator()' here
950950

951-
void thrower() {
951+
int thrower() {
952952
throw 42;
953953
}
954954

955955
const auto indirect_throw_in_noexcept_lambda = [] () noexcept { thrower(); };
956956
// CHECK-MESSAGES: :[[@LINE-1]]:48: warning: an exception may be thrown in function 'operator()' which should not throw exceptions
957957
// CHECK-MESSAGES: :[[@LINE-5]]:3: note: frame #0: unhandled exception of type 'int' may be thrown in function 'thrower' here
958958
// CHECK-MESSAGES: :[[@LINE-3]]:65: note: frame #1: function 'operator()' calls function 'thrower' here
959+
960+
int f(int);
961+
void throw_in_function_arg() noexcept {
962+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_in_function_arg' which should not throw exceptions
963+
f(false ? 0 : throw 1);
964+
}
965+
// CHECK-MESSAGES: :[[@LINE-2]]:17: note: frame #0: unhandled exception of type 'int' may be thrown in function 'throw_in_function_arg' here
966+
967+
int g(int, int, int);
968+
void throw_in_last_function_arg() noexcept {
969+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_in_last_function_arg' which should not throw exceptions
970+
g(42, 67, false ? 0 : throw 1);
971+
}
972+
// CHECK-MESSAGES: :[[@LINE-2]]:25: note: frame #0: unhandled exception of type 'int' may be thrown in function 'throw_in_last_function_arg' here
973+
974+
void indirect_throw_in_function_arg() noexcept {
975+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_throw_in_function_arg' which should not throw exceptions
976+
f(thrower());
977+
}
978+
// CHECK-MESSAGES: :[[@LINE-26]]:3: note: frame #0: unhandled exception of type 'int' may be thrown in function 'thrower' here
979+
// CHECK-MESSAGES: :[[@LINE-3]]:5: note: frame #1: function 'indirect_throw_in_function_arg' calls function 'thrower' here
980+
981+
void indirect_throw_from_lambda_in_function_arg() noexcept {
982+
// 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
983+
f([] { throw 1; return 0; }());
984+
}
985+
// CHECK-MESSAGES: :[[@LINE-2]]:10: note: frame #0: unhandled exception of type 'int' may be thrown in function 'operator()' here
986+
// CHECK-MESSAGES: :[[@LINE-3]]:30: note: frame #1: function 'indirect_throw_from_lambda_in_function_arg' calls function 'operator()' here
987+
988+
struct S {
989+
S(int) noexcept {}
990+
};
991+
992+
void throw_in_constructor_arg() noexcept {
993+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_in_constructor_arg' which should not throw exceptions
994+
S s(false ? 0 : throw 1);
995+
}
996+
// CHECK-MESSAGES: :[[@LINE-2]]:19: note: frame #0: unhandled exception of type 'int' may be thrown in function 'throw_in_constructor_arg' here
997+
998+
void indirect_throw_in_constructor_arg() noexcept {
999+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'indirect_throw_in_constructor_arg' which should not throw exceptions
1000+
S s = thrower();
1001+
}
1002+
// CHECK-MESSAGES: :[[@LINE-50]]:3: note: frame #0: unhandled exception of type 'int' may be thrown in function 'thrower' here
1003+
// CHECK-MESSAGES: :[[@LINE-3]]:9: note: frame #1: function 'indirect_throw_in_constructor_arg' calls function 'thrower' here
1004+
1005+
void weird_throw_in_call_subexpression() noexcept {
1006+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'weird_throw_in_call_subexpression' which should not throw exceptions
1007+
(false ? []{} : throw 1)();
1008+
}
1009+
// CHECK-MESSAGES: :[[@LINE-2]]:19: note: frame #0: unhandled exception of type 'int' may be thrown in function 'weird_throw_in_call_subexpression' here

0 commit comments

Comments
 (0)