#include "Test/Tester.h" #include "Feature/Hover.h" #include "clang/AST/RecursiveASTVisitor.h" namespace clice::testing { namespace { using namespace feature; struct DeclCollector : public clang::RecursiveASTVisitor { llvm::StringMap decls; bool VisitNamedDecl(const clang::NamedDecl* decl) { assert(decl && "Decl must be a valid pointer"); decls[decl->getNameAsString()] = decl; return true; } }; suite<"Hover"> hover = [] { Tester tester; llvm::StringMap decls; auto run = [&](llvm::StringRef code, LocalSourceRange range = {}) { tester.clear(); tester.add_main("main.cpp", code); tester.compile(); DeclCollector collector; collector.TraverseTranslationUnitDecl(tester.unit->tu()); decls = std::move(collector.decls); }; test("Namespace") = [&] { run(R"cpp( namespace A { namespace B { namespace C {} /// anonymous namespace namespace { namespace D {} } } inline namespace E {} } /// std namespace namespace std { namespace F {} } )cpp"); // EXPECT_HOVER("", ""); // EXPECT_HOVER("", ""); // EXPECT_HOVER("", ""); // EXPECT_HOVER("", ""); // EXPECT_HOVER("", ""); // EXPECT_HOVER("", ""); // EXPECT_HOVER("", ""); /// FIXME: inline ? // EXPECT_HOVER("E", "### namespace A::(inline)E"); }; test("RecordScope") = [&] { run(R"cpp( typedef struct A { struct B { struct C {}; }; struct { struct D {}; } _; } T; /// forward declaration struct FORWARD_STRUCT; struct FORWARD_CLASS; /// in function body void f() { struct X {}; class Y {}; struct { struct Z {}; } _; } namespace n1 { namespace n2 { struct NA { struct NB {}; }; } namespace { struct NC {}; } } namespace out { namespace in { struct M { int x; double y; char z; T a, b; }; } } )cpp"); // EXPECT_HOVER("A", ""); // EXPECT_HOVER("B", ""); // EXPECT_HOVER("C", ""); // EXPECT_HOVER("X", ""); // EXPECT_HOVER("Y", ""); // EXPECT_HOVER("Z", ""); // EXPECT_HOVER("NA", ""); // EXPECT_HOVER("NB", ""); // EXPECT_HOVER("NC", ""); auto M_TEXT = R"md(### Struct `M` In namespace: `out::in` ___ ___ size: 24 (0x18) bytes, align: 8 (0x8) bytes, ___ 5 fields: + x: `int` + y: `double` + z: `char` + a: `T` (aka `struct A`) + b: `T` (aka `struct A`) ___ )md"; }; test("EnumStyle") = [&] { run(R"cpp( enum Free { A = 1, B = 2, C = 999, }; enum class Scope: long { A = -8, B = 2, C = 100, }; )cpp"); auto FREE_STYLE = R"md(### Enum `Free` `(unsigned int)` In namespace: `(global)`, (unscoped) ___ ___ 3 items: + A = `1 (0x1)` + B = `2 (0x2)` + C = `999 (0x3E7)` ___ )md"; // EXPECT_HOVER("Scope", ""); }; test("FunctionStyle") = [&] { run(R"cpp( typedef long long ll; ll f(int x, int y, ll z = 1) { return 0; } template T t(T a, T b, int c, ll d, S s) { return a; } namespace { constexpr static const char* g() { return "hello"; } } namespace test { namespace { [[deprecated("test deprecate message")]] consteval int h() { return 1; } } } struct A { constexpr static A m(int left, double right) { return A(); } }; )cpp"); auto FUNC_STYLE = R"md(### Method `m` In namespace: `(global)`, scope: `A` ___ `constexpr` `inline` `static` ___ -> `A` (aka `struct A`) ___ 2 parameters: + left: `int` + right: `double` ___ ___ )md"; // EXPECT_HOVER("f", FREE_STYLE); // EXPECT_HOVER("t", FREE_STYLE); // EXPECT_HOVER("g", FREE_STYLE); // EXPECT_HOVER("h", FREE_STYLE); }; test("VariableStyle") = [&] { run(R"cpp( void f() { constexpr static auto x1 = 1; } )cpp"); auto FREE_STYLE = R"md(### Variable `x1` In namespace: `(global)`, scope: `f` ___ `constexpr` `static` `(local variable)` Type: `const int` size = 4 bytes, align = 4 bytes ___ ___ )md"; }; /// TEST_F(Hover, HeaderAndNamespace) { /// auto header = R"cpp()cpp"; /// /// auto code = R"cpp( /// #in$(h1)clude "head$(h3)er.h"$(h4) /// #in$(h2)clude /// /// $(n1)names$(n2)pace$(n3) outt$(n4)er { /// /// namespac$(n5)e $(n6){ /// /// nam$(n7)espace inne$(n8)r { /// /// }$(n9) /// /// } /// /// }$(n10) /// /// )cpp"; /// } // TEST_F(Hover, VariableAndLiteral) { // auto code = R"cpp( // // introduce size_t // #include // // long operator ""_w(const char*, size_t) { // return 1; // }; // // aut$(v1)o$(v2) i$(v3)1$(v4) = $(n1)1$(n2); // auto$(v5) i2 = $(n3)-$(n4)1$(n5); // // auto l1$(v6) = $(l1)"test$(l2)_string_li$(l3)t"; // auto l2 = R$(l4)"tes$(l5)t(raw_string_lit)test"$(l6); // auto l3 = u8$(l7)"test$(l8)_string_li$(l9)t"; // auto l$(v7)4 = $(l10)"$(l11)udf_string$(l12)"_w; // )cpp"; // run(code); // } /// TEST_F(Hover, FunctionDeclAndParameter) { /// auto code = R"cpp( /// // introduce size_t /// #include /// /// i$(f1)nt$(f2) f() { /// return 0; /// } /// /// lo$(f3)ng oper$(f4)ator ""_w(const char* str, std::si$(p1)ze_t$(p2) leng$(p3)th) {$(f5) /// return 1; /// }; /// /// /// struct A { /// int f$(f6)n(i$(p4)nt par$(p5)am) { /// return param; /// } /// /// voi$(f7)d ope$(f8)rator()$(f9)(int par$(p6)am) {} /// }; /// /// /// templ$(f10)ate /// void templ$(f11)ate_func1(T1 le$(p10)ft, T2 righ$(p11)t)$(f12) {} /// /// template /// void templ$(f13)ate_func2() {} /// /// template typenam$(p17)e Outt$(p18)er> /// void templ$(f14)ate_func3() {} /// /// )cpp"; /// run(code); /// } test("AutoAndDecltype") = [&] { auto code = R"cpp( $(a1)aut$(a2)o$(a3) i = -1; $(d1)dec$(d2)ltype$(d3)(i) j = 2; struct A { int x; }; aut$(a4)o va$(a5)r = A{}; a$(fa)uto f1() { return 1; } de$(fn_decltype)cltype(au$(fn_decltype_auto)to) f2() {} int f3(au$(fn_para_auto)to x) {} )cpp"; run(code); /// FIXME: It seems a bug of SelectionTree, which cannot select any node of `f3`; /// EXPECT_HOVER_TYPE("fn_para_auto", is); }; test("Expr") = [&] { auto code = R"cpp( int xxxx = 1; int yyyy = xx$(e1)xx; struct A { int function(int param) { return thi$(e2)s$(e3)->$(e4)funct$(e5)ion(para$(e6)m); } int fn(int param) { return static$(e7)_cast(nul$(e8)lptr)->function(par$(e9)am); } }; )cpp"; run(code); }; }; } // namespace } // namespace clice::testing