1529 lines
41 KiB
C++
1529 lines
41 KiB
C++
#include "Test/Tester.h"
|
|
#include "Feature/InlayHint.h"
|
|
|
|
namespace clice::testing {
|
|
|
|
namespace {
|
|
|
|
suite<"InlayHint"> inlay_hint = [] {
|
|
Tester tester;
|
|
std::vector<feature::InlayHint> hints;
|
|
llvm::DenseMap<std::uint32_t, feature::InlayHint> hints_map;
|
|
|
|
auto run = [&](llvm::StringRef code,
|
|
std::source_location location = std::source_location::current()) {
|
|
tester.clear();
|
|
tester.add_main("main.cpp", code);
|
|
tester.compile_with_pch("-std=c++23");
|
|
|
|
LocalSourceRange range = LocalSourceRange(0, tester.unit->interested_content().size());
|
|
hints = feature::inlay_hints(*tester.unit, range, {});
|
|
|
|
hints_map.clear();
|
|
for(auto& hint: hints) {
|
|
hints_map[hint.offset] = hint;
|
|
}
|
|
|
|
if(!tester.unit->diagnostics().empty()) {
|
|
for(auto& diagnostic: tester.unit->diagnostics()) {
|
|
std::println("{}", diagnostic.message);
|
|
}
|
|
}
|
|
|
|
fatal / expect(tester.unit->diagnostics().empty(), location);
|
|
};
|
|
|
|
auto dump_results = [&] {
|
|
std::println("{}", pretty_dump(hints));
|
|
};
|
|
|
|
auto expect_size = [&](std::uint32_t size,
|
|
std::source_location location = std::source_location::current()) {
|
|
fatal / expect(eq(hints.size(), size), location) << pretty_dump(hints);
|
|
};
|
|
|
|
auto expect_hint = [&](llvm::StringRef pos,
|
|
llvm::StringRef name,
|
|
std::source_location location = std::source_location::current()) {
|
|
auto offset = tester.point(pos);
|
|
auto it = hints_map.find(offset);
|
|
fatal / expect(it != hints_map.end(), location)
|
|
<< std::format("offset is {}\n", offset) << pretty_dump(hints);
|
|
|
|
auto& parts = it->second.parts;
|
|
/// Currently, we has only one label.
|
|
fatal / expect(eq(parts.size(), 1), location);
|
|
expect(eq(parts[0].name, name), location);
|
|
};
|
|
|
|
test("Parameters") = [&] {
|
|
/// Name hint for normal param.
|
|
run(R"c(
|
|
int foo(int param);
|
|
int x = foo($(0)42);
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "param:");
|
|
|
|
// No hint for anonymous param.
|
|
run(R"c(
|
|
int foo(int);
|
|
int x = foo(42);
|
|
)c");
|
|
expect_size(0);
|
|
|
|
/// Reference hint for anonymous lvalue ref param.
|
|
run(R"c(
|
|
int foo(int&);
|
|
int x = 1;
|
|
int y = foo($(0)x);
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "&:");
|
|
|
|
// No hint for anonymous const lvalue ref param.
|
|
run(R"c(
|
|
int foo(const int&);
|
|
int x = foo(42);
|
|
)c");
|
|
expect_size(0);
|
|
|
|
run(R"c(
|
|
template <typename... Args>
|
|
int foo(Args&& ...);
|
|
int x = foo(42);
|
|
)c");
|
|
expect_size(0);
|
|
|
|
run(R"c(
|
|
namespace std {
|
|
template <typename T> T&& forward(T&);
|
|
}
|
|
|
|
int foo(int);
|
|
template <typename... Args>
|
|
int bar(Args&&... args) { return foo(std::forward<Args>(args)...); }
|
|
int x = bar(42);
|
|
)c");
|
|
expect_size(0);
|
|
|
|
// No hint for anonymous r-value ref parameter
|
|
run(R"c(
|
|
int foo(int&&);
|
|
int x = foo(42);
|
|
)c");
|
|
expect_size(0);
|
|
|
|
// Parameter name picked up from definition if necessary
|
|
run(R"c(
|
|
int foo(int);
|
|
int x = foo($(0)42);
|
|
int foo(int param) {
|
|
return 0;
|
|
}
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "param:");
|
|
|
|
// Parameter name picked up from definition if necessary
|
|
run(R"c(
|
|
int foo(int, int b);
|
|
int x = foo($(0)42, $(1)42);
|
|
int foo(int a, int) {
|
|
return 0;
|
|
}
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", "a:");
|
|
expect_hint("1", "b:");
|
|
|
|
// Parameter name picked up from definition in a resolved forwarded parameter
|
|
run(R"c(
|
|
int foo(int, int);
|
|
template <typename... Args>
|
|
int bar(Args... args) {
|
|
return foo(args...);
|
|
}
|
|
int x = bar($(0)42, $(1)42);
|
|
int foo(int a, int b) {
|
|
return 0;
|
|
}
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", "a:");
|
|
expect_hint("1", "b:");
|
|
|
|
// Prefer name from declaration
|
|
run(R"c(
|
|
int foo(int good);
|
|
int x = foo($(0)42);
|
|
int foo(int bad) {
|
|
return 0;
|
|
}
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "good:");
|
|
|
|
// Only name hint for const l-value ref parameter
|
|
run(R"c(
|
|
int foo(const int& param);
|
|
int x = foo($(0)42);
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "param:");
|
|
|
|
// Only name hint for const l-value ref parameter via type alias
|
|
run(R"c(
|
|
using alias = const int&;
|
|
int foo(alias param);
|
|
int x = 1;
|
|
int y = foo($(0)x);
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "param:");
|
|
|
|
// Reference and name hint for l-value ref parameter
|
|
run(R"c(
|
|
int foo(int& param);
|
|
int x = 1;
|
|
int y = foo($(0)x);
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "¶m:");
|
|
|
|
// Reference and name hint for l-value ref parameter via type alias
|
|
run(R"c(
|
|
using alias = int&;
|
|
int foo(alias param);
|
|
int x = 1;
|
|
int y = foo($(0)x);
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "¶m:");
|
|
|
|
// Only name hint for r-value ref parameter
|
|
run(R"c(
|
|
int foo(int&& param);
|
|
int x = foo($(0)42);
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "param:");
|
|
|
|
// Arg matches param
|
|
run(R"c(
|
|
void foo(int param);
|
|
struct S {
|
|
static const int param = 42;
|
|
};
|
|
void bar() {
|
|
int param = 42;
|
|
// Do not show redundant "param: param".
|
|
foo(param);
|
|
// But show it if the argument is qualified.
|
|
foo($(0)S::param);
|
|
}
|
|
struct A {
|
|
int param;
|
|
void bar() {
|
|
// Do not show "param: param" for member-expr.
|
|
foo(param);
|
|
}
|
|
};
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "param:");
|
|
|
|
// Arg matches param reference
|
|
run(R"c(
|
|
void foo(int& param);
|
|
void foo2(const int& param);
|
|
void bar() {
|
|
int param;
|
|
// show reference hint on mutable reference
|
|
foo($(0)param);
|
|
// but not on const reference
|
|
foo2(param);
|
|
}
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "&:");
|
|
|
|
// Name hint for variadic parameter using std::forward in a constructor call
|
|
run(R"c(
|
|
namespace std { template <typename T> T&& forward(T&); }
|
|
struct S { S(int a); };
|
|
template <typename T, typename... Args>
|
|
T bar(Args&&... args) { return T{std::forward<Args>(args)...}; }
|
|
int x = 1;
|
|
S y = bar<S>($(0)x);
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "a:");
|
|
|
|
// Name hint for variadic parameter in a constructor call
|
|
run(R"c(
|
|
struct S { S(int a); };
|
|
template <typename T, typename... Args>
|
|
T bar(Args&&... args) { return T{args...}; }
|
|
int x = 1;
|
|
S y = bar<S>($(0)x);
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "a:");
|
|
|
|
// Name for variadic parameter using std::forward
|
|
run(R"c(
|
|
namespace std { template <typename T> T&& forward(T&); }
|
|
int foo(int a);
|
|
template <typename... Args>
|
|
int bar(Args&&... args) { return foo(std::forward<Args>(args)...); }
|
|
int x = 1;
|
|
int y = bar($(0)x);
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "a:");
|
|
|
|
// Name hint for variadic parameter
|
|
run(R"c(
|
|
int foo(int a);
|
|
template <typename... Args>
|
|
int bar(Args&&... args) { return foo(args...); }
|
|
int x = bar($(0)42);
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "a:");
|
|
|
|
// Name hint for variadic parameter when the parameter pack is not the last template
|
|
// parameter
|
|
run(R"c(
|
|
int foo(int a);
|
|
template <typename... Args, typename Arg>
|
|
int bar(Arg, Args&&... args) { return foo(args...); }
|
|
int x = bar(1, $(0)42);
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "a:");
|
|
|
|
// Name for variadic parameter that involves both head and tail parameters
|
|
run(R"c(
|
|
namespace std { template <typename T> T&& forward(T&); }
|
|
int baz(int, int b, double);
|
|
template <typename... Args>
|
|
int foo(int a, Args&&... args) {
|
|
return baz(1, std::forward<Args>(args)..., 1.0);
|
|
}
|
|
template <typename... Args>
|
|
int bar(Args&&... args) { return foo(std::forward<Args>(args)...); }
|
|
int x = bar($(0)32, $(1)42);
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", "a:");
|
|
expect_hint("1", "b:");
|
|
|
|
// No hint for operator call with operator syntax
|
|
run(R"c(
|
|
struct S {};
|
|
void operator+(S lhs, S rhs);
|
|
void bar() {
|
|
S a, b;
|
|
a + b;
|
|
}
|
|
)c");
|
|
expect_size(0);
|
|
|
|
// Function call operator
|
|
run(R"c(
|
|
struct W {
|
|
void operator()(int x);
|
|
};
|
|
|
|
struct S : W {
|
|
using W::operator();
|
|
static void operator()(int x, int y);
|
|
};
|
|
|
|
void bar() {
|
|
auto l1 = [](int x) -> void {};
|
|
auto l2 = [](int x) static -> void {};
|
|
|
|
S s;
|
|
s($(0)1);
|
|
s.operator()($(1)1);
|
|
s.operator()($(2)1, $(3)2);
|
|
S::operator()($(4)1, $(5)2);
|
|
|
|
l1($(6)1);
|
|
l1.operator()($(7)1);
|
|
l2($(8)1);
|
|
l2.operator()($(9)1);
|
|
|
|
void (*ptr)(int a, int b) = &S::operator();
|
|
ptr($(10)1, $(11)2);
|
|
}
|
|
)c");
|
|
|
|
expect_hint("0", "x:");
|
|
expect_hint("1", "x:");
|
|
expect_hint("2", "x:");
|
|
expect_hint("3", "y:");
|
|
expect_hint("4", "x:");
|
|
expect_hint("5", "y:");
|
|
expect_hint("6", "x:");
|
|
expect_hint("7", "x:");
|
|
expect_hint("8", "x:");
|
|
expect_hint("9", "x:");
|
|
expect_hint("10", "a:");
|
|
expect_hint("11", "b:");
|
|
|
|
// Deducing this
|
|
run(R"c(
|
|
struct S {
|
|
template <typename This>
|
|
int operator()(this This &&Self, int Param) {
|
|
return 42;
|
|
}
|
|
|
|
int function(this auto &Self, int Param) {
|
|
return Param;
|
|
}
|
|
};
|
|
void work() {
|
|
S s;
|
|
s($(0)42);
|
|
s.function($(1)42);
|
|
S()($(2)42);
|
|
auto lambda = [](this auto &Self, char C) -> void {
|
|
return Self(C);
|
|
};
|
|
lambda($(3)'A');
|
|
}
|
|
)c");
|
|
|
|
expect_hint("0", "Param:");
|
|
expect_hint("1", "Param:");
|
|
expect_hint("2", "Param:");
|
|
expect_hint("3", "C:");
|
|
|
|
// Constructor with parentheses
|
|
run(R"c(
|
|
struct S {
|
|
S(int param);
|
|
};
|
|
void bar() {
|
|
S obj($(0)42);
|
|
}
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "param:");
|
|
|
|
// Constructor with braces
|
|
run(R"c(
|
|
struct S {
|
|
S(int param);
|
|
};
|
|
void bar() {
|
|
S obj{$(0)42};
|
|
}
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "param:");
|
|
|
|
// Member initialization
|
|
run(R"c(
|
|
struct S {
|
|
S(int param);
|
|
};
|
|
|
|
struct T {
|
|
S member;
|
|
T() : member($(0)42) {}
|
|
};
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "param:");
|
|
|
|
// Function pointer
|
|
run(R"c(
|
|
void (*f1)(int param);
|
|
void (*f2)(int param) noexcept;
|
|
using f3_t = void(*)(int param);
|
|
f3_t f3;
|
|
using f4_t = void(*)(int param) noexcept;
|
|
f4_t f4;
|
|
|
|
void bar() {
|
|
f1($(0)42);
|
|
f2($(1)42);
|
|
f3($(2)42);
|
|
f4($(3)42);
|
|
}
|
|
)c");
|
|
expect_size(4);
|
|
expect_hint("0", "param:");
|
|
expect_hint("1", "param:");
|
|
expect_hint("2", "param:");
|
|
expect_hint("3", "param:");
|
|
|
|
// Leading underscore
|
|
run(R"c(
|
|
void foo(int p1, int _p2, int __p3);
|
|
void bar() {
|
|
foo($(0)41, $(1)42, $(2)43);
|
|
}
|
|
)c");
|
|
expect_size(3);
|
|
expect_hint("0", "p1:");
|
|
expect_hint("1", "p2:");
|
|
expect_hint("2", "p3:");
|
|
|
|
// Variadic function
|
|
run(R"c(
|
|
template <typename... T>
|
|
void foo(int fixed, T... variadic);
|
|
|
|
void bar() {
|
|
foo($(0)41, 42, 43);
|
|
}
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "fixed:");
|
|
|
|
// Varargs function
|
|
run(R"c(
|
|
void foo(int fixed, ...);
|
|
|
|
void bar() {
|
|
foo($(0)41, 42, 43);
|
|
}
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "fixed:");
|
|
|
|
// Do not show hint for parameter of copy or move constructor
|
|
run(R"c(
|
|
struct S {
|
|
S();
|
|
S(const S& other);
|
|
S(S&& other);
|
|
};
|
|
|
|
void bar() {
|
|
S a;
|
|
S b = S(a); // copy
|
|
S c = S(S()); // move
|
|
}
|
|
)c");
|
|
expect_size(0);
|
|
|
|
// Do not hint call to user-defined literal operator
|
|
run(R"c(
|
|
long double operator ""_w(long double param);
|
|
void bar() {
|
|
1.2_w;
|
|
}
|
|
)c");
|
|
expect_size(0);
|
|
|
|
// Parameter name comment
|
|
run(R"c(
|
|
void foo(int param);
|
|
void bar() {
|
|
foo(/*param*/42);
|
|
foo( /* param = */ 42);
|
|
#define X 42
|
|
#define Y X
|
|
#define Z(...) Y
|
|
foo(/*param=*/Z(a));
|
|
foo($(0)Z(a));
|
|
foo(/* the answer */$(1)42);
|
|
}
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", "param:");
|
|
expect_hint("1", "param:");
|
|
|
|
// Setter functions
|
|
run(R"c(
|
|
struct S {
|
|
void setParent(S* parent);
|
|
void set_parent(S* parent);
|
|
void setTimeout(int timeoutMillis);
|
|
void setTimeoutMillis(int timeout_millis);
|
|
};
|
|
void bar() {
|
|
S s;
|
|
// Parameter name matches setter name - omit hint.
|
|
s.setParent(nullptr);
|
|
// Support snake_case
|
|
s.set_parent(nullptr);
|
|
// Parameter name may contain extra info - show hint.
|
|
s.setTimeout($(0)120);
|
|
// FIXME: Ideally we'd want to omit this.
|
|
s.setTimeoutMillis($(1)120);
|
|
}
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", "timeoutMillis:");
|
|
expect_hint("1", "timeout_millis:");
|
|
};
|
|
|
|
test("Types") = [&] {
|
|
// Basic type hint
|
|
run(R"c(
|
|
auto waldo$(0) = 42;
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", ": int");
|
|
|
|
// Decorations
|
|
run(R"c(
|
|
int x = 42;
|
|
auto* var1$(0) = &x;
|
|
auto&& var2$(1) = x;
|
|
const auto& var3$(2) = x;
|
|
)c");
|
|
expect_size(3);
|
|
expect_hint("0", ": int *");
|
|
expect_hint("1", ": int &");
|
|
expect_hint("2", ": const int &");
|
|
|
|
// Decltype auto
|
|
run(R"c(
|
|
int x = 42;
|
|
int& y = x;
|
|
decltype(auto) z$(0) = y;
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", ": int &");
|
|
|
|
// No qualifiers
|
|
run(R"c(
|
|
namespace A {
|
|
namespace B {
|
|
struct S1 {};
|
|
S1 foo();
|
|
auto x$(0) = foo();
|
|
|
|
struct S2 {
|
|
template <typename T>
|
|
struct Inner {};
|
|
};
|
|
|
|
S2::Inner<int> bar();
|
|
auto y$(1) = bar();
|
|
}
|
|
}
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", ": S1");
|
|
expect_hint("1", ": S2::Inner<int>");
|
|
|
|
// Lambda
|
|
run(R"c(
|
|
void f() {
|
|
int cap = 42;
|
|
auto L$(0) = [cap, init$(1) = 1 + 1](int a)$(2) {
|
|
return a + cap + init;
|
|
};
|
|
}
|
|
)c");
|
|
expect_size(3);
|
|
expect_hint("0", ": (lambda)");
|
|
expect_hint("1", ": int");
|
|
expect_hint("2", "-> int");
|
|
|
|
// Lambda return hint shown even if no param list
|
|
run(R"c(
|
|
auto x$(0) = []$(1){return 42;};
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", ": (lambda)");
|
|
expect_hint("1", "-> int");
|
|
|
|
// Structured bindings - public struct
|
|
run(R"c(
|
|
struct Point {
|
|
int x;
|
|
int y;
|
|
};
|
|
Point foo();
|
|
auto [x$(0), y$(1)] = foo();
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", ": int");
|
|
expect_hint("1", ": int");
|
|
|
|
// Structured bindings - array
|
|
run(R"c(
|
|
int arr[2];
|
|
auto [x$(0), y$(1)] = arr;
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", ": int");
|
|
expect_hint("1", ": int");
|
|
|
|
// Structured bindings - tuple-like
|
|
run(R"c(
|
|
struct IntPair {
|
|
int a;
|
|
int b;
|
|
};
|
|
|
|
namespace std {
|
|
template <typename T>
|
|
struct tuple_size {};
|
|
|
|
template <>
|
|
struct tuple_size<IntPair> {
|
|
constexpr static unsigned value = 2;
|
|
};
|
|
|
|
template <unsigned I, typename T>
|
|
struct tuple_element {};
|
|
|
|
template <unsigned I>
|
|
struct tuple_element<I, IntPair> {
|
|
using type = int;
|
|
};
|
|
}
|
|
|
|
template <unsigned I>
|
|
int get(const IntPair& p) {
|
|
if constexpr (I == 0) {
|
|
return p.a;
|
|
} else if constexpr (I == 1) {
|
|
return p.b;
|
|
}
|
|
}
|
|
|
|
IntPair bar();
|
|
auto [x$(0), y$(1)] = bar();
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", ": int");
|
|
expect_hint("1", ": int");
|
|
|
|
// Return type deduction
|
|
run(R"c(
|
|
auto f1(int x)$(0); // Hint forward declaration too
|
|
auto f1(int x)$(1) { return x + 1; }
|
|
|
|
// Include pointer operators in hint
|
|
int s;
|
|
auto& f2()$(2) { return s; }
|
|
|
|
// Do not hint `auto` for trailing return type.
|
|
auto f3() -> int;
|
|
|
|
// Do not hint when a trailing return type is specified.
|
|
auto f4() -> auto* { return "foo"; }
|
|
|
|
auto f5()$(3) {}
|
|
|
|
// `auto` conversion operator
|
|
struct A {
|
|
operator auto()$(4) { return 42; }
|
|
};
|
|
)c");
|
|
expect_size(5);
|
|
expect_hint("0", "-> int");
|
|
expect_hint("1", "-> int");
|
|
expect_hint("2", "-> int &");
|
|
expect_hint("3", "-> void");
|
|
expect_hint("4", "-> int");
|
|
|
|
// Decltype
|
|
run(R"c(
|
|
decltype(0)$(0) a;
|
|
decltype(a)$(1) b;
|
|
const decltype(0)$(2) &c = b;
|
|
|
|
decltype(0)$(3) e();
|
|
auto f() -> decltype(0)$(4);
|
|
|
|
template <class, class> struct Foo;
|
|
using G = Foo<decltype(0)$(5), float>;
|
|
|
|
auto h$(6) = decltype(0)$(7){};
|
|
)c");
|
|
expect_size(8);
|
|
expect_hint("0", ": int");
|
|
expect_hint("1", ": int");
|
|
expect_hint("2", ": int");
|
|
expect_hint("3", ": int");
|
|
expect_hint("4", ": int");
|
|
expect_hint("5", ": int");
|
|
expect_hint("6", ": int");
|
|
expect_hint("7", ": int");
|
|
|
|
// Long type name
|
|
run(R"c(
|
|
template <typename, typename, typename>
|
|
struct A {};
|
|
struct MultipleWords {};
|
|
A<MultipleWords, MultipleWords, MultipleWords> foo();
|
|
// Omit type hint past a certain length (currently 32)
|
|
auto var = foo();
|
|
)c");
|
|
expect_size(0);
|
|
|
|
// Default template args
|
|
run(R"c(
|
|
template <typename, typename = int>
|
|
struct A {};
|
|
A<float> foo();
|
|
auto var$(0) = foo();
|
|
A<float> bar[1];
|
|
auto [binding$(1)] = bar;
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", ": A<float>");
|
|
expect_hint("1", ": A<float>");
|
|
|
|
// Deduplication
|
|
run(R"c(
|
|
template <typename T>
|
|
void foo() {
|
|
auto var$(0) = 42;
|
|
}
|
|
|
|
template void foo<int>();
|
|
template void foo<float>();
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", ": int");
|
|
|
|
// Singly instantiated template
|
|
run(R"c(
|
|
auto lambda$(0) = [](auto* param$(1), auto) { return 42; };
|
|
int m = lambda("foo", 3);
|
|
)c");
|
|
expect_hint("0", ": (lambda)");
|
|
expect_hint("1", ": const char *");
|
|
|
|
// No hint for packs, or auto params following packs
|
|
run(R"c(
|
|
int x(auto a$(0), auto... b, auto c) { return 42; }
|
|
int m = x<void*, char, float>(nullptr, 'c', 2.0, 2);
|
|
)c");
|
|
expect_hint("0", ": void *");
|
|
};
|
|
|
|
skip / test("Designators") = [&] {
|
|
// Basic designator hints
|
|
run(R"c(
|
|
struct S { int x, y, z; };
|
|
S s {$(0)1, $(1)2+2};
|
|
|
|
int x[] = {$(2)0, $(3)1};
|
|
)c");
|
|
expect_size(4);
|
|
expect_hint("0", ".x=");
|
|
expect_hint("1", ".y=");
|
|
expect_hint("2", "[0]=");
|
|
expect_hint("3", "[1]=");
|
|
|
|
// Nested designators
|
|
run(R"c(
|
|
struct Inner { int x, y; };
|
|
struct Outer { Inner a, b; };
|
|
Outer o{ $(0)a{ $(1)1, $(2)2 }, $(3)bx3 };
|
|
)c");
|
|
expect_size(4);
|
|
expect_hint("0", ".a=");
|
|
expect_hint("1", ".x=");
|
|
expect_hint("2", ".y=");
|
|
expect_hint("3", ".b.x=");
|
|
|
|
// Anonymous record
|
|
run(R"c(
|
|
struct S {
|
|
union {
|
|
struct {
|
|
struct {
|
|
int y;
|
|
};
|
|
} x;
|
|
};
|
|
};
|
|
S s{$(0)xy42};
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", ".x.y=");
|
|
|
|
// Suppression
|
|
run(R"c(
|
|
struct Point { int a, b, c, d, e, f, g, h; };
|
|
Point p{/*a=*/1, .c=2, /* .d = */3, $(0)4};
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", ".e=");
|
|
|
|
// Std array
|
|
run(R"c(
|
|
template <typename T, int N> struct Array { T __elements[N]; };
|
|
Array<int, 2> x = {$(0)0, $(1)1};
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", "[0]=");
|
|
expect_hint("1", "[1]=");
|
|
|
|
// Only aggregate init
|
|
run(R"c(
|
|
struct Copyable { int x; } c;
|
|
Copyable d{c};
|
|
|
|
struct Constructible { Constructible(int x); };
|
|
Constructible x{42};
|
|
)c");
|
|
expect_size(0);
|
|
|
|
// No crash
|
|
run(R"c(
|
|
struct A {};
|
|
struct Foo {int a; int b;};
|
|
void test() {
|
|
Foo f{A(), $(0)1};
|
|
}
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", ".b=");
|
|
};
|
|
|
|
skip / test("BlockEnd") = [&] {
|
|
// Functions
|
|
run(R"c(
|
|
int foo() {
|
|
return 41;
|
|
$(0)}
|
|
|
|
template<int X>
|
|
int bar() {
|
|
// No hint for lambda for now
|
|
auto f = []() {
|
|
return X;
|
|
};
|
|
return f();
|
|
$(1)}
|
|
|
|
// No hint because this isn't a definition
|
|
int buz();
|
|
|
|
struct S{};
|
|
bool operator==(S, S) {
|
|
return true;
|
|
$(2)}
|
|
)c");
|
|
expect_size(3);
|
|
expect_hint("0", " // foo");
|
|
expect_hint("1", " // bar");
|
|
expect_hint("2", " // operator==");
|
|
|
|
// Methods
|
|
run(R"c(
|
|
struct Test {
|
|
// No hint because there's no function body
|
|
Test() = default;
|
|
|
|
~Test() {
|
|
$(0)}
|
|
|
|
void method1() {
|
|
$(1)}
|
|
|
|
// No hint because this isn't a definition
|
|
void method2();
|
|
|
|
template <typename T>
|
|
void method3() {
|
|
$(2)}
|
|
|
|
// No hint because this isn't a definition
|
|
template <typename T>
|
|
void method4();
|
|
|
|
Test operator+(int) const {
|
|
return *this;
|
|
$(3)}
|
|
|
|
operator bool() const {
|
|
return true;
|
|
$(4)}
|
|
|
|
// No hint because there's no function body
|
|
operator int() const = delete;
|
|
} x;
|
|
|
|
void Test::method2() {
|
|
$(5)}
|
|
|
|
template <typename T>
|
|
void Test::method4() {
|
|
$(6)}
|
|
)c");
|
|
expect_size(7);
|
|
expect_hint("0", " // ~Test");
|
|
expect_hint("1", " // method1");
|
|
expect_hint("2", " // method3");
|
|
expect_hint("3", " // operator+");
|
|
expect_hint("4", " // operator bool");
|
|
expect_hint("5", " // Test::method2");
|
|
expect_hint("6", " // Test::method4");
|
|
|
|
// Namespaces
|
|
run(R"c(
|
|
namespace {
|
|
void foo();
|
|
$(0)}
|
|
|
|
namespace ns {
|
|
void bar();
|
|
$(1)}
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", " // namespace");
|
|
expect_hint("1", " // namespace ns");
|
|
|
|
// Types
|
|
run(R"c(
|
|
struct S {
|
|
$(0)};
|
|
|
|
class C {
|
|
$(1)};
|
|
|
|
union U {
|
|
$(2)};
|
|
|
|
enum E1 {
|
|
$(3)};
|
|
|
|
enum class E2 {
|
|
$(4)};
|
|
)c");
|
|
expect_size(5);
|
|
expect_hint("0", " // struct S");
|
|
expect_hint("1", " // class C");
|
|
expect_hint("2", " // union U");
|
|
expect_hint("3", " // enum E1");
|
|
expect_hint("4", " // enum class E2");
|
|
|
|
// If statements
|
|
run(R"c(
|
|
void foo(bool cond) {
|
|
if (cond)
|
|
;
|
|
|
|
if (cond) {
|
|
$(0)}
|
|
|
|
if (cond) {
|
|
} else {
|
|
$(1)}
|
|
|
|
if (cond) {
|
|
} else if (!cond) {
|
|
$(2)}
|
|
|
|
if (cond) {
|
|
} else {
|
|
if (!cond) {
|
|
$(3)}
|
|
$(4)}
|
|
|
|
if (auto X = cond) {
|
|
$(5)}
|
|
|
|
if (int i = 0; i > 10) {
|
|
$(6)}
|
|
}
|
|
)c");
|
|
expect_size(7);
|
|
expect_hint("0", " // if cond");
|
|
expect_hint("1", " // if cond");
|
|
expect_hint("2", " // if");
|
|
expect_hint("3", " // if !cond");
|
|
expect_hint("4", " // if cond");
|
|
expect_hint("5", " // if X");
|
|
expect_hint("6", " // if i > 10");
|
|
|
|
// Loops
|
|
run(R"c(
|
|
void foo() {
|
|
while (true)
|
|
;
|
|
|
|
while (true) {
|
|
$(0)}
|
|
|
|
do {
|
|
} while (true);
|
|
|
|
for (;true;) {
|
|
$(1)}
|
|
|
|
for (int I = 0; I < 10; ++I) {
|
|
$(2)}
|
|
|
|
int Vs[] = {1,2,3};
|
|
for (auto V : Vs) {
|
|
$(3)}
|
|
}
|
|
)c");
|
|
expect_size(4);
|
|
expect_hint("0", " // while true");
|
|
expect_hint("1", " // for true");
|
|
expect_hint("2", " // for I");
|
|
expect_hint("3", " // for V");
|
|
|
|
// Switch
|
|
run(R"c(
|
|
void foo(int I) {
|
|
switch (I) {
|
|
case 0: break;
|
|
$(0)}
|
|
}
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", " // switch I");
|
|
|
|
// Print literals
|
|
run(R"c(
|
|
void foo() {
|
|
while ("foo") {
|
|
$(0)}
|
|
|
|
while ("foo but this time it is very long") {
|
|
$(1)}
|
|
|
|
while (true) {
|
|
$(2)}
|
|
|
|
while (1) {
|
|
$(3)}
|
|
|
|
while (1.5) {
|
|
$(4)}
|
|
}
|
|
)c");
|
|
expect_size(5);
|
|
expect_hint("0", " // while \"foo\"");
|
|
expect_hint("1", " // while \"foo but...\"");
|
|
expect_hint("2", " // while true");
|
|
expect_hint("3", " // while 1");
|
|
expect_hint("4", " // while 1.5");
|
|
|
|
// Print refs
|
|
run(R"c(
|
|
namespace ns {
|
|
int Var;
|
|
int func();
|
|
struct S {
|
|
int Field;
|
|
int method() const;
|
|
};
|
|
}
|
|
void foo() {
|
|
while (ns::Var) {
|
|
$(0)}
|
|
|
|
while (ns::func()) {
|
|
$(1)}
|
|
|
|
while (ns::S{}.Field) {
|
|
$(2)}
|
|
|
|
while (ns::S{}.method()) {
|
|
$(3)}
|
|
}
|
|
)c");
|
|
expect_size(4);
|
|
expect_hint("0", " // while Var");
|
|
expect_hint("1", " // while func");
|
|
expect_hint("2", " // while Field");
|
|
expect_hint("3", " // while method");
|
|
|
|
// Print conversions
|
|
run(R"c(
|
|
struct S {
|
|
S(int);
|
|
S(int, int);
|
|
explicit operator bool();
|
|
};
|
|
void foo(int I) {
|
|
while (float(I)) {
|
|
$(0)}
|
|
|
|
while (S(I)) {
|
|
$(1)}
|
|
|
|
while (S(I, I)) {
|
|
$(2)}
|
|
}
|
|
)c");
|
|
expect_size(3);
|
|
expect_hint("0", " // while float");
|
|
expect_hint("1", " // while S");
|
|
expect_hint("2", " // while S");
|
|
|
|
// Print operators
|
|
run(R"c(
|
|
using Integer = int;
|
|
void foo(Integer I) {
|
|
while(++I){
|
|
$(0)}
|
|
|
|
while(I++){
|
|
$(1)}
|
|
|
|
while(+(I + I)){
|
|
$(2)}
|
|
|
|
while(I < 0){
|
|
$(3)}
|
|
|
|
while((I + I) < I){
|
|
$(4)}
|
|
|
|
while(I < (I + I)){
|
|
$(5)}
|
|
|
|
while((I + I) < (I + I)){
|
|
$(6)}
|
|
}
|
|
)c");
|
|
expect_size(7);
|
|
expect_hint("0", " // while ++I");
|
|
expect_hint("1", " // while I++");
|
|
expect_hint("2", " // while");
|
|
expect_hint("3", " // while I < 0");
|
|
expect_hint("4", " // while ... < I");
|
|
expect_hint("5", " // while I < ...");
|
|
expect_hint("6", " // while");
|
|
|
|
// Trailing semicolon
|
|
run(R"c(
|
|
// The hint is placed after the trailing ';'
|
|
struct S1 {
|
|
$(0)} ;
|
|
|
|
// The hint is always placed in the same line with the closing '}'.
|
|
// So in this case where ';' is missing, it is attached to '}'.
|
|
struct S2 {
|
|
$(1)}
|
|
|
|
;
|
|
|
|
// No hint because only one trailing ';' is allowed
|
|
struct S3 {
|
|
};;
|
|
|
|
// No hint because trailing ';' is only allowed for class/struct/union/enum
|
|
void foo() {
|
|
};
|
|
|
|
// Rare case, but yes we'll have a hint here.
|
|
struct {
|
|
int x;
|
|
$(2)}
|
|
|
|
s2;
|
|
)c");
|
|
expect_size(3);
|
|
expect_hint("0", " // struct S1");
|
|
expect_hint("1", " // struct S2");
|
|
expect_hint("2", " // struct");
|
|
|
|
// Trailing text
|
|
run(R"c(
|
|
struct S1 {
|
|
$(0)} ;
|
|
|
|
// No hint for S2 because of the trailing comment
|
|
struct S2 {
|
|
}; /* Put anything here */
|
|
|
|
struct S3 {
|
|
// No hint for S4 because of the trailing source code
|
|
struct S4 {
|
|
};$(1)};
|
|
|
|
// No hint for ns because of the trailing comment
|
|
namespace ns {
|
|
} // namespace ns
|
|
)c");
|
|
expect_size(2);
|
|
expect_hint("0", " // struct S1");
|
|
expect_hint("1", " // struct S3");
|
|
|
|
// Macro
|
|
run(R"c(
|
|
#define DECL_STRUCT(NAME) struct NAME {
|
|
#define RBRACE }
|
|
|
|
DECL_STRUCT(S1)
|
|
$(0)};
|
|
|
|
// No hint because we require a '}'
|
|
DECL_STRUCT(S2)
|
|
RBRACE;
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", " // struct S1");
|
|
|
|
// Pointer to member function
|
|
run(R"c(
|
|
class A {};
|
|
using Predicate = bool(A::*)();
|
|
void foo(A* a, Predicate p) {
|
|
if ((a->*p)()) {
|
|
$(0)}
|
|
}
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", " // if");
|
|
};
|
|
|
|
skip / test("DefaultArguments") = [&] {
|
|
// Smoke test
|
|
run(R"c(
|
|
int foo(int A = 4) { return A; }
|
|
int bar(int A, int B = 1, bool C = foo($(0))) { return A; }
|
|
int A = bar($(1)2$(2));
|
|
|
|
void baz(int = 5) { if (false) baz($(3)); };
|
|
)c");
|
|
expect_size(4);
|
|
expect_hint("0", "A: 4");
|
|
expect_hint("1", "A: ");
|
|
expect_hint("2", ", B: 1, C: foo()");
|
|
expect_hint("3", "5");
|
|
|
|
// Without parameter names
|
|
run(R"c(
|
|
struct Baz {
|
|
Baz(float a = 3 //
|
|
+ 2);
|
|
};
|
|
struct Foo {
|
|
Foo(int, Baz baz = //
|
|
Baz{$(0)}
|
|
|
|
//
|
|
) {}
|
|
};
|
|
|
|
int main() {
|
|
Foo foo1(1$(1));
|
|
Foo foo2{2$(2)};
|
|
Foo foo3 = {3$(3)};
|
|
auto foo4 = Foo{4$(4)};
|
|
}
|
|
)c");
|
|
expect_size(5);
|
|
expect_hint("0", "...");
|
|
expect_hint("1", ", Baz{}");
|
|
expect_hint("2", ", Baz{}");
|
|
expect_hint("3", ", Baz{}");
|
|
expect_hint("4", ", Baz{}");
|
|
};
|
|
|
|
test("Special") = [&] {
|
|
// Macros
|
|
run(R"c(
|
|
void foo(int param);
|
|
#define ExpandsToCall() foo(42)
|
|
void bar() {
|
|
ExpandsToCall();
|
|
}
|
|
)c");
|
|
expect_size(0);
|
|
|
|
run(R"c(
|
|
#define PI 3.14
|
|
void foo(double param);
|
|
void bar() {
|
|
foo($(0)PI);
|
|
}
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "param:");
|
|
|
|
run(R"c(
|
|
void abort();
|
|
#define ASSERT(expr) if (!(expr)) abort()
|
|
int foo(int param);
|
|
void bar() {
|
|
ASSERT(foo($(0)42) == 0);
|
|
}
|
|
)c");
|
|
expect_size(1);
|
|
expect_hint("0", "param:");
|
|
|
|
run(R"c(
|
|
void foo(double x, double y);
|
|
#define CONSTANTS 3.14, 2.72
|
|
void bar() {
|
|
foo(CONSTANTS);
|
|
}
|
|
)c");
|
|
expect_size(0);
|
|
|
|
// Implicit constructor
|
|
run(R"c(
|
|
struct S {
|
|
S(int param);
|
|
};
|
|
void bar(S);
|
|
S foo() {
|
|
// Do not show hint for implicit constructor call in argument.
|
|
bar(42);
|
|
// Do not show hint for implicit constructor call in return.
|
|
return 42;
|
|
}
|
|
)c");
|
|
expect_size(0);
|
|
|
|
// Aggregate init
|
|
run(R"c(
|
|
struct Point {
|
|
int x;
|
|
int y;
|
|
};
|
|
void bar() {
|
|
Point p{41, 42};
|
|
}
|
|
)c");
|
|
expect_size(0);
|
|
|
|
// Builtin functions
|
|
run(R"c(
|
|
namespace std { template <typename T> T&& forward(T&); }
|
|
void foo() {
|
|
int i;
|
|
int&& s = std::forward(i);
|
|
}
|
|
)c");
|
|
expect_size(0);
|
|
|
|
// Pseudo object expression
|
|
run(R"c(
|
|
struct S {
|
|
__declspec(property(get=GetX, put=PutX)) int x[];
|
|
int GetX(int y, int z) { return 42 + y; }
|
|
void PutX(int) { }
|
|
|
|
// This is a PseudoObjectExpression whose syntactic form is a binary
|
|
// operator.
|
|
void Work(int y) { x = y; } // Not `x = y: y`.
|
|
};
|
|
|
|
int printf(const char *Format, ...);
|
|
|
|
int main() {
|
|
S s;
|
|
__builtin_dump_struct(&s, printf); // Not `Format: __builtin_dump_struct()`
|
|
printf($(0)"Hello, %d", 42); // Normal calls are not affected.
|
|
// This builds a PseudoObjectExpr, but here it's useful for showing the
|
|
// arguments from the semantic form.
|
|
return s.x[ $(1)1 ][ $(2)2 ]; // `x[y: 1][z: 2]`
|
|
}
|
|
)c");
|
|
expect_size(3);
|
|
expect_hint("0", "Format:");
|
|
expect_hint("1", "y:");
|
|
expect_hint("2", "z:");
|
|
};
|
|
|
|
test("VariadicTemplate") = [&] {
|
|
// Arg packs and constructors
|
|
run(R"c(
|
|
struct Foo{ Foo(); Foo(int x); };
|
|
|
|
void foo(Foo a, int b);
|
|
|
|
template <typename... Args>
|
|
void bar(Args... args) {
|
|
foo(args...);
|
|
}
|
|
|
|
template <typename... Args>
|
|
void baz(Args... args) { foo($(0)Foo{args...}, $(1)1); }
|
|
|
|
template <typename... Args>
|
|
void bax(Args... args) { foo($(2){args...}, args...); }
|
|
|
|
void foo() {
|
|
bar($(3)Foo{}, $(4)42);
|
|
bar($(5)42, $(6)42);
|
|
baz($(7)42);
|
|
bax($(8)42);
|
|
}
|
|
)c");
|
|
/// FIXME:
|
|
/// expect_hint("0", "a:");
|
|
/// expect_hint("1", "b:");
|
|
/// expect_hint("2", "a:");
|
|
expect_hint("3", "a:");
|
|
expect_hint("4", "b:");
|
|
expect_hint("5", "a:");
|
|
expect_hint("6", "b:");
|
|
expect_hint("7", "x:");
|
|
expect_hint("8", "b:");
|
|
|
|
// Doesn't expand all args
|
|
run(R"c(
|
|
void foo(int x, int y);
|
|
int id(int a, int b, int c);
|
|
template <typename... Args>
|
|
void bar(Args... args) {
|
|
foo(id($(0)args, $(1)1, $(2)args)...);
|
|
}
|
|
void foo() {
|
|
bar(1, 2); // FIXME: We could have `bar(a: 1, a: 2)` here.
|
|
}
|
|
)c");
|
|
/// FIXME:
|
|
/// expect_size(3);
|
|
/// expect_hint("0", "a:");
|
|
/// expect_hint("1", "b:");
|
|
/// expect_hint("2", "c:");
|
|
};
|
|
|
|
skip / test("Dependent") = [&] {
|
|
// Dependent calls
|
|
run(R"c(
|
|
template <typename T>
|
|
void nonmember(T par1);
|
|
|
|
template <typename T>
|
|
struct A {
|
|
void member(T par2);
|
|
static void static_member(T par3);
|
|
};
|
|
|
|
void overload(int anInt);
|
|
void overload(double aDouble);
|
|
|
|
template <typename T>
|
|
struct S {
|
|
void bar(A<T> a, T t) {
|
|
nonmember($(0)t);
|
|
a.member($(1)t);
|
|
A<T>::static_member($(2)t);
|
|
// We don't want to arbitrarily pick between
|
|
// "anInt" or "aDouble", so just show no hint.
|
|
overload(T{});
|
|
}
|
|
};
|
|
)c");
|
|
expect_size(3);
|
|
expect_hint("0", "par1:");
|
|
expect_hint("1", "par2:");
|
|
expect_hint("2", "par3:");
|
|
};
|
|
};
|
|
|
|
} // namespace
|
|
} // namespace clice::testing
|