Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang-tools-extra/clang-tidy/utils/ExceptionAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,11 @@ ExceptionAnalyzer::throwsException(const Stmt *St,
Results.merge(DestructorExcs);
}
}
} else if (const auto *Lambda = dyn_cast<LambdaExpr>(St)) {
for (const Stmt *Init : Lambda->capture_inits()) {
ExceptionInfo Excs = throwsException(Init, Caught, CallStack);
Results.merge(Excs);
}
} else {
for (const Stmt *Child : St->children()) {
ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,11 @@ Changes in existing checks
correcting a spelling mistake on its option
``NamePrefixSuffixSilenceDissimilarityTreshold``.

- 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.

- Improved :doc:`bugprone-infinite-loop
<clang-tidy/checks/bugprone/infinite-loop>` check by adding detection for
variables introduced by structured bindings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -894,3 +894,41 @@ void pointer_exception_can_not_escape_with_void_handler() noexcept {
} catch (void *) {
}
}

void throw_in_uninvoked_lambda() noexcept {
[] { throw 42; };
}

void throw_in_lambda() noexcept {
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: an exception may be thrown in function 'throw_in_lambda' which should not throw exceptions
[] { throw 42; }();
// CHECK-MESSAGES: :[[@LINE-1]]:8: note: frame #0: unhandled exception of type 'int' may be thrown in function 'operator()' here
// CHECK-MESSAGES: :[[@LINE-2]]:19: note: frame #1: function 'throw_in_lambda' calls function 'operator()' here
}

struct copy_constructor_throws {
copy_constructor_throws(const copy_constructor_throws&) { throw 42; }
};

void throw_in_lambda_default_by_value_capture(const copy_constructor_throws& a) noexcept {
// 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
[=] { a; };
// CHECK-MESSAGES: :[[@LINE-6]]:61: note: frame #0: unhandled exception of type 'int' may be thrown in function 'copy_constructor_throws' here
// CHECK-MESSAGES: :[[@LINE-2]]:4: note: frame #1: function 'throw_in_lambda_default_by_value_capture' calls function 'copy_constructor_throws' here
}

void throw_in_lambda_explicit_by_value_capture(const copy_constructor_throws& a) noexcept {
// 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
[a] {};
// CHECK-MESSAGES: :[[@LINE-13]]:61: note: frame #0: unhandled exception of type 'int' may be thrown in function 'copy_constructor_throws' here
// CHECK-MESSAGES: :[[@LINE-2]]:4: note: frame #1: function 'throw_in_lambda_explicit_by_value_capture' calls function 'copy_constructor_throws' here
}

void no_throw_in_lambda_by_reference_capture(const copy_constructor_throws& a) noexcept {
[&] { a; };
[&a] {};
}

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
Loading