diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -509,6 +509,50 @@ {"template concept Fooable = true"}); } +TEST_F(TargetDeclTest, Coroutine) { + Flags.push_back("-std=c++20"); + + Code = R"cpp( + namespace std::experimental { + template struct coroutine_traits; + template struct coroutine_handle { + template + coroutine_handle(coroutine_handle&&) noexcept; + static coroutine_handle from_address(void* __addr) noexcept; + }; + } // namespace std::experimental + + struct executor {}; + struct awaitable {}; + struct awaitable_frame { + awaitable get_return_object(); + void return_void(); + void unhandled_exception(); + struct result_t { + ~result_t(); + bool await_ready() const noexcept; + void await_suspend(std::experimental::coroutine_handle) noexcept; + void await_resume() const noexcept; + }; + result_t initial_suspend() noexcept; + result_t final_suspend() noexcept; + result_t await_transform(executor) noexcept; + }; + + namespace std::experimental { + template <> + struct coroutine_traits { + typedef awaitable_frame promise_type; + }; + } // namespace std::experimental + + awaitable foo() { + co_await [[executor]](); + } + )cpp"; + EXPECT_DECLS("RecordTypeLoc", "struct executor"); +} + TEST_F(TargetDeclTest, FunctionTemplate) { Code = R"cpp( // Implicit specialization. diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h --- a/clang/include/clang/AST/ExprCXX.h +++ b/clang/include/clang/AST/ExprCXX.h @@ -4725,6 +4725,8 @@ return static_cast(SubExprs[SubExpr::Common]); } + Expr *getOperand() const; + /// getOpaqueValue - Return the opaque value placeholder. OpaqueValueExpr *getOpaqueValue() const { return OpaqueValue; } @@ -4743,7 +4745,7 @@ SourceLocation getBeginLoc() const LLVM_READONLY { return KeywordLoc; } SourceLocation getEndLoc() const LLVM_READONLY { - return getCommonExpr()->getEndLoc(); + return getOperand()->getEndLoc(); } child_range children() { @@ -4782,11 +4784,6 @@ CoawaitExpr(EmptyShell Empty) : CoroutineSuspendExpr(CoawaitExprClass, Empty) {} - Expr *getOperand() const { - // FIXME: Dig out the actual operand or store it. - return getCommonExpr(); - } - bool isImplicit() const { return CoawaitBits.IsImplicit; } void setIsImplicit(bool value = true) { CoawaitBits.IsImplicit = value; } @@ -4859,11 +4856,6 @@ CoyieldExpr(EmptyShell Empty) : CoroutineSuspendExpr(CoyieldExprClass, Empty) {} - Expr *getOperand() const { - // FIXME: Dig out the actual operand or store it. - return getCommonExpr(); - } - static bool classof(const Stmt *T) { return T->getStmtClass() == CoyieldExprClass; } diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp --- a/clang/lib/AST/ExprCXX.cpp +++ b/clang/lib/AST/ExprCXX.cpp @@ -1742,3 +1742,31 @@ alignof(CUDAKernelCallExpr)); return new (Mem) CUDAKernelCallExpr(NumArgs, HasFPFeatures, Empty); } + +Expr *CoroutineSuspendExpr::getOperand() const { + // Dig out the actual operand from the common-expr. + Expr *Result = getCommonExpr(); + // Unwrap MaterializeTemporaryExpr if present. + if (const auto *MTE = dyn_cast(Result)) { + Result = MTE->getSubExpr(); + } + // Unwrap CXXBindTemporaryExpr if present. + if (auto *BTE = dyn_cast(Result)) { + Result = BTE->getSubExpr(); + } + // FIXME: Unwrap `operator co_await`. + // Unwrap `promise.await_transform(operand)` to recover `operand`. + if (auto *MCE = dyn_cast(Result)) { + if (const auto *ME = dyn_cast(MCE->getCallee())) { + if (const auto *II = + ME->getMemberDecl()->getDeclName().getAsIdentifierInfo()) { + if (II->getName() == "await_transform") { + if (MCE->getNumArgs() == 1) { + Result = MCE->getArg(0); + } + } + } + } + } + return Result; +}