Files
clice/tests/unit/Feature/InlayHint.cpp
2025-09-07 23:27:39 +08:00

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", "&param:");
// 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", "&param:");
// 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