198 lines · cpp
1//===- unittests/Frontend/NoAlterCodeGenActionTest.cpp --------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Unit tests for CodeGenAction may not alter the AST.10//11//===----------------------------------------------------------------------===//12 13#include "clang/AST/ASTConsumer.h"14#include "clang/AST/RecursiveASTVisitor.h"15#include "clang/Basic/LangStandard.h"16#include "clang/CodeGen/BackendUtil.h"17#include "clang/CodeGen/CodeGenAction.h"18#include "clang/Frontend/CompilerInstance.h"19#include "clang/Frontend/MultiplexConsumer.h"20#include "clang/Lex/PreprocessorOptions.h"21#include "clang/Tooling/Tooling.h"22#include "llvm/Support/FormatVariadic.h"23#include "llvm/Support/VirtualFileSystem.h"24#include "gtest/gtest.h"25 26using namespace llvm;27using namespace clang;28using namespace clang::frontend;29using namespace clang::tooling;30 31namespace {32 33class ASTChecker : public RecursiveASTVisitor<ASTChecker> {34public:35 ASTContext &Ctx;36 ASTChecker(ASTContext &Ctx) : Ctx(Ctx) {}37 bool VisitReturnStmt(ReturnStmt *RS) {38 EXPECT_TRUE(RS->getRetValue());39 return true;40 }41 42 bool VisitCoroutineBodyStmt(CoroutineBodyStmt *CS) {43 return VisitReturnStmt(cast<ReturnStmt>(CS->getReturnStmt()));44 }45};46 47class ASTCheckerConsumer : public ASTConsumer {48public:49 void HandleTranslationUnit(ASTContext &Ctx) override {50 ASTChecker Checker(Ctx);51 Checker.TraverseAST(Ctx);52 }53};54 55class TestCodeGenAction : public EmitLLVMOnlyAction {56public:57 using Base = EmitLLVMOnlyAction;58 using Base::Base;59 60 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,61 StringRef InFile) override {62 std::vector<std::unique_ptr<ASTConsumer>> Consumers;63 Consumers.push_back(std::make_unique<ASTCheckerConsumer>());64 Consumers.push_back(Base::CreateASTConsumer(CI, InFile));65 return std::make_unique<MultiplexConsumer>(std::move(Consumers));66 }67};68 69const char *test_contents = R"cpp(70 71namespace std {72 73template <typename R, typename...> struct coroutine_traits {74 using promise_type = typename R::promise_type;75};76 77template <typename Promise = void> struct coroutine_handle;78 79template <> struct coroutine_handle<void> {80 static coroutine_handle from_address(void *addr) noexcept;81 void operator()() { resume(); }82 void *address() const noexcept;83 void resume() const { __builtin_coro_resume(ptr); }84 void destroy() const { __builtin_coro_destroy(ptr); }85 bool done() const;86 coroutine_handle &operator=(decltype(nullptr));87 coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}88 coroutine_handle() : ptr(nullptr) {}89// void reset() { ptr = nullptr; } // add to P0057?90 explicit operator bool() const;91 92protected:93 void *ptr;94};95 96template <typename Promise> struct coroutine_handle : coroutine_handle<> {97 using coroutine_handle<>::operator=;98 99 static coroutine_handle from_address(void *addr) noexcept;100 101 Promise &promise() const;102 static coroutine_handle from_promise(Promise &promise);103};104 105template <typename _PromiseT>106bool operator==(coroutine_handle<_PromiseT> const &_Left,107 coroutine_handle<_PromiseT> const &_Right) noexcept {108 return _Left.address() == _Right.address();109}110 111template <typename _PromiseT>112bool operator!=(coroutine_handle<_PromiseT> const &_Left,113 coroutine_handle<_PromiseT> const &_Right) noexcept {114 return !(_Left == _Right);115}116 117struct noop_coroutine_promise {};118 119template <>120struct coroutine_handle<noop_coroutine_promise> {121 operator coroutine_handle<>() const noexcept;122 123 constexpr explicit operator bool() const noexcept { return true; }124 constexpr bool done() const noexcept { return false; }125 126 constexpr void operator()() const noexcept {}127 constexpr void resume() const noexcept {}128 constexpr void destroy() const noexcept {}129 130 noop_coroutine_promise &promise() const noexcept {131 return *static_cast<noop_coroutine_promise *>(132 __builtin_coro_promise(this->__handle_, alignof(noop_coroutine_promise), false));133 }134 135 constexpr void *address() const noexcept { return __handle_; }136 137private:138 friend coroutine_handle<noop_coroutine_promise> noop_coroutine() noexcept;139 140 coroutine_handle() noexcept {141 this->__handle_ = __builtin_coro_noop();142 }143 144 void *__handle_ = nullptr;145};146 147using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;148 149inline noop_coroutine_handle noop_coroutine() noexcept { return noop_coroutine_handle(); }150 151struct suspend_always {152 bool await_ready() noexcept { return false; }153 void await_suspend(coroutine_handle<>) noexcept {}154 void await_resume() noexcept {}155};156struct suspend_never {157 bool await_ready() noexcept { return true; }158 void await_suspend(coroutine_handle<>) noexcept {}159 void await_resume() noexcept {}160};161 162} // namespace std163 164using namespace std;165 166class invoker {167public:168 class invoker_promise {169 public:170 invoker get_return_object() { return invoker{}; }171 auto initial_suspend() { return suspend_always{}; }172 auto final_suspend() noexcept { return suspend_always{}; }173 void return_void() {}174 void unhandled_exception() {}175 };176 using promise_type = invoker_promise;177 invoker() {}178 invoker(const invoker &) = delete;179 invoker &operator=(const invoker &) = delete;180 invoker(invoker &&) = delete;181 invoker &operator=(invoker &&) = delete;182};183 184invoker g() {185 co_return;186}187 188)cpp";189 190TEST(CodeGenTest, TestNonAlterTest) {191 EXPECT_TRUE(runToolOnCodeWithArgs(std::make_unique<TestCodeGenAction>(),192 test_contents,193 {194 "-std=c++20",195 }));196}197} // namespace198