409 lines
7.4 KiB
C++
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
|