Skip to content

Commit 0e93ab8

Browse files
authored
[clang-tidy] Improve bugprone-exception-escape's handling of lambdas (#160592)
Fixes #132605.
1 parent 9534190 commit 0e93ab8

File tree

3 files changed

+72
-0
lines changed

3 files changed

+72
-0
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,11 @@ ExceptionAnalyzer::throwsException(const Stmt *St,
595595
Results.merge(DestructorExcs);
596596
}
597597
}
598+
} else if (const auto *Lambda = dyn_cast<LambdaExpr>(St)) {
599+
for (const Stmt *Init : Lambda->capture_inits()) {
600+
ExceptionInfo Excs = throwsException(Init, Caught, CallStack);
601+
Results.merge(Excs);
602+
}
598603
} else {
599604
for (const Stmt *Child : St->children()) {
600605
ExceptionInfo Excs = throwsException(Child, Caught, CallStack);

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,11 @@ Changes in existing checks
244244
correcting a spelling mistake on its option
245245
``NamePrefixSuffixSilenceDissimilarityTreshold``.
246246

247+
- Improved :doc:`bugprone-exception-escape
248+
<clang-tidy/checks/bugprone/exception-escape>` check's handling of lambdas:
249+
exceptions from captures are now diagnosed, exceptions in the bodies of
250+
lambdas that aren't actually invoked are not.
251+
247252
- Improved :doc:`bugprone-infinite-loop
248253
<clang-tidy/checks/bugprone/infinite-loop>` check by adding detection for
249254
variables introduced by structured bindings.

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

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,3 +894,65 @@ void pointer_exception_can_not_escape_with_void_handler() noexcept {
894894
} catch (void *) {
895895
}
896896
}
897+
898+
void throw_in_uninvoked_lambda() noexcept {
899+
[] { throw 42; };
900+
}
901+
902+
void throw_in_lambda() noexcept {
903+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_in_lambda' which should not throw exceptions
904+
[] { throw 42; }();
905+
// CHECK-MESSAGES: :[[@LINE-1]]:8: note: frame #0: unhandled exception of type 'int' may be thrown in function 'operator()' here
906+
// CHECK-MESSAGES: :[[@LINE-2]]:19: note: frame #1: function 'throw_in_lambda' calls function 'operator()' here
907+
}
908+
909+
struct copy_constructor_throws {
910+
copy_constructor_throws(const copy_constructor_throws&) { throw 42; }
911+
};
912+
913+
void throw_in_lambda_default_by_value_capture(const copy_constructor_throws& a) noexcept {
914+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_in_lambda_default_by_value_capture' which should not throw exceptions
915+
[=] { a; };
916+
// CHECK-MESSAGES: :[[@LINE-6]]:61: note: frame #0: unhandled exception of type 'int' may be thrown in function 'copy_constructor_throws' here
917+
// CHECK-MESSAGES: :[[@LINE-2]]:4: note: frame #1: function 'throw_in_lambda_default_by_value_capture' calls function 'copy_constructor_throws' here
918+
}
919+
920+
void throw_in_lambda_explicit_by_value_capture(const copy_constructor_throws& a) noexcept {
921+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_in_lambda_explicit_by_value_capture' which should not throw exceptions
922+
[a] {};
923+
// CHECK-MESSAGES: :[[@LINE-13]]:61: note: frame #0: unhandled exception of type 'int' may be thrown in function 'copy_constructor_throws' here
924+
// CHECK-MESSAGES: :[[@LINE-2]]:4: note: frame #1: function 'throw_in_lambda_explicit_by_value_capture' calls function 'copy_constructor_throws' here
925+
}
926+
927+
void no_throw_in_lambda_by_reference_capture(const copy_constructor_throws& a) noexcept {
928+
[&] { a; };
929+
[&a] {};
930+
}
931+
932+
void throw_in_lambda_init_capture() noexcept {
933+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_in_lambda_init_capture' which should not throw exceptions
934+
[a = [] { throw 42; return 0; }()] {};
935+
// CHECK-MESSAGES: :[[@LINE-1]]:13: note: frame #0: unhandled exception of type 'int' may be thrown in function 'operator()' here
936+
// CHECK-MESSAGES: :[[@LINE-2]]:34: note: frame #1: function 'throw_in_lambda_init_capture' calls function 'operator()' here
937+
}
938+
939+
void throw_from_nested_lambda() noexcept {
940+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_from_nested_lambda' which should not throw exceptions
941+
[] { [] { throw 42; }(); }();
942+
// CHECK-MESSAGES: :[[@LINE-1]]:13: note: frame #0: unhandled exception of type 'int' may be thrown in function 'operator()' here
943+
// CHECK-MESSAGES: :[[@LINE-2]]:24: note: frame #1: function 'operator()' calls function 'operator()' here
944+
// CHECK-MESSAGES: :[[@LINE-3]]:29: note: frame #2: function 'throw_from_nested_lambda' calls function 'operator()' here
945+
}
946+
947+
const auto throw_in_noexcept_lambda = [] () noexcept { throw 42; };
948+
// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: an exception may be thrown in function 'operator()' which should not throw exceptions
949+
// CHECK-MESSAGES: :[[@LINE-2]]:56: note: frame #0: unhandled exception of type 'int' may be thrown in function 'operator()' here
950+
951+
void thrower() {
952+
throw 42;
953+
}
954+
955+
const auto indirect_throw_in_noexcept_lambda = [] () noexcept { thrower(); };
956+
// CHECK-MESSAGES: :[[@LINE-1]]:48: warning: an exception may be thrown in function 'operator()' which should not throw exceptions
957+
// CHECK-MESSAGES: :[[@LINE-5]]:3: note: frame #0: unhandled exception of type 'int' may be thrown in function 'thrower' here
958+
// CHECK-MESSAGES: :[[@LINE-3]]:65: note: frame #1: function 'operator()' calls function 'thrower' here

0 commit comments

Comments
 (0)