Skip to content

Commit aba40fb

Browse files
authored
[coroutines] Do not check coroutine wrappers for skipped function bodies (#76729)
Without function bodies, we cannot tell whether a function is a coroutine or not. The analysis of coroutine wrappers is not useful when this information is not available. We therefore now skip this analysis for skipped function bodies.
1 parent 43a5c4a commit aba40fb

File tree

2 files changed

+58
-3
lines changed

2 files changed

+58
-3
lines changed

clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,62 @@ TEST(DiagnosticTest, MakeUnique) {
420420
"no matching constructor for initialization of 'S'")));
421421
}
422422

423+
TEST(DiagnosticTest, CoroutineInHeader) {
424+
StringRef CoroutineH = R"cpp(
425+
namespace std {
426+
template <class Ret, typename... T>
427+
struct coroutine_traits { using promise_type = typename Ret::promise_type; };
428+
429+
template <class Promise = void>
430+
struct coroutine_handle {
431+
static coroutine_handle from_address(void *) noexcept;
432+
static coroutine_handle from_promise(Promise &promise);
433+
constexpr void* address() const noexcept;
434+
};
435+
template <>
436+
struct coroutine_handle<void> {
437+
template <class PromiseType>
438+
coroutine_handle(coroutine_handle<PromiseType>) noexcept;
439+
static coroutine_handle from_address(void *);
440+
constexpr void* address() const noexcept;
441+
};
442+
443+
struct awaitable {
444+
bool await_ready() noexcept { return false; }
445+
void await_suspend(coroutine_handle<>) noexcept {}
446+
void await_resume() noexcept {}
447+
};
448+
} // namespace std
449+
)cpp";
450+
451+
StringRef Header = R"cpp(
452+
#include "coroutine.h"
453+
template <typename T> struct [[clang::coro_return_type]] Gen {
454+
struct promise_type {
455+
Gen<T> get_return_object() {
456+
return {};
457+
}
458+
std::awaitable initial_suspend();
459+
std::awaitable final_suspend() noexcept;
460+
void unhandled_exception();
461+
void return_value(T t);
462+
};
463+
};
464+
465+
Gen<int> foo_coro(int b) { co_return b; }
466+
)cpp";
467+
Annotations Main(R"cpp(
468+
// error-ok
469+
#include "header.hpp"
470+
Gen<int> $[[bar_coro]](int b) { return foo_coro(b); }
471+
)cpp");
472+
TestTU TU = TestTU::withCode(Main.code());
473+
TU.AdditionalFiles["coroutine.h"] = std::string(CoroutineH);
474+
TU.AdditionalFiles["header.hpp"] = std::string(Header);
475+
TU.ExtraArgs.push_back("--std=c++20");
476+
EXPECT_THAT(TU.build().getDiagnostics(), ElementsAre(hasRange(Main.range())));
477+
}
478+
423479
TEST(DiagnosticTest, MakeShared) {
424480
// We usually miss diagnostics from header functions as we don't parse them.
425481
// std::make_shared is only parsed when --parse-forwarding-functions is set

clang/lib/Sema/SemaDecl.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15845,8 +15845,6 @@ static void diagnoseImplicitlyRetainedSelf(Sema &S) {
1584515845
}
1584615846

1584715847
void Sema::CheckCoroutineWrapper(FunctionDecl *FD) {
15848-
if (!FD)
15849-
return;
1585015848
RecordDecl *RD = FD->getReturnType()->getAsRecordDecl();
1585115849
if (!RD || !RD->getUnderlyingDecl()->hasAttr<CoroReturnTypeAttr>())
1585215850
return;
@@ -15869,7 +15867,8 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
1586915867
sema::AnalysisBasedWarnings::Policy WP = AnalysisWarnings.getDefaultPolicy();
1587015868
sema::AnalysisBasedWarnings::Policy *ActivePolicy = nullptr;
1587115869

15872-
if (getLangOpts().Coroutines) {
15870+
// If we skip function body, we can't tell if a function is a coroutine.
15871+
if (getLangOpts().Coroutines && FD && !FD->hasSkippedBody()) {
1587315872
if (FSI->isCoroutine())
1587415873
CheckCompletedCoroutineBody(FD, Body);
1587515874
else

0 commit comments

Comments
 (0)