}
ExceptionAnalyzer::ExceptionInfo ExceptionAnalyzer::throwsException(
- const FunctionDecl *Func,
+ const FunctionDecl *Func, const ExceptionInfo::Throwables &Caught,
llvm::SmallSet<const FunctionDecl *, 32> &CallStack) {
if (CallStack.count(Func))
return ExceptionInfo::createNonThrowing();
if (const Stmt *Body = Func->getBody()) {
CallStack.insert(Func);
- ExceptionInfo Result =
- throwsException(Body, ExceptionInfo::Throwables(), CallStack);
+ ExceptionInfo Result = throwsException(Body, Caught, CallStack);
// For a constructor, we also have to check the initializers.
if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Func)) {
for (const CXXCtorInitializer *Init : Ctor->inits()) {
- ExceptionInfo Excs = throwsException(
- Init->getInit(), ExceptionInfo::Throwables(), CallStack);
+ ExceptionInfo Excs =
+ throwsException(Init->getInit(), Caught, CallStack);
Result.merge(Excs);
}
}
Results.merge(Uncaught);
} else if (const auto *Call = dyn_cast<CallExpr>(St)) {
if (const FunctionDecl *Func = Call->getDirectCallee()) {
- ExceptionInfo Excs = throwsException(Func, CallStack);
+ ExceptionInfo Excs = throwsException(Func, Caught, CallStack);
Results.merge(Excs);
}
} else if (const auto *Construct = dyn_cast<CXXConstructExpr>(St)) {
ExceptionInfo Excs =
- throwsException(Construct->getConstructor(), CallStack);
+ throwsException(Construct->getConstructor(), Caught, CallStack);
Results.merge(Excs);
} else if (const auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(St)) {
ExceptionInfo Excs =
Results.merge(Excs);
} else if (const auto *Coro = dyn_cast<CoroutineBodyStmt>(St)) {
for (const Stmt *Child : Coro->childrenExclBody()) {
- ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
- Results.merge(Excs);
+ if (Child != Coro->getExceptionHandler()) {
+ ExceptionInfo Excs = throwsException(Child, Caught, CallStack);
+ Results.merge(Excs);
+ }
}
ExceptionInfo Excs = throwsException(Coro->getBody(), Caught, CallStack);
+ Results.merge(throwsException(Coro->getExceptionHandler(),
+ Excs.getExceptionTypes(), CallStack));
for (const Type *Throwable : Excs.getExceptionTypes()) {
if (const auto ThrowableRec = Throwable->getAsCXXRecordDecl()) {
ExceptionInfo DestructorExcs =
- throwsException(ThrowableRec->getDestructor(), CallStack);
+ throwsException(ThrowableRec->getDestructor(), Caught, CallStack);
Results.merge(DestructorExcs);
}
}
const auto CacheEntry = FunctionCache.find(Func);
if (CacheEntry == FunctionCache.end()) {
llvm::SmallSet<const FunctionDecl *, 32> CallStack;
- ExceptionList = throwsException(Func, CallStack);
+ ExceptionList =
+ throwsException(Func, ExceptionInfo::Throwables(), CallStack);
// Cache the result of the analysis. This is done prior to filtering
// because it is best to keep as much information as possible.
template <typename Task, typename T, bool ThrowInPromiseConstructor,
bool ThrowInInitialSuspend, bool ThrowInGetReturnObject,
- bool ThrowInUnhandledException>
+ bool ThrowInUnhandledException, bool RethrowInUnhandledException>
struct Promise;
template <
typename T, bool ThrowInTaskConstructor = false,
bool ThrowInPromiseConstructor = false, bool ThrowInInitialSuspend = false,
- bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false>
+ bool ThrowInGetReturnObject = false, bool ThrowInUnhandledException = false,
+ bool RethrowInUnhandledException = false>
struct Task {
using promise_type =
Promise<Task, T, ThrowInPromiseConstructor, ThrowInInitialSuspend,
- ThrowInGetReturnObject, ThrowInUnhandledException>;
+ ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
explicit Task(promise_type &p) {
if constexpr (ThrowInTaskConstructor) {
template <bool ThrowInTaskConstructor, bool ThrowInPromiseConstructor,
bool ThrowInInitialSuspend, bool ThrowInGetReturnObject,
- bool ThrowInUnhandledException>
+ bool ThrowInUnhandledException, bool RethrowInUnhandledException>
struct Task<void, ThrowInTaskConstructor, ThrowInPromiseConstructor,
ThrowInInitialSuspend, ThrowInGetReturnObject,
- ThrowInUnhandledException> {
+ ThrowInUnhandledException, RethrowInUnhandledException> {
using promise_type =
Promise<Task, void, ThrowInPromiseConstructor, ThrowInInitialSuspend,
- ThrowInGetReturnObject, ThrowInUnhandledException>;
+ ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException>;
explicit Task(promise_type &p) {
if constexpr (ThrowInTaskConstructor) {
template <typename Task, typename T, bool ThrowInPromiseConstructor,
bool ThrowInInitialSuspend, bool ThrowInGetReturnObject,
- bool ThrowInUnhandledException>
+ bool ThrowInUnhandledException, bool RethrowInUnhandledException>
struct Promise {
Promise() {
if constexpr (ThrowInPromiseConstructor) {
void unhandled_exception() {
if constexpr (ThrowInUnhandledException) {
throw 1;
+ } else if constexpr (RethrowInUnhandledException) {
+ throw;
}
}
template <typename Task, bool ThrowInPromiseConstructor,
bool ThrowInInitialSuspend, bool ThrowInGetReturnObject,
- bool ThrowInUnhandledException>
+ bool ThrowInUnhandledException, bool RethrowInUnhandledException>
struct Promise<Task, void, ThrowInPromiseConstructor, ThrowInInitialSuspend,
- ThrowInGetReturnObject, ThrowInUnhandledException> {
+ ThrowInGetReturnObject, ThrowInUnhandledException, RethrowInUnhandledException> {
Promise() {
if constexpr (ThrowInPromiseConstructor) {
throw 1;
void unhandled_exception() {
if constexpr (ThrowInUnhandledException) {
throw 1;
+ } else if constexpr (RethrowInUnhandledException) {
+ throw;
}
}
co_return a / b;
}
+Task<int, false, false, false, false, false, true>
+i_ShouldNotDiag(const int a, const int b) {
+ co_return a / b;
+}
+
+Task<int, false, false, false, false, false, true>
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+ co_return a / b;
+}
+
+Task<int, false, false, false, false, false, true>
+j_ShouldNotDiag(const int a, const int b) {
+ if (b == 0)
+ throw b;
+
+ co_return a / b;
+}
+
+Task<int, false, false, false, false, false, true>
+j_ShouldDiag(const int a, const int b) noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: an exception may be thrown in function 'j_ShouldDiag' which should not throw exceptions
+ if (b == 0)
+ throw b;
+
+ co_return a / b;
+}
+
} // namespace coreturn
namespace coyield {
co_yield a / b;
}
+Task<int, false, false, false, false, false, true>
+i_ShouldNotDiag(const int a, const int b) {
+ co_yield a / b;
+}
+
+Task<int, false, false, false, false, false, true>
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+ co_yield a / b;
+}
+
+Task<int, false, false, false, false, false, true>
+j_ShouldNotDiag(const int a, const int b) {
+ if (b == 0)
+ throw b;
+
+ co_yield a / b;
+}
+
+Task<int, false, false, false, false, false, true>
+j_ShouldDiag(const int a, const int b) noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: an exception may be thrown in function 'j_ShouldDiag' which should not throw exceptions
+ if (b == 0)
+ throw b;
+
+ co_yield a / b;
+}
+
} // namespace coyield
namespace coawait {
co_await returnOne();
}
+Task<int, false, false, false, false, false, true>
+i_ShouldNotDiag(const int a, const int b) {
+ co_await returnOne();
+}
+
+Task<int, false, false, false, false, false, true>
+i_ShouldNotDiagNoexcept(const int a, const int b) noexcept {
+ co_await returnOne();
+}
+
+Task<int, false, false, false, false, false, true>
+j_ShouldNotDiag(const int a, const int b) {
+ co_await returnOne();
+ if (b == 0)
+ throw b;
+}
+
+Task<int, false, false, false, false, false, true>
+j_ShouldDiag(const int a, const int b) noexcept {
+ // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: an exception may be thrown in function 'j_ShouldDiag' which should not throw exceptions
+ co_await returnOne();
+ if (b == 0)
+ throw b;
+}
+
} // namespace coawait
} // namespace function
co_return a / b;
};
+const auto i_ShouldNotDiag =
+ [](const int a,
+ const int b) -> Task<int, false, false, false, false, false, true> {
+ co_return a / b;
+};
+
+const auto i_ShouldNotDiagNoexcept =
+ [](const int a,
+ const int b) noexcept -> Task<int, false, false, false, false, false, true> {
+ co_return a / b;
+};
+
+const auto j_ShouldNotDiag =
+ [](const int a,
+ const int b) -> Task<int, false, false, false, false, false, true> {
+ if (b == 0)
+ throw b;
+
+ co_return a / b;
+};
+
+const auto j_ShouldDiag =
+ [](const int a,
+ const int b) noexcept -> Task<int, false, false, false, false, false, true> {
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in function 'operator()' which should not throw exceptions
+ if (b == 0)
+ throw b;
+
+ co_return a / b;
+};
+
} // namespace coreturn
namespace coyield {
co_yield a / b;
};
+const auto i_ShouldNotDiag =
+ [](const int a,
+ const int b) -> Task<int, false, false, false, false, false, true> {
+ co_yield a / b;
+};
+
+const auto i_ShouldNotDiagNoexcept =
+ [](const int a,
+ const int b) noexcept -> Task<int, false, false, false, false, false, true> {
+ co_yield a / b;
+};
+
+const auto j_ShouldNotDiag =
+ [](const int a,
+ const int b) -> Task<int, false, false, false, false, false, true> {
+ if (b == 0)
+ throw b;
+
+ co_yield a / b;
+};
+
+const auto j_ShouldDiag =
+ [](const int a,
+ const int b) noexcept -> Task<int, false, false, false, false, false, true> {
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in function 'operator()' which should not throw exceptions
+ if (b == 0)
+ throw b;
+
+ co_yield a / b;
+};
+
} // namespace coyield
namespace coawait {
co_await returnOne();
};
+const auto i_ShouldNotDiag =
+ [](const int a,
+ const int b) -> Task<int, false, false, false, false, false, true> {
+ co_await returnOne();
+};
+
+const auto i_ShouldNotDiagNoexcept =
+ [](const int a,
+ const int b) noexcept -> Task<int, false, false, false, false, false, true> {
+ co_await returnOne();
+};
+
+const auto j_ShouldNotDiag =
+ [](const int a,
+ const int b) -> Task<int, false, false, false, false, false, true> {
+ co_await returnOne();
+ if (b == 0)
+ throw b;
+};
+
+const auto j_ShouldDiag =
+ [](const int a,
+ const int b) noexcept -> Task<int, false, false, false, false, false, true> {
+ // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: an exception may be thrown in function 'operator()' which should not throw exceptions
+ co_await returnOne();
+ if (b == 0)
+ throw b;
+};
+
} // namespace coawait
} // namespace lambda