diff --git a/CMakeLists.txt b/CMakeLists.txt index b843fe54..d984e223 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,9 +41,14 @@ if(WIN32) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") endif() -set(CLICE_CXX_FLAGS "-fno-rtti;-fno-exceptions;-Wno-deprecated-declarations") +set(CLICE_CXX_FLAGS "-fno-rtti;-fno-exceptions;-Wno-deprecated-declarations;-Wno-undefined-inline;") set(CLICE_LINKER_FLAGS "") +if(CLICE_USE_LIBCXX) + list(APPEND CLICE_CXX_FLAGS "-stdlib=libc++") + list(APPEND CLICE_LINKER_FLAGS "-stdlib=libc++") +endif() + # Build-type specific flags if(CMAKE_BUILD_TYPE STREQUAL "Debug") message(STATUS "Debug build: Enabling -g -O0 -fsanitize=address") @@ -131,14 +136,7 @@ if(CLICE_ENABLE_TEST) add_executable(unit_tests "${CLICE_TEST_SOURCES}" "${CMAKE_SOURCE_DIR}/src/Driver/unit_tests.cc") target_include_directories(unit_tests PUBLIC "${CMAKE_SOURCE_DIR}") - FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.17.x - ) - FetchContent_MakeAvailable(googletest) - - target_link_libraries(unit_tests PRIVATE gtest_main GTest::gtest clice-core) + target_link_libraries(unit_tests PRIVATE clice-core) target_compile_options(unit_tests PUBLIC ${CLICE_CXX_FLAGS}) target_link_options(unit_tests PUBLIC ${CLICE_LINKER_FLAGS}) diff --git a/include/Support/Binary.h b/include/Support/Binary.h index 31fe07cb..ee86d121 100644 --- a/include/Support/Binary.h +++ b/include/Support/Binary.h @@ -8,6 +8,7 @@ #include "Enum.h" #include "Struct.h" #include "Format.h" +#include "FixedString.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" @@ -216,28 +217,6 @@ struct Packer { } }; -template -struct fixed_string : std::array { - template - constexpr fixed_string(const char (&str)[M]) { - for(std::size_t i = 0; i < N; ++i) { - this->data()[i] = str[i]; - } - this->data()[N] = '\0'; - } - - constexpr auto size() const { - return N; - } - - constexpr operator std::string_view () const { - return {this->data(), N}; - } -}; - -template -fixed_string(const char (&)[M]) -> fixed_string; - /// A helper class to access the binary data. template struct Proxy { diff --git a/include/Support/FixedString.h b/include/Support/FixedString.h new file mode 100644 index 00000000..1c3cf833 --- /dev/null +++ b/include/Support/FixedString.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +namespace clice { + +template +struct fixed_string : std::array { + template + constexpr fixed_string(const char (&str)[M]) { + for(std::size_t i = 0; i < N; ++i) { + this->data()[i] = str[i]; + } + this->data()[N] = '\0'; + } + + constexpr auto size() const { + return N; + } + + constexpr operator std::string_view () const { + return {this->data(), N}; + } +}; + +template +fixed_string(const char (&)[M]) -> fixed_string; + +} // namespace clice diff --git a/include/Test/TExpr.h b/include/Test/TExpr.h new file mode 100644 index 00000000..5c1cfff2 --- /dev/null +++ b/include/Test/TExpr.h @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include +#include +#include "Support/Compare.h" +#include "Support/Ranges.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +namespace clice::testing { + +template +concept is_expr_v = requires { typename T::expr_tag; }; + +template +struct default_formatter : std::formatter { + using Base = std::formatter; + + template + auto format(const auto& value, FormatContext& ctx) const { + llvm::SmallString<256> buffer; + static_cast(this)->format_to(std::back_inserter(buffer), value); + return Base::format(std::string_view(buffer), ctx); + } +}; + +template +decltype(auto) compute(const Expr& expr) { + if constexpr(requires { typename Expr::expr_tag; }) { + return expr(); + } else { + return expr; + } +} + +} // namespace clice::testing + +#define BINARY_PREDICATE(name, op) \ + namespace clice::testing { \ + decltype(auto) name##_impl(auto&& lhs, auto&& rhs); \ + \ + template \ + struct name { \ + const LHS& lhs; \ + const RHS& rhs; \ + \ + using expr_tag = int; \ + \ + auto operator() () const { \ + return name##_impl(compute(lhs), compute(rhs)); \ + } \ + }; \ + \ + template \ + name(const LHS&, const RHS&) -> name; \ + } \ + \ + template \ + struct std::formatter> : \ + clice::testing::default_formatter>> { \ + void format_to(auto&& inserter, const auto& expr) const { \ + std::format_to(inserter, "{} " #op " {}", expr.lhs, expr.rhs); \ + } \ + }; \ + \ + decltype(auto) clice::testing::name##_impl(auto&& lhs, auto&& rhs) + +BINARY_PREDICATE(add, +) { + return lhs + rhs; +}; + +BINARY_PREDICATE(sub, -) { + return lhs - rhs; +} + +BINARY_PREDICATE(mul, *) { + return lhs * rhs; +} + +BINARY_PREDICATE(eq, ==) { + return refl::equal(lhs, rhs); +} + +BINARY_PREDICATE(ne, !=) { + return !refl::equal(lhs, rhs); +} + +BINARY_PREDICATE(lt, <) { + return refl::less(lhs, rhs); +} + +BINARY_PREDICATE(le, <=) { + return refl::less_equal(lhs, rhs); +} + +BINARY_PREDICATE(gt, >) { + return refl::less(rhs, lhs); +} + +BINARY_PREDICATE(ge, >=) { + return refl::less_equal(rhs, lhs); +} + +BINARY_PREDICATE(has, has) { + return ranges::contains(lhs, rhs); +} + +#undef BINARY_PREDICATE + diff --git a/include/Test/Test.h b/include/Test/Test.h index f73cadfd..3baca2a0 100644 --- a/include/Test/Test.h +++ b/include/Test/Test.h @@ -1,109 +1,141 @@ #pragma once -#include "gtest/gtest.h" +#include "TExpr.h" +#include "LocationChain.h" #include "Support/JSON.h" #include "Support/Format.h" #include "Support/Compare.h" #include "Support/FileSystem.h" -#include "Test/LocationChain.h" +#include "Support/FixedString.h" +#include "llvm/ADT/FunctionExtras.h" namespace clice::testing { -#undef EXPECT_TRUE -#undef EXPECT_FALSE -#undef ASSERT_TRUE -#undef ASSERT_FALSE -#undef EXPECT_EQ -#undef EXPECT_NE -#undef ASSERT_EQ -#undef ASSERT_NE +struct may_failure; -llvm::StringRef test_dir(); +class Runner { +public: + static Runner& instance(); -template -inline std::string diff(const LHS& lhs, const RHS& rhs) { - std::string left; - if constexpr(json::serializable) { - llvm::raw_string_ostream(left) << json::serialize(lhs); - } else { - left = "cannot dump value"; + using Suite = void (*)(); + using Test = llvm::unique_function; + + void add_suite(std::string_view name, Suite suite); + + void on_test(std::string_view name, Test test, bool skipped); + + /// Current test is failed, continue to execute the next test in the suite. + void fail(const may_failure& failure); + + bool fatal_error_occured() { + return curr_fatal; } - SCOPED_TRACE("Verifying link at index 0"); - std::string right; - if constexpr(json::serializable) { - llvm::raw_string_ostream(right) << json::serialize(rhs); - } else { - right = "cannot dump value"; + /// Run all test suites. + int run_tests(); + +private: + Runner() = default; + Runner(const Runner&) = delete; + Runner(Runner&&) = delete; + +private: + bool curr_failed = false; + bool skipped = false; + bool curr_fatal = false; + + /// Whether all tests in this test suite are skipped. + bool all_skipped = true; + + std::string curr_suite_name; + std::uint32_t curr_tests_count = 0; + std::uint32_t curr_failed_tests_count = 0; + std::uint32_t total_tests_count = 0; + std::uint32_t total_suites_count = 0; + std::uint32_t total_failed_tests_count = 0; + std::chrono::milliseconds curr_test_duration; + std::chrono::milliseconds total_test_duration; + std::unordered_map> suites; +}; + +template +struct suite { + template + suite(Suite suite) { + static_assert(std::convertible_to, "Suite must be stateless!"); + Runner::instance().add_suite(suite_name, suite); + } +}; + +struct test { + test(std::string_view name) : name(name) {} + + template + void operator= (Test&& test) { + Runner::instance().on_test(name, std::forward(test), skipped); } - return std::format("left : {}\nright: {}\n", left, right); -} + bool skipped = false; + std::string name; +}; -inline void EXPECT_FAILURE(std::string message, LocationChain chain = LocationChain()) { - chain.backtrace(); - GTEST_MESSAGE_AT_("", 0, message.c_str(), ::testing::TestPartResult::kNonFatalFailure); -} +struct may_failure { + bool failed = false; + bool fatal = false; + std::string expression; + std::source_location location; -inline void ASSERT_FAILURE(std::string message, LocationChain chain = LocationChain()) { - chain.backtrace(); - GTEST_MESSAGE_AT_("", 0, message.c_str(), ::testing::TestPartResult::kFatalFailure); - std::abort(); -} - -inline void EXPECT_TRUE(auto&& value, LocationChain chain = LocationChain()) { - if(!static_cast(value)) { - EXPECT_FAILURE("EXPECT true!", chain); + ~may_failure() { + Runner::instance().fail(*this); } -} +}; -inline void EXPECT_FALSE(auto&& value, LocationChain chain = LocationChain()) { - if(static_cast(value)) { - EXPECT_FAILURE("EXPECT false!", chain); - } -} +inline struct { + template + may_failure operator() (const TExpr& expr, + std::source_location location = std::source_location::current()) { + bool failed = false; + std::string expression = "false"; -inline void ASSERT_TRUE(auto&& value, LocationChain chain = LocationChain()) { - if(!static_cast(value)) { - ASSERT_FAILURE("ASSERT true!", chain); - if constexpr(requires { value.error(); }) { - clice::println("{}", value.error()); + if constexpr(is_expr_v) { + auto result = expr(); + if(!static_cast(result)) { + failed = true; + + /// TODO: use pretty print, if the expression is too long. + expression = std::format("{}", expr); + } + } else { + if(!static_cast(expr)) { + failed = true; + } } - } -} -inline void ASSERT_FALSE(auto&& value, LocationChain chain = LocationChain()) { - if(static_cast(value)) { - ASSERT_FAILURE("ASSERT false!", chain); + return may_failure{failed, false, expression, location}; } -} +} expect; -template -inline void EXPECT_EQ(const LHS& lhs, const RHS& rhs, LocationChain chain = LocationChain()) { - if(!refl::equal(lhs, rhs)) { - EXPECT_FAILURE(diff(lhs, rhs), chain); +inline struct { + test&& operator/ (test&& test) { + test.skipped = true; + return std::move(test); } -} +} skip; -template -inline void EXPECT_NE(const LHS& lhs, const RHS& rhs, LocationChain chain = LocationChain()) { - if(refl::equal(lhs, rhs)) { - EXPECT_FAILURE(diff(lhs, rhs), chain); +inline struct { + may_failure&& operator/ (may_failure&& failure) { + failure.fatal = true; + return std::move(failure); } -} +} fatal; -template -inline void ASSERT_EQ(const LHS& lhs, const RHS& rhs, LocationChain chain = LocationChain()) { - if(!refl::equal(lhs, rhs)) { - ASSERT_FAILURE(diff(lhs, rhs), chain); +struct that_t { + template + constexpr decltype(auto) operator% (const TExpr& expr) const { + return expr; } -} +}; -template -inline void ASSERT_NE(const LHS& lhs, const RHS& rhs, LocationChain chain = LocationChain()) { - if(refl::equal(lhs, rhs)) { - ASSERT_FAILURE(diff(lhs, rhs), chain); - } -} +inline that_t that; } // namespace clice::testing diff --git a/include/Test/Tester.h b/include/Test/Tester.h index 0c4875a1..17f5c79b 100644 --- a/include/Test/Tester.h +++ b/include/Test/Tester.h @@ -157,9 +157,15 @@ struct Tester { return ranges.lookup(name); } } -}; -struct TestFixture : ::testing::Test, Tester {}; + void clear() { + params = CompilationParams(); + database = CompilationDatabase(); + unit.reset(); + sources.all_files.clear(); + src_path.clear(); + } +}; } // namespace clice::testing diff --git a/src/Driver/unit_tests.cc b/src/Driver/unit_tests.cc index 2f7b2695..e6c5dc97 100644 --- a/src/Driver/unit_tests.cc +++ b/src/Driver/unit_tests.cc @@ -2,49 +2,182 @@ #include "llvm/ADT/SmallString.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Signals.h" +#include "Support/GlobPattern.h" +#include -namespace clice { +using namespace clice; +using namespace clice::testing; -namespace cl { +constexpr static std::string_view GREEN = "\033[32m"; +constexpr static std::string_view YELLOW = "\033[33m"; +constexpr static std::string_view RED = "\033[31m"; +constexpr static std::string_view CLEAR = "\033[0m"; -llvm::cl::opt test_dir("test-dir", - llvm::cl::desc("specify the test source directory path"), - llvm::cl::value_desc("path"), - llvm::cl::Required); +namespace { -llvm::cl::opt resource_dir("resource-dir", llvm::cl::desc("Resource dir path")); +namespace cl = llvm::cl; -} // namespace cl +cl::opt test_dir("test-dir", + cl::desc("specify the test source directory path"), + cl::value_desc("path"), + cl::Required); -namespace testing { +cl::opt resource_dir("resource-dir", cl::desc("Resource dir path")); -llvm::StringRef test_dir() { - return cl::test_dir; +cl::opt test_filter("test_filter"); + +cl::opt enable_example("enable-example", cl::init(false)); + +std::optional pattern; + +} // namespace + +namespace clice::testing { + +Runner& Runner::instance() { + static Runner runner; + return runner; } -} // namespace testing +void Runner::add_suite(std::string_view name, Suite suite) { + suites[name].emplace_back(suite); +} -} // namespace clice +void Runner::on_test(std::string_view name, Test test, bool skipped) { + std::string full_name = std::format("{}.{}", curr_suite_name, name); -int main(int argc, char** argv) { - using namespace clice; + /// If this test if filter, directly return. + if(pattern && !pattern->match(full_name)) { + return; + } + /// If there is any test in the suite, we print the suite start info. + if(all_skipped) { + std::println("{}[----------] tests from {}{}", GREEN, curr_suite_name, CLEAR); + all_skipped = false; + } + + if(skipped) { + /// If this test is marked as skipped, only print skip information. + std::println("{}[ SKIPPED ] {}{}", YELLOW, full_name, CLEAR); + return; + } + + /// Reset whether this test is failed or fatal. + curr_failed = false; + curr_fatal = false; + + using namespace std::chrono; + + std::println("{}[ RUN ] {}.{}{}", GREEN, curr_suite_name, name, CLEAR); + auto begin = system_clock::now(); + + test(); + + auto duration = duration_cast(system_clock::now() - begin); + std::println("{}[ {} ] {} ({} ms){}", + curr_failed ? RED : GREEN, + curr_failed ? "FAILED" : " OK", + full_name, + duration.count(), + CLEAR); + + /// Update test information. + curr_tests_count += 1; + total_tests_count += 1; + + curr_test_duration += duration; + total_test_duration += duration; + + if(curr_failed) { + curr_failed_tests_count += 1; + total_failed_tests_count += 1; + } +} + +void Runner::fail(const may_failure& failure) { + if(failure.failed) { + curr_failed = true; + std::println("{}Failure at {}:{}:{}! [{}]{}", + RED, + failure.location.file_name(), + failure.location.line(), + failure.location.column(), + failure.expression, + CLEAR); + } + + if(failure.fatal) { + curr_fatal = true; + std::println("{}--> Test stopped due to fatal error.{}", RED, CLEAR); + std::exit(1); + } +} + +int Runner::run_tests() { + /// Register all tests. + std::println("{}[----------] Global test environment set-up.{}", GREEN, CLEAR); + + for(auto& [suite_name, suite]: suites) { + if(!enable_example && suite_name == "TEST.Example") { + continue; + } + + curr_fatal = false; + all_skipped = true; + curr_suite_name = suite_name; + curr_tests_count = 0; + curr_failed_tests_count = 0; + curr_test_duration = std::chrono::milliseconds(); + + for(auto& callback: suite) { + callback(); + } + + /// If there is any test in the suite, we print the suite info. + if(!all_skipped) { + total_suites_count += 1; + std::println("{}[----------] {} tests from {} ({} ms total)\n{}", + GREEN, + curr_tests_count, + suite_name, + total_test_duration.count(), + CLEAR); + } + } + + std::println("{}[----------] Global test environment tear-down{}", GREEN, CLEAR); + std::println("{}[==========] {} tests from {} test suites ran. ({} ms total){}", + GREEN, + total_tests_count, + total_suites_count, + total_test_duration.count(), + CLEAR); + + return total_failed_tests_count != 0; +} + +} // namespace clice::testing + +int main(int argc, const char* argv[]) { llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); - - ::testing::InitGoogleTest(&argc, argv); llvm::cl::ParseCommandLineOptions(argc, argv, "clice test\n"); - if(!cl::resource_dir.empty()) { - fs::resource_dir = cl::resource_dir.getValue(); + if(!test_filter.empty()) { + if(auto result = GlobPattern::create(test_filter)) { + pattern.emplace(std::move(*result)); + } + } + + if(!resource_dir.empty()) { + fs::resource_dir = resource_dir; } else { if(auto result = fs::init_resource_dir(argv[0]); !result) { - llvm::outs() << std::format("Failed to get resource directory, because {}\n", - result.error()); + std::println("Failed to get resource directory, because {}", result.error()); return 1; } } - bool res = RUN_ALL_TESTS(); - return res; + using namespace clice::testing; + return Runner::instance().run_tests(); } - diff --git a/src/Feature/CodeCompletion.cpp b/src/Feature/CodeCompletion.cpp index d4dd9d9d..7ba3ce82 100644 --- a/src/Feature/CodeCompletion.cpp +++ b/src/Feature/CodeCompletion.cpp @@ -179,8 +179,9 @@ CompletionItemKind completion_kind(const clang::NamedDecl* decl) { case clang::Decl::ObjCImplementation: case clang::Decl::ObjCCompatibleAlias: + case clang::Decl::OutlinedFunction: case clang::Decl::HLSLBuffer: { - llvm_unreachable("Invalid code completion item, NamedDecl is expected"); + std::unreachable(); } } } diff --git a/tests/unit/AST/Resolver.cpp b/tests/unit/AST/Resolver.cpp index db89c044..78f0da18 100644 --- a/tests/unit/AST/Resolver.cpp +++ b/tests/unit/AST/Resolver.cpp @@ -36,25 +36,25 @@ struct InputFinder : clang::RecursiveASTVisitor { } }; -struct TemplateResolver : TestFixture { - void run(llvm::StringRef code, LocationChain chain = LocationChain()) { - add_main("main.cpp", code); - compile(); +suite<"TemplateResolver"> suite = [] { + auto run = [](llvm::StringRef code) { + Tester tester; + tester.add_main("main.cpp", code); + tester.compile(); - InputFinder finder(*unit); - finder.TraverseAST(unit->context()); + InputFinder finder(*tester.unit); + finder.TraverseAST(tester.unit->context()); - auto input = unit->resolver().resolve(finder.input); - auto expect = finder.expect; + auto input = tester.unit->resolver().resolve(finder.input); + auto target = finder.expect; - EXPECT_EQ(input.isNull(), false, chain); - EXPECT_EQ(expect.isNull(), false, chain); - EXPECT_EQ(input.getCanonicalType(), expect.getCanonicalType(), chain); - } -}; + expect(that % (!input.isNull())); + expect(that % (!target.isNull())); + expect(that % (input.getCanonicalType() == target.getCanonicalType())); + }; -TEST_F(TemplateResolver, TypeParameterType) { - run(R"cpp( + test("TypeParameterType") = [&] { + run(R"cpp( template struct A { using type = T; @@ -66,10 +66,10 @@ struct test { using expect = X; }; )cpp"); -} + }; -TEST_F(TemplateResolver, SingleLevel) { - run(R"cpp( + test("SingleLevel") = [&] { + run(R"cpp( template struct type_list {}; @@ -84,10 +84,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, SingleLevelNotDependent) { - run(R"cpp( + test("SingleLevelNotDependent") = [&] { + run(R"cpp( template struct A { using type = int; @@ -99,10 +99,10 @@ struct test { using expect = int; }; )cpp"); -} + }; -TEST_F(TemplateResolver, MultiLevel) { - run(R"cpp( + test("MultiLevel") = [&] { + run(R"cpp( template struct type_list {}; @@ -127,10 +127,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, MultiLevelNotDependent) { - run(R"cpp( + test("MultiLevelNotDependent") = [&] { + run(R"cpp( template struct A { using type = int; @@ -152,10 +152,10 @@ struct test { using expect = int; }; )cpp"); -} + }; -TEST_F(TemplateResolver, ArgumentDependent) { - run(R"cpp( + test("ArgumentDependent") = [&] { + run(R"cpp( template struct type_list {}; @@ -175,10 +175,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, AliasArgument) { - run(R"cpp( + test("AliasArgument") = [&] { + run(R"cpp( template struct type_list {}; @@ -199,10 +199,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, AliasDependent) { - run(R"cpp( + test("AliasDependent") = [&] { + run(R"cpp( template struct type_list {}; @@ -223,10 +223,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, AliasTemplate) { - run(R"cpp( + test("AliasTemplate") = [&] { + run(R"cpp( template struct type_list {}; @@ -247,10 +247,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, BaseDependent) { - run(R"cpp( + test("BaseDependent") = [&] { + run(R"cpp( template struct type_list {}; @@ -268,10 +268,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, MultiNested) { - run(R"cpp( + test("MultiNested") = [&] { + run(R"cpp( template struct type_list {}; @@ -287,10 +287,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, OuterDependentMemberClass) { - run(R"cpp( + test("OuterDependentMemberClass") = [&] { + run(R"cpp( template struct type_list {}; @@ -311,10 +311,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, InnerDependentMemberClass) { - run(R"cpp( + test("InnerDependentMemberClass") = [&] { + run(R"cpp( template struct type_list {}; @@ -329,10 +329,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, InnerDependentPartialMemberClass) { - run(R"cpp( + test("InnerDependentPartialMemberClass") = [&] { + run(R"cpp( template struct type_list {}; @@ -350,10 +350,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, PartialSpecialization) { - run(R"cpp( + test("PartialSpecialization") = [&] { + run(R"cpp( template struct type_list {}; @@ -374,10 +374,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, PartialDefaultArgument) { - run(R"cpp( + test("PartialDefaultArgument") = [&] { + run(R"cpp( template struct X {}; @@ -392,10 +392,10 @@ struct test { using expect = T; }; )cpp"); -} + }; -TEST_F(TemplateResolver, DefaultArgument) { - run(R"cpp( + test("DefaultArgument") = [&] { + run(R"cpp( template struct type_list {}; @@ -415,10 +415,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, PackExpansion) { - run(R"cpp( + test("PackExpansion") = [&] { + run(R"cpp( template struct type_list {}; @@ -433,10 +433,10 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -TEST_F(TemplateResolver, BasePackExpansion) { - run(R"cpp( + test("BasePackExpansion") = [&] { + run(R"cpp( template struct type_list {}; @@ -454,21 +454,22 @@ struct test { using expect = type_list; }; )cpp"); -} + }; -/// FIXME: headers not found -/// -/// TEST_F(TemplateResolver, Standard) { -/// run(R"cpp( -/// #include -/// -/// template -/// struct test { -/// using input = typename std::vector::reference; -/// using expect = T&; -/// }; -/// )cpp"); -/// } + /// FIXME: headers not found + /// + /// test("Standard") = [&] { + /// run(R"cpp( + /// #include + /// + /// template + /// struct test { + /// using input = typename std::vector::reference; + /// using expect = T&; + /// }; + /// )cpp"); + /// }; +}; } // namespace diff --git a/tests/unit/AST/Selection.cpp b/tests/unit/AST/Selection.cpp index 102e80fb..34284113 100644 --- a/tests/unit/AST/Selection.cpp +++ b/tests/unit/AST/Selection.cpp @@ -2,6 +2,15 @@ #include "AST/Selection.h" #include "clang/Lex/Lexer.h" +namespace clice { + +std::ostringstream& operator<< (std::ostringstream& os, const LocalSourceRange& range) { + os << range.begin << " - " << range.end; + return os; +} + +} // namespace clice + namespace clice::testing { namespace { @@ -203,33 +212,30 @@ std::optional toHalfOpenFileRange(const SourceManager& SM, } // namespace -void dump_diagnostics() {} +suite<"SelectionTree"> selection = [] { + auto select_right = [](llvm::StringRef code, auto&& callback) { + Tester tester; + tester.add_main("main.cpp", code); + expect(that % tester.compile()); + /// expect(that % tester.unit->diagnostics().empty()); -void select_right(llvm::StringRef code, auto&& callback, LocationChain chain = LocationChain()) { - Tester tester; - tester.add_main("main.cpp", code); - ASSERT_TRUE(tester.compile(), chain); - /// ASSERT_TRUE(tester.unit->diagnostics().empty(), chain); + auto points = tester.nameless_points(); + expect(that % points.size() >= 1); - auto points = tester.nameless_points(); - ASSERT_TRUE(points.size() >= 1, chain); + LocalSourceRange selected_range; + selected_range.begin = points[0]; + selected_range.end = points.size() == 2 ? points[1] : points[0]; + auto tree = SelectionTree::create_right(*tester.unit, selected_range); + callback(tester, tree); + }; - LocalSourceRange selected_range; - selected_range.begin = points[0]; - selected_range.end = points.size() == 2 ? points[1] : points[0]; - auto tree = SelectionTree::create_right(*tester.unit, selected_range); - callback(tester, tree); -} - -void EXPECT_SELECT(llvm::StringRef code, const char* kind, LocationChain chain = LocationChain()) { - select_right( - code, - [&](Tester& tester, SelectionTree& tree) { + auto expect_select = [&](llvm::StringRef code, const char* kind) { + select_right(code, [&](Tester& tester, SelectionTree& tree) { auto node = tree.common_ancestor(); if(!kind) { - ASSERT_FALSE(node, chain); + expect(that % !node); } else { - ASSERT_TRUE(node, chain); + expect(that % node); auto range2 = toHalfOpenFileRange(tester.unit->context().getSourceManager(), tester.unit->lang_options(), node->source_range()); @@ -241,90 +247,89 @@ void EXPECT_SELECT(llvm::StringRef code, const char* kind, LocationChain chain = /// llvm::outs() << tree << "\n"; /// tree.print(llvm::outs(), *node, 2); - ASSERT_EQ(node->kind(), llvm::StringRef(kind), chain); - ASSERT_EQ(range, tester.range(), chain); + expect(that % node->kind() == llvm::StringRef(kind)); + expect(that % range == tester.range()); } - }, - chain); -} + }); + }; -TEST(Selection, Expressions) { - EXPECT_SELECT(R"( + test("Expressions") = [&] { + expect_select(R"( struct AAA { struct BBB { static int ccc(); };}; int x = @[AAA::BBB::c$c$c](); )", - "DeclRefExpr"); + "DeclRefExpr"); - EXPECT_SELECT(R"( + expect_select(R"( struct AAA { struct BBB { static int ccc(); };}; int x = @[AAA::BBB::ccc($)]; )", - "CallExpr"); + "CallExpr"); - EXPECT_SELECT(R"( + expect_select(R"( struct S { int foo() const; int bar() { return @[f$oo](); } }; )", - "MemberExpr"); + "MemberExpr"); - EXPECT_SELECT(R"(void foo() { @[$foo](); })", "DeclRefExpr"); - EXPECT_SELECT(R"(void foo() { @[f$oo](); })", "DeclRefExpr"); - EXPECT_SELECT(R"(void foo() { @[fo$o](); })", "DeclRefExpr"); + expect_select(R"(void foo() { @[$foo](); })", "DeclRefExpr"); + expect_select(R"(void foo() { @[f$oo](); })", "DeclRefExpr"); + expect_select(R"(void foo() { @[fo$o](); })", "DeclRefExpr"); - EXPECT_SELECT(R"(void foo() { @[foo$] (); })", "DeclRefExpr"); + expect_select(R"(void foo() { @[foo$] (); })", "DeclRefExpr"); - EXPECT_SELECT(R"(void foo() { @[foo$()]; })", "CallExpr"); - EXPECT_SELECT(R"(void foo() { @[foo$()]; /*comment*/$})", "CallExpr"); - EXPECT_SELECT(R"(const int x = 1, y = 2; int array[ @[$x] ][10][y];)", "DeclRefExpr"); - EXPECT_SELECT(R"(const int x = 1, y = 2; int array[x][10][ @[$y] ];)", "DeclRefExpr"); - EXPECT_SELECT(R"(void func(int x) { int v_array[ @[$x] ][10]; })", "DeclRefExpr"); - EXPECT_SELECT(R"( + expect_select(R"(void foo() { @[foo$()]; })", "CallExpr"); + expect_select(R"(void foo() { @[foo$()]; /*comment*/$})", "CallExpr"); + expect_select(R"(const int x = 1, y = 2; int array[ @[$x] ][10][y];)", "DeclRefExpr"); + expect_select(R"(const int x = 1, y = 2; int array[x][10][ @[$y] ];)", "DeclRefExpr"); + expect_select(R"(void func(int x) { int v_array[ @[$x] ][10]; })", "DeclRefExpr"); + expect_select(R"( int a; decltype(@[$a] + a) b; )", - "DeclRefExpr"); + "DeclRefExpr"); - EXPECT_SELECT(R"( + expect_select(R"( void func() { @[__$func__]; } )", - "PredefinedExpr"); -} + "PredefinedExpr"); + }; -TEST(Selection, Literals) { - EXPECT_SELECT(R"( + test("Literals") = [&] { + expect_select(R"( auto lambda = [](const char*){ return 0; }; int x = lambda(@["y$"]); )", - "StringLiteral"); + "StringLiteral"); - EXPECT_SELECT(R"(int x = @[42]$;)", "IntegerLiteral"); - EXPECT_SELECT(R"(const int x = 1, y = 2; int array[x][ @[$10] ][y];)", "IntegerLiteral"); + expect_select(R"(int x = @[42]$;)", "IntegerLiteral"); + expect_select(R"(const int x = 1, y = 2; int array[x][ @[$10] ][y];)", "IntegerLiteral"); - EXPECT_SELECT(R"( + expect_select(R"( struct Foo{}; Foo operator""_ud(unsigned long long); Foo x = @[$12_ud]; )", - "UserDefinedLiteral"); -} + "UserDefinedLiteral"); + }; -TEST(Selection, ControlFlow) { - EXPECT_SELECT(R"( + test("ControlFlow") = [&] { + expect_select(R"( void foo() { @[if (1$11) { return; } else {$ }]} } )", - "IfStmt"); + "IfStmt"); - EXPECT_SELECT(R"(int bar; void foo() @[{ foo (); }]$)", "CompoundStmt"); + expect_select(R"(int bar; void foo() @[{ foo (); }]$)", "CompoundStmt"); - /// FIXME: - /// EXPECT_SELECT(R"( - /// /*error-ok*/ - /// void func() @[{^])", - /// "CompoundStmt"); + /// FIXME: + /// expect_select(R"( + /// /*error-ok*/ + /// void func() @[{^])", + /// "CompoundStmt"); - EXPECT_SELECT(R"( + expect_select(R"( struct Str { const char *begin(); const char *end(); @@ -335,313 +340,315 @@ TEST(Selection, ControlFlow) { ; } )", - "CallExpr"); -} + "CallExpr"); + }; -TEST(Selection, Declarations) { - /// FIXME: how to handle this? - /// EXPECT_SELECT(R"( - /// #define TARGET void foo() - /// @[TAR$GET{ return; }] - /// )", - /// "FunctionDecl"); + test("Declarations") = [&] { + /// FIXME: how to handle this? + /// expect_select(R"( + /// #define TARGET void foo() + /// @[TAR$GET{ return; }] + /// )", + /// "FunctionDecl"); - EXPECT_SELECT(R"(@[$void foo$()];)", "FunctionDecl"); - EXPECT_SELECT(R"(@[void $foo()];)", "FunctionDecl"); + expect_select(R"(@[$void foo$()];)", "FunctionDecl"); + expect_select(R"(@[void $foo()];)", "FunctionDecl"); - EXPECT_SELECT(R"( + expect_select(R"( struct S { S(const char*); }; @[S s $= "foo"]; )", - "VarDecl"); + "VarDecl"); - EXPECT_SELECT(R"( + expect_select(R"( struct S { S(const char*); }; @[S $s = "foo"]; )", - "VarDecl"); + "VarDecl"); - EXPECT_SELECT(R"( + expect_select(R"( @[void (*$S)(int) = nullptr]; )", - "VarDecl"); + "VarDecl"); - EXPECT_SELECT(R"(@[int $a], b;)", "VarDecl"); - EXPECT_SELECT(R"(@[int a, $b];)", "VarDecl"); - EXPECT_SELECT(R"(@[struct {int x;} $y];)", "VarDecl"); - EXPECT_SELECT(R"(struct foo { @[int has$h<:32:>]; };)", "FieldDecl"); - EXPECT_SELECT(R"(struct {@[int $x];} y;)", "FieldDecl"); + expect_select(R"(@[int $a], b;)", "VarDecl"); + expect_select(R"(@[int a, $b];)", "VarDecl"); + expect_select(R"(@[struct {int x;} $y];)", "VarDecl"); + expect_select(R"(struct foo { @[int has$h<:32:>]; };)", "FieldDecl"); + expect_select(R"(struct {@[int $x];} y;)", "FieldDecl"); - EXPECT_SELECT(R"( + expect_select(R"( void test(int bar) { auto l = [ $@[foo = bar] ] { }; })", - "VarDecl"); -} + "VarDecl"); + }; -TEST(Selection, Types) { - EXPECT_SELECT(R"( + test("Types") = [&] { + expect_select(R"( struct AAA { struct BBB { static int ccc(); };}; int x = AAA::@[B$B$B]::ccc(); )", - "RecordTypeLoc"); - EXPECT_SELECT(R"( + "RecordTypeLoc"); + expect_select(R"( struct AAA { struct BBB { static int ccc(); };}; int x = AAA::@[B$BB$]::ccc(); )", - "RecordTypeLoc"); - EXPECT_SELECT(R"( + "RecordTypeLoc"); + expect_select(R"( struct Foo {}; struct Bar : private @[Fo$o] {}; )", - "RecordTypeLoc"); - EXPECT_SELECT(R"( + "RecordTypeLoc"); + expect_select(R"( struct Foo {}; struct Bar : @[Fo$o] {}; )", - "RecordTypeLoc"); - EXPECT_SELECT(R"(@[$void] (*S)(int) = nullptr;)", "BuiltinTypeLoc"); - /// EXPECT_SELECT(R"(@[void (*S)$(int)] = nullptr;)", "FunctionProtoTypeLoc"); - EXPECT_SELECT(R"(@[void ($*S)(int)] = nullptr;)", "PointerTypeLoc"); - /// EXPECT_SELECT(R"(@[void $(*S)(int)] = nullptr;)", "ParenTypeLoc"); - EXPECT_SELECT(R"(@[$void] foo();)", "BuiltinTypeLoc"); - EXPECT_SELECT(R"(@[void foo$()];)", "FunctionProtoTypeLoc"); - EXPECT_SELECT(R"(const int x = 1, y = 2; @[i$nt] array[x][10][y];)", "BuiltinTypeLoc"); - EXPECT_SELECT(R"(int (*getFunc(@[do$uble]))(int);)", "BuiltinTypeLoc"); - EXPECT_SELECT(R"(class X{}; @[int X::$*]y[10];)", "MemberPointerTypeLoc"); - EXPECT_SELECT(R"(const @[a$uto] x = 42;)", "AutoTypeLoc"); - /// EXPECT_SELECT(R"(@[decltype$(1)] b;)", "DecltypeTypeLoc"); - EXPECT_SELECT(R"(@[de$cltype(a$uto)] a = 1;)", "AutoTypeLoc"); - EXPECT_SELECT(R"( + "RecordTypeLoc"); + expect_select(R"(@[$void] (*S)(int) = nullptr;)", "BuiltinTypeLoc"); + /// expect_select(R"(@[void (*S)$(int)] = nullptr;)", "FunctionProtoTypeLoc"); + expect_select(R"(@[void ($*S)(int)] = nullptr;)", "PointerTypeLoc"); + /// expect_select(R"(@[void $(*S)(int)] = nullptr;)", "ParenTypeLoc"); + expect_select(R"(@[$void] foo();)", "BuiltinTypeLoc"); + expect_select(R"(@[void foo$()];)", "FunctionProtoTypeLoc"); + expect_select(R"(const int x = 1, y = 2; @[i$nt] array[x][10][y];)", "BuiltinTypeLoc"); + expect_select(R"(int (*getFunc(@[do$uble]))(int);)", "BuiltinTypeLoc"); + expect_select(R"(class X{}; @[int X::$*]y[10];)", "MemberPointerTypeLoc"); + expect_select(R"(const @[a$uto] x = 42;)", "AutoTypeLoc"); + /// expect_select(R"(@[decltype$(1)] b;)", "DecltypeTypeLoc"); + expect_select(R"(@[de$cltype(a$uto)] a = 1;)", "AutoTypeLoc"); + expect_select(R"( typedef int Foo; enum Bar : @[Fo$o] {}; )", - "TypedefTypeLoc"); - EXPECT_SELECT(R"( + "TypedefTypeLoc"); + expect_select(R"( typedef int Foo; enum Bar : @[Fo$o]; )", - "TypedefTypeLoc"); -} + "TypedefTypeLoc"); + }; -TEST(Selection, CXXFeatures) { - EXPECT_SELECT(R"( + test("CXXFeatures") = [&] { + expect_select(R"( template int x = @[T::$U::]ccc(); )", - "NestedNameSpecifierLoc"); - EXPECT_SELECT(R"( + "NestedNameSpecifierLoc"); + expect_select(R"( struct Foo {}; struct Bar : @[v$ir$tual private Foo] {}; )", - "CXXBaseSpecifier"); - EXPECT_SELECT(R"( + "CXXBaseSpecifier"); + expect_select(R"( struct X { X(int); }; class Y { X x; Y() : @[$x(4)] {} }; )", - "CXXCtorInitializer"); - EXPECT_SELECT(R"(@[st$ruct {int x;}] y;)", "CXXRecordDecl"); - EXPECT_SELECT(R"(struct foo { @[op$erator int()]; };)", "CXXConversionDecl"); - EXPECT_SELECT(R"(struct foo { @[$~foo()]; };)", "CXXDestructorDecl"); - EXPECT_SELECT(R"(struct foo { @[~$foo()]; };)", "CXXDestructorDecl"); - EXPECT_SELECT(R"(struct foo { @[fo$o(){}] };)", "CXXConstructorDecl"); - EXPECT_SELECT(R"( + "CXXCtorInitializer"); + expect_select(R"(@[st$ruct {int x;}] y;)", "CXXRecordDecl"); + expect_select(R"(struct foo { @[op$erator int()]; };)", "CXXConversionDecl"); + expect_select(R"(struct foo { @[$~foo()]; };)", "CXXDestructorDecl"); + expect_select(R"(struct foo { @[~$foo()]; };)", "CXXDestructorDecl"); + expect_select(R"(struct foo { @[fo$o(){}] };)", "CXXConstructorDecl"); + expect_select(R"( struct S1 { void f(); }; struct S2 { S1 * operator->(); }; void test(S2 s2) { s2@[-$>]f(); } )", - "DeclRefExpr"); // Test for overloaded operator-> -} + "DeclRefExpr"); // Test for overloaded operator-> + }; -TEST(Selection, UsingEnum) { - EXPECT_SELECT(R"( + test("UsingEnum") = [&] { + expect_select(R"( namespace ns { enum class A {}; }; using enum ns::@[$A]; )", - "EnumTypeLoc"); - EXPECT_SELECT(R"( + "EnumTypeLoc"); + expect_select(R"( namespace ns { enum class A {}; using B = A; }; using enum ns::@[$B]; )", - "TypedefTypeLoc"); - EXPECT_SELECT(R"( + "TypedefTypeLoc"); + expect_select(R"( namespace ns { enum class A {}; }; using enum @[$ns::]A; )", - "NestedNameSpecifierLoc"); - EXPECT_SELECT(R"( + "NestedNameSpecifierLoc"); + expect_select(R"( namespace ns { enum class A {}; }; @[using $enum ns::A]; )", - "UsingEnumDecl"); - EXPECT_SELECT(R"( + "UsingEnumDecl"); + expect_select(R"( namespace ns { enum class A {}; }; @[$using enum ns::A]; )", - "UsingEnumDecl"); -} + "UsingEnumDecl"); + }; -TEST(Selection, Templates) { - EXPECT_SELECT(R"(template void foo(@[T*$...]x);)", "PackExpansionTypeLoc"); - EXPECT_SELECT(R"(template void foo(@[$T]*...x);)", "TemplateTypeParmTypeLoc"); - EXPECT_SELECT(R"(template void foo() { @[$T] t; })", "TemplateTypeParmTypeLoc"); - EXPECT_SELECT(R"( + test("Templates") = [&] { + expect_select(R"(template void foo(@[T*$...]x);)", "PackExpansionTypeLoc"); + expect_select(R"(template void foo(@[$T]*...x);)", + "TemplateTypeParmTypeLoc"); + expect_select(R"(template void foo() { @[$T] t; })", + "TemplateTypeParmTypeLoc"); + expect_select(R"( template struct Foo {}; template <@[template class /*cursor here*/$U]> struct Foo*> {}; )", - "TemplateTemplateParmDecl"); - EXPECT_SELECT(R"(template struct foo { ~foo<@[$T]>(){} };)", - "TemplateTypeParmTypeLoc"); - EXPECT_SELECT(R"( + "TemplateTemplateParmDecl"); + expect_select(R"(template struct foo { ~foo<@[$T]>(){} };)", + "TemplateTypeParmTypeLoc"); + expect_select(R"( template class Vector {}; template