Files
clice/tests/unit/Feature/Hover.cpp
2025-08-16 23:09:13 +08:00

409 lines
7.4 KiB
C++

#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<DeclCollector> {
llvm::StringMap<const clang::Decl*> 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<const clang::Decl*> 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`
___
<TODO: document>
___
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`)
___
<TODO: source code>
)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)
___
<TODO: document>
___
3 items:
+ A = `1 (0x1)`
+ B = `2 (0x2)`
+ C = `999 (0x3E7)`
___
<TODO: source code>
)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<typename T, typename S>
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`
___
<TODO: document>
___
<TODO: source code>
)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
___
<TODO: document>
___
<TODO: source code>
)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 <stddef.h$(h5)>
///
/// $(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 <cstddef>
//
// 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 <bits/c++config.h>
///
/// 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<typenam$(p7)e T1$(p8), typename T$(p9)2>
/// void templ$(f11)ate_func1(T1 le$(p10)ft, T2 righ$(p11)t)$(f12) {}
///
/// template<in$(p12)t NonTy$(p13)peParam = 1>
/// void templ$(f13)ate_func2() {}
///
/// template<templa$(p14)te<typen$(p15)ame Inn$(p16)er> 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<Var>);
};
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<A*>(nul$(e8)lptr)->function(par$(e9)am);
}
};
)cpp";
run(code);
};
};
} // namespace
} // namespace clice::testing