mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 06:26:10 +00:00

The header for namespace symbol is barely useful. Differential Revision: https://reviews.llvm.org/D154068
4123 lines
122 KiB
C++
4123 lines
122 KiB
C++
//===-- HoverTests.cpp ----------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "AST.h"
|
|
#include "Annotations.h"
|
|
#include "Config.h"
|
|
#include "Hover.h"
|
|
#include "TestFS.h"
|
|
#include "TestIndex.h"
|
|
#include "TestTU.h"
|
|
#include "index/MemIndex.h"
|
|
#include "clang/AST/Attr.h"
|
|
#include "clang/Format/Format.h"
|
|
#include "clang/Index/IndexSymbol.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
#include <functional>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace clang {
|
|
namespace clangd {
|
|
namespace {
|
|
|
|
using PassMode = HoverInfo::PassType::PassMode;
|
|
|
|
std::string guard(llvm::StringRef Code) {
|
|
return "#pragma once\n" + Code.str();
|
|
}
|
|
|
|
TEST(Hover, Structured) {
|
|
struct {
|
|
const char *const Code;
|
|
const std::function<void(HoverInfo &)> ExpectedBuilder;
|
|
} Cases[] = {
|
|
// Global scope.
|
|
{R"cpp(
|
|
// Best foo ever.
|
|
void [[fo^o]]() {}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.Documentation = "Best foo ever.";
|
|
HI.Definition = "void foo()";
|
|
HI.ReturnType = "void";
|
|
HI.Type = "void ()";
|
|
HI.Parameters.emplace();
|
|
}},
|
|
// Inside namespace
|
|
{R"cpp(
|
|
namespace ns1 { namespace ns2 {
|
|
/// Best foo ever.
|
|
void [[fo^o]]() {}
|
|
}}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "ns1::ns2::";
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.Documentation = "Best foo ever.";
|
|
HI.Definition = "void foo()";
|
|
HI.ReturnType = "void";
|
|
HI.Type = "void ()";
|
|
HI.Parameters.emplace();
|
|
}},
|
|
// Field
|
|
{R"cpp(
|
|
namespace ns1 { namespace ns2 {
|
|
class Foo {
|
|
char [[b^ar]];
|
|
double y[2];
|
|
};
|
|
}}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "ns1::ns2::";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Name = "bar";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.Definition = "char bar";
|
|
HI.Type = "char";
|
|
HI.Offset = 0;
|
|
HI.Size = 8;
|
|
HI.Padding = 56;
|
|
HI.AccessSpecifier = "private";
|
|
}},
|
|
// Union field
|
|
{R"cpp(
|
|
union Foo {
|
|
char [[b^ar]];
|
|
double y[2];
|
|
};
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Name = "bar";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.Definition = "char bar";
|
|
HI.Type = "char";
|
|
HI.Size = 8;
|
|
HI.Padding = 120;
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
// Bitfield
|
|
{R"cpp(
|
|
struct Foo {
|
|
int [[^x]] : 1;
|
|
int y : 1;
|
|
};
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Name = "x";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.Definition = "int x : 1";
|
|
HI.Type = "int";
|
|
HI.Offset = 0;
|
|
HI.Size = 1;
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
// Local to class method.
|
|
{R"cpp(
|
|
namespace ns1 { namespace ns2 {
|
|
struct Foo {
|
|
void foo() {
|
|
int [[b^ar]];
|
|
}
|
|
};
|
|
}}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "ns1::ns2::";
|
|
HI.LocalScope = "Foo::foo::";
|
|
HI.Name = "bar";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Definition = "int bar";
|
|
HI.Type = "int";
|
|
}},
|
|
// Predefined variable
|
|
{R"cpp(
|
|
void foo() {
|
|
[[__f^unc__]];
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "__func__";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Documentation =
|
|
"Name of the current function (predefined variable)";
|
|
HI.Value = "\"foo\"";
|
|
HI.Type = "const char[4]";
|
|
}},
|
|
// Predefined variable (dependent)
|
|
{R"cpp(
|
|
template<int> void foo() {
|
|
[[__f^unc__]];
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "__func__";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Documentation =
|
|
"Name of the current function (predefined variable)";
|
|
HI.Type = "const char[]";
|
|
}},
|
|
// Anon namespace and local scope.
|
|
{R"cpp(
|
|
namespace ns1 { namespace {
|
|
struct {
|
|
char [[b^ar]];
|
|
} T;
|
|
}}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "ns1::";
|
|
HI.LocalScope = "(anonymous struct)::";
|
|
HI.Name = "bar";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.Definition = "char bar";
|
|
HI.Type = "char";
|
|
HI.Offset = 0;
|
|
HI.Size = 8;
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
// Struct definition shows size.
|
|
{R"cpp(
|
|
struct [[^X]]{};
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "X";
|
|
HI.Kind = index::SymbolKind::Struct;
|
|
HI.Definition = "struct X {}";
|
|
HI.Size = 8;
|
|
}},
|
|
// Variable with template type
|
|
{R"cpp(
|
|
template <typename T, class... Ts> class Foo { public: Foo(int); };
|
|
Foo<int, char, bool> [[fo^o]] = Foo<int, char, bool>(5);
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Definition = "Foo<int, char, bool> foo = Foo<int, char, bool>(5)";
|
|
HI.Type = "Foo<int, char, bool>";
|
|
}},
|
|
// Implicit template instantiation
|
|
{R"cpp(
|
|
template <typename T> class vector{};
|
|
[[vec^tor]]<int> foo;
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "vector<int>";
|
|
HI.Kind = index::SymbolKind::Class;
|
|
HI.Definition = "template <> class vector<int> {}";
|
|
}},
|
|
// Class template
|
|
{R"cpp(
|
|
template <template<typename, bool...> class C,
|
|
typename = char,
|
|
int = 0,
|
|
bool Q = false,
|
|
class... Ts> class Foo final {};
|
|
template <template<typename, bool...> class T>
|
|
[[F^oo]]<T> foo;
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "Foo";
|
|
HI.Kind = index::SymbolKind::Class;
|
|
HI.Definition =
|
|
R"cpp(template <template <typename, bool...> class C, typename = char, int = 0,
|
|
bool Q = false, class... Ts>
|
|
class Foo final {})cpp";
|
|
HI.TemplateParameters = {
|
|
{{"template <typename, bool...> class"},
|
|
std::string("C"),
|
|
std::nullopt},
|
|
{{"typename"}, std::nullopt, std::string("char")},
|
|
{{"int"}, std::nullopt, std::string("0")},
|
|
{{"bool"}, std::string("Q"), std::string("false")},
|
|
{{"class..."}, std::string("Ts"), std::nullopt},
|
|
};
|
|
}},
|
|
// Function template
|
|
{R"cpp(
|
|
template <template<typename, bool...> class C,
|
|
typename = char,
|
|
int = 0,
|
|
bool Q = false,
|
|
class... Ts> void foo();
|
|
template<typename, bool...> class Foo;
|
|
|
|
void bar() {
|
|
[[fo^o]]<Foo>();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.Definition = "template <> void foo<Foo, char, 0, false, <>>()";
|
|
HI.ReturnType = "void";
|
|
HI.Type = "void ()";
|
|
HI.Parameters.emplace();
|
|
}},
|
|
// Function decl
|
|
{R"cpp(
|
|
template<typename, bool...> class Foo {};
|
|
Foo<bool, true, false> foo(int, bool T = false);
|
|
|
|
void bar() {
|
|
[[fo^o]](3);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.Definition = "Foo<bool, true, false> foo(int, bool T = false)";
|
|
HI.ReturnType = "Foo<bool, true, false>";
|
|
HI.Type = "Foo<bool, true, false> (int, bool)";
|
|
HI.Parameters = {
|
|
{{"int"}, std::nullopt, std::nullopt},
|
|
{{"bool"}, std::string("T"), std::string("false")},
|
|
};
|
|
}},
|
|
// Pointers to lambdas
|
|
{R"cpp(
|
|
void foo() {
|
|
auto lamb = [](int T, bool B) -> bool { return T && B; };
|
|
auto *b = &lamb;
|
|
auto *[[^c]] = &b;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "foo::";
|
|
HI.Name = "c";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Definition = "auto *c = &b";
|
|
HI.Type = "(lambda) **";
|
|
HI.ReturnType = "bool";
|
|
HI.Parameters = {
|
|
{{"int"}, std::string("T"), std::nullopt},
|
|
{{"bool"}, std::string("B"), std::nullopt},
|
|
};
|
|
return HI;
|
|
}},
|
|
// Lambda parameter with decltype reference
|
|
{R"cpp(
|
|
auto lamb = [](int T, bool B) -> bool { return T && B; };
|
|
void foo(decltype(lamb)& bar) {
|
|
[[ba^r]](0, false);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "foo::";
|
|
HI.Name = "bar";
|
|
HI.Kind = index::SymbolKind::Parameter;
|
|
HI.Definition = "decltype(lamb) &bar";
|
|
HI.Type = {"decltype(lamb) &", "(lambda) &"};
|
|
HI.ReturnType = "bool";
|
|
HI.Parameters = {
|
|
{{"int"}, std::string("T"), std::nullopt},
|
|
{{"bool"}, std::string("B"), std::nullopt},
|
|
};
|
|
return HI;
|
|
}},
|
|
// Lambda parameter with decltype
|
|
{R"cpp(
|
|
auto lamb = [](int T, bool B) -> bool { return T && B; };
|
|
void foo(decltype(lamb) bar) {
|
|
[[ba^r]](0, false);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "foo::";
|
|
HI.Name = "bar";
|
|
HI.Kind = index::SymbolKind::Parameter;
|
|
HI.Definition = "decltype(lamb) bar";
|
|
HI.Type = "class (lambda)";
|
|
HI.ReturnType = "bool";
|
|
HI.Parameters = {
|
|
{{"int"}, std::string("T"), std::nullopt},
|
|
{{"bool"}, std::string("B"), std::nullopt},
|
|
};
|
|
HI.Value = "false";
|
|
return HI;
|
|
}},
|
|
// Lambda variable
|
|
{R"cpp(
|
|
void foo() {
|
|
int bar = 5;
|
|
auto lamb = [&bar](int T, bool B) -> bool { return T && B && bar; };
|
|
bool res = [[lam^b]](bar, false);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "foo::";
|
|
HI.Name = "lamb";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Definition = "auto lamb = [&bar](int T, bool B) -> bool {}";
|
|
HI.Type = "class (lambda)";
|
|
HI.ReturnType = "bool";
|
|
HI.Parameters = {
|
|
{{"int"}, std::string("T"), std::nullopt},
|
|
{{"bool"}, std::string("B"), std::nullopt},
|
|
};
|
|
return HI;
|
|
}},
|
|
// Local variable in lambda
|
|
{R"cpp(
|
|
void foo() {
|
|
auto lamb = []{int [[te^st]];};
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "foo::(anonymous class)::operator()::";
|
|
HI.Name = "test";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Definition = "int test";
|
|
HI.Type = "int";
|
|
}},
|
|
// Partially-specialized class template. (formerly type-parameter-0-0)
|
|
{R"cpp(
|
|
template <typename T> class X;
|
|
template <typename T> class [[^X]]<T*> {};
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "X<T *>";
|
|
HI.NamespaceScope = "";
|
|
HI.Kind = index::SymbolKind::Class;
|
|
HI.Definition = "template <typename T> class X<T *> {}";
|
|
}},
|
|
// Constructor of partially-specialized class template
|
|
{R"cpp(
|
|
template<typename, typename=void> struct X;
|
|
template<typename T> struct X<T*>{ [[^X]](); };
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "X";
|
|
HI.LocalScope = "X<T *>::"; // FIXME: X<T *, void>::
|
|
HI.Kind = index::SymbolKind::Constructor;
|
|
HI.Definition = "X()";
|
|
HI.Parameters.emplace();
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
{"class X { [[^~]]X(); };", // FIXME: Should be [[~X]]()
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "~X";
|
|
HI.LocalScope = "X::";
|
|
HI.Kind = index::SymbolKind::Destructor;
|
|
HI.Definition = "~X()";
|
|
HI.Parameters.emplace();
|
|
HI.AccessSpecifier = "private";
|
|
}},
|
|
{"class X { [[op^erator]] int(); };",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "operator int";
|
|
HI.LocalScope = "X::";
|
|
HI.Kind = index::SymbolKind::ConversionFunction;
|
|
HI.Definition = "operator int()";
|
|
HI.Parameters.emplace();
|
|
HI.AccessSpecifier = "private";
|
|
}},
|
|
{"class X { operator [[^X]](); };",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "X";
|
|
HI.Kind = index::SymbolKind::Class;
|
|
HI.Definition = "class X {}";
|
|
}},
|
|
|
|
// auto on structured bindings
|
|
{R"cpp(
|
|
void foo() {
|
|
struct S { int x; float y; };
|
|
[[au^to]] [x, y] = S();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "S";
|
|
}},
|
|
// undeduced auto
|
|
{R"cpp(
|
|
template<typename T>
|
|
void foo() {
|
|
[[au^to]] x = T{};
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "/* not deduced */";
|
|
}},
|
|
// auto on lambda
|
|
{R"cpp(
|
|
void foo() {
|
|
[[au^to]] lamb = []{};
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "class(lambda)";
|
|
}},
|
|
// auto on template instantiation
|
|
{R"cpp(
|
|
template<typename T> class Foo{};
|
|
void foo() {
|
|
[[au^to]] x = Foo<int>();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "Foo<int>";
|
|
}},
|
|
// auto on specialized template
|
|
{R"cpp(
|
|
template<typename T> class Foo{};
|
|
template<> class Foo<int>{};
|
|
void foo() {
|
|
[[au^to]] x = Foo<int>();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "Foo<int>";
|
|
}},
|
|
|
|
// empty macro
|
|
{R"cpp(
|
|
#define MACRO
|
|
[[MAC^RO]]
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "MACRO";
|
|
HI.Kind = index::SymbolKind::Macro;
|
|
HI.Definition = "#define MACRO";
|
|
}},
|
|
|
|
// object-like macro
|
|
{R"cpp(
|
|
#define MACRO 41
|
|
int x = [[MAC^RO]];
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "MACRO";
|
|
HI.Kind = index::SymbolKind::Macro;
|
|
HI.Value = "41 (0x29)";
|
|
HI.Type = "int";
|
|
HI.Definition = "#define MACRO 41\n\n"
|
|
"// Expands to\n"
|
|
"41";
|
|
}},
|
|
|
|
// function-like macro
|
|
{R"cpp(
|
|
// Best MACRO ever.
|
|
#define MACRO(x,y,z) void foo(x, y, z)
|
|
[[MAC^RO]](int, double d, bool z = false);
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "MACRO";
|
|
HI.Kind = index::SymbolKind::Macro;
|
|
HI.Definition = "#define MACRO(x, y, z) void foo(x, y, z)\n\n"
|
|
"// Expands to\n"
|
|
"void foo(int, double d, bool z = false)";
|
|
}},
|
|
|
|
// nested macro
|
|
{R"cpp(
|
|
#define STRINGIFY_AUX(s) #s
|
|
#define STRINGIFY(s) STRINGIFY_AUX(s)
|
|
#define DECL_STR(NAME, VALUE) const char *v_##NAME = STRINGIFY(VALUE)
|
|
#define FOO 41
|
|
|
|
[[DECL^_STR]](foo, FOO);
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "DECL_STR";
|
|
HI.Kind = index::SymbolKind::Macro;
|
|
HI.Type = HoverInfo::PrintedType("const char *");
|
|
HI.Definition = "#define DECL_STR(NAME, VALUE) const char *v_##NAME = "
|
|
"STRINGIFY(VALUE)\n\n"
|
|
"// Expands to\n"
|
|
"const char *v_foo = \"41\"";
|
|
}},
|
|
|
|
// constexprs
|
|
{R"cpp(
|
|
constexpr int add(int a, int b) { return a + b; }
|
|
int [[b^ar]] = add(1, 2);
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "bar";
|
|
HI.Definition = "int bar = add(1, 2)";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Type = "int";
|
|
HI.NamespaceScope = "";
|
|
HI.Value = "3";
|
|
}},
|
|
{R"cpp(
|
|
int [[b^ar]] = sizeof(char);
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "bar";
|
|
HI.Definition = "int bar = sizeof(char)";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Type = "int";
|
|
HI.NamespaceScope = "";
|
|
HI.Value = "1";
|
|
}},
|
|
{R"cpp(
|
|
template<int a, int b> struct Add {
|
|
static constexpr int result = a + b;
|
|
};
|
|
int [[ba^r]] = Add<1, 2>::result;
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "bar";
|
|
HI.Definition = "int bar = Add<1, 2>::result";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Type = "int";
|
|
HI.NamespaceScope = "";
|
|
HI.Value = "3";
|
|
}},
|
|
{R"cpp(
|
|
enum Color { RED = -123, GREEN = 5, };
|
|
Color x = [[GR^EEN]];
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "GREEN";
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Color::";
|
|
HI.Definition = "GREEN = 5";
|
|
HI.Kind = index::SymbolKind::EnumConstant;
|
|
HI.Type = "enum Color";
|
|
HI.Value = "5"; // Numeric on the enumerator name, no hex as small.
|
|
}},
|
|
{R"cpp(
|
|
enum Color { RED = -123, GREEN = 5, };
|
|
Color x = RED;
|
|
Color y = [[^x]];
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "x";
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "Color x = RED";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Type = "Color";
|
|
HI.Value = "RED (0xffffff85)"; // Symbolic on an expression.
|
|
}},
|
|
{R"cpp(
|
|
template<int a, int b> struct Add {
|
|
static constexpr int result = a + b;
|
|
};
|
|
int bar = Add<1, 2>::[[resu^lt]];
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "result";
|
|
HI.Definition = "static constexpr int result = a + b";
|
|
HI.Kind = index::SymbolKind::StaticProperty;
|
|
HI.Type = "const int";
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Add<1, 2>::";
|
|
HI.Value = "3";
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
{R"cpp(
|
|
using my_int = int;
|
|
constexpr my_int answer() { return 40 + 2; }
|
|
int x = [[ans^wer]]();
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "answer";
|
|
HI.Definition = "constexpr my_int answer()";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.Type = {"my_int ()", "int ()"};
|
|
HI.ReturnType = {"my_int", "int"};
|
|
HI.Parameters.emplace();
|
|
HI.NamespaceScope = "";
|
|
HI.Value = "42 (0x2a)";
|
|
}},
|
|
{R"cpp(
|
|
const char *[[ba^r]] = "1234";
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "bar";
|
|
HI.Definition = "const char *bar = \"1234\"";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Type = "const char *";
|
|
HI.NamespaceScope = "";
|
|
HI.Value = "&\"1234\"[0]";
|
|
}},
|
|
{R"cpp(// Should not crash
|
|
template <typename T>
|
|
struct Tmpl {
|
|
Tmpl(int name);
|
|
};
|
|
|
|
template <typename A>
|
|
void boom(int name) {
|
|
new Tmpl<A>([[na^me]]);
|
|
})cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "name";
|
|
HI.Definition = "int name";
|
|
HI.Kind = index::SymbolKind::Parameter;
|
|
HI.Type = "int";
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "boom::";
|
|
}},
|
|
{
|
|
R"cpp(// Should not print inline or anon namespaces.
|
|
namespace ns {
|
|
inline namespace in_ns {
|
|
namespace a {
|
|
namespace {
|
|
namespace b {
|
|
inline namespace in_ns2 {
|
|
class Foo {};
|
|
} // in_ns2
|
|
} // b
|
|
} // anon
|
|
} // a
|
|
} // in_ns
|
|
} // ns
|
|
void foo() {
|
|
ns::a::b::[[F^oo]] x;
|
|
(void)x;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "Foo";
|
|
HI.Kind = index::SymbolKind::Class;
|
|
HI.NamespaceScope = "ns::a::b::";
|
|
HI.Definition = "class Foo {}";
|
|
}},
|
|
{
|
|
R"cpp(
|
|
template <typename T> class Foo {};
|
|
class X;
|
|
void foo() {
|
|
[[^auto]] x = Foo<X>();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "Foo<X>";
|
|
}},
|
|
{// Falls back to primary template, when the type is not instantiated.
|
|
R"cpp(
|
|
// comment from primary
|
|
template <typename T> class Foo {};
|
|
// comment from specialization
|
|
template <typename T> class Foo<T*> {};
|
|
void foo() {
|
|
[[Fo^o]]<int*> *x = nullptr;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "Foo<int *>";
|
|
HI.Kind = index::SymbolKind::Class;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "template <> class Foo<int *>";
|
|
// FIXME: Maybe force instantiation to make use of real template
|
|
// pattern.
|
|
HI.Documentation = "comment from primary";
|
|
}},
|
|
{// Template Type Parameter
|
|
R"cpp(
|
|
template <typename [[^T]] = int> void foo();
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "T";
|
|
HI.Kind = index::SymbolKind::TemplateTypeParm;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "typename T = int";
|
|
HI.LocalScope = "foo::";
|
|
HI.Type = "typename";
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
{// TemplateTemplate Type Parameter
|
|
R"cpp(
|
|
template <template<typename> class [[^T]]> void foo();
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "T";
|
|
HI.Kind = index::SymbolKind::TemplateTemplateParm;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "template <typename> class T";
|
|
HI.LocalScope = "foo::";
|
|
HI.Type = "template <typename> class";
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
{// NonType Template Parameter
|
|
R"cpp(
|
|
template <int [[^T]] = 5> void foo();
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "T";
|
|
HI.Kind = index::SymbolKind::NonTypeTemplateParm;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "int T = 5";
|
|
HI.LocalScope = "foo::";
|
|
HI.Type = "int";
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
|
|
{// Getter
|
|
R"cpp(
|
|
struct X { int Y; float [[^y]]() { return Y; } };
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "y";
|
|
HI.Kind = index::SymbolKind::InstanceMethod;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "float y()";
|
|
HI.LocalScope = "X::";
|
|
HI.Documentation = "Trivial accessor for `Y`.";
|
|
HI.Type = "float ()";
|
|
HI.ReturnType = "float";
|
|
HI.Parameters.emplace();
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
{// Setter
|
|
R"cpp(
|
|
struct X { int Y; void [[^setY]](float v) { Y = v; } };
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "setY";
|
|
HI.Kind = index::SymbolKind::InstanceMethod;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "void setY(float v)";
|
|
HI.LocalScope = "X::";
|
|
HI.Documentation = "Trivial setter for `Y`.";
|
|
HI.Type = "void (float)";
|
|
HI.ReturnType = "void";
|
|
HI.Parameters.emplace();
|
|
HI.Parameters->emplace_back();
|
|
HI.Parameters->back().Type = "float";
|
|
HI.Parameters->back().Name = "v";
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
{// Setter (builder)
|
|
R"cpp(
|
|
struct X { int Y; X& [[^setY]](float v) { Y = v; return *this; } };
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "setY";
|
|
HI.Kind = index::SymbolKind::InstanceMethod;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "X &setY(float v)";
|
|
HI.LocalScope = "X::";
|
|
HI.Documentation = "Trivial setter for `Y`.";
|
|
HI.Type = "X &(float)";
|
|
HI.ReturnType = "X &";
|
|
HI.Parameters.emplace();
|
|
HI.Parameters->emplace_back();
|
|
HI.Parameters->back().Type = "float";
|
|
HI.Parameters->back().Name = "v";
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
{// Setter (move)
|
|
R"cpp(
|
|
namespace std { template<typename T> T&& move(T&& t); }
|
|
struct X { int Y; void [[^setY]](float v) { Y = std::move(v); } };
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "setY";
|
|
HI.Kind = index::SymbolKind::InstanceMethod;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "void setY(float v)";
|
|
HI.LocalScope = "X::";
|
|
HI.Documentation = "Trivial setter for `Y`.";
|
|
HI.Type = "void (float)";
|
|
HI.ReturnType = "void";
|
|
HI.Parameters.emplace();
|
|
HI.Parameters->emplace_back();
|
|
HI.Parameters->back().Type = "float";
|
|
HI.Parameters->back().Name = "v";
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
{// Field type initializer.
|
|
R"cpp(
|
|
struct X { int x = 2; };
|
|
X ^[[x]];
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "x";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "X x";
|
|
HI.Type = "X";
|
|
}},
|
|
{// Don't crash on null types.
|
|
R"cpp(auto [^[[x]]] = 1; /*error-ok*/)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "x";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "";
|
|
HI.Type = "NULL TYPE";
|
|
// Bindings are in theory public members of an anonymous struct.
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
{// Extra info for function call.
|
|
R"cpp(
|
|
void fun(int arg_a, int &arg_b) {};
|
|
void code() {
|
|
int a = 1, b = 2;
|
|
fun(a, [[^b]]);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "b";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "int b = 2";
|
|
HI.LocalScope = "code::";
|
|
HI.Value = "2";
|
|
HI.Type = "int";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "arg_b";
|
|
HI.CalleeArgInfo->Type = "int &";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::Ref, false};
|
|
}},
|
|
{// make_unique-like function call
|
|
R"cpp(
|
|
struct Foo {
|
|
explicit Foo(int arg_a) {}
|
|
};
|
|
template<class T, class... Args>
|
|
T make(Args&&... args)
|
|
{
|
|
return T(args...);
|
|
}
|
|
|
|
void code() {
|
|
int a = 1;
|
|
auto foo = make<Foo>([[^a]]);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "a";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "int a = 1";
|
|
HI.LocalScope = "code::";
|
|
HI.Value = "1";
|
|
HI.Type = "int";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "arg_a";
|
|
HI.CalleeArgInfo->Type = "int";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::Value, false};
|
|
}},
|
|
{
|
|
R"cpp(
|
|
void foobar(const float &arg);
|
|
int main() {
|
|
int a = 0;
|
|
foobar([[^a]]);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "a";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "int a = 0";
|
|
HI.LocalScope = "main::";
|
|
HI.Value = "0";
|
|
HI.Type = "int";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "arg";
|
|
HI.CalleeArgInfo->Type = "const float &";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::Value, true};
|
|
}},
|
|
{
|
|
R"cpp(
|
|
struct Foo {
|
|
explicit Foo(const float& arg) {}
|
|
};
|
|
int main() {
|
|
int a = 0;
|
|
Foo foo([[^a]]);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "a";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "int a = 0";
|
|
HI.LocalScope = "main::";
|
|
HI.Value = "0";
|
|
HI.Type = "int";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "arg";
|
|
HI.CalleeArgInfo->Type = "const float &";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::Value, true};
|
|
}},
|
|
{// Literal passed to function call
|
|
R"cpp(
|
|
void fun(int arg_a, const int &arg_b) {};
|
|
void code() {
|
|
int a = 1;
|
|
fun(a, [[^2]]);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "literal";
|
|
HI.Kind = index::SymbolKind::Unknown;
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "arg_b";
|
|
HI.CalleeArgInfo->Type = "const int &";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::ConstRef, false};
|
|
}},
|
|
{// Expression passed to function call
|
|
R"cpp(
|
|
void fun(int arg_a, const int &arg_b) {};
|
|
void code() {
|
|
int a = 1;
|
|
fun(a, 1 [[^+]] 2);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "expression";
|
|
HI.Kind = index::SymbolKind::Unknown;
|
|
HI.Type = "int";
|
|
HI.Value = "3";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "arg_b";
|
|
HI.CalleeArgInfo->Type = "const int &";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::ConstRef, false};
|
|
}},
|
|
{
|
|
R"cpp(
|
|
int add(int lhs, int rhs);
|
|
int main() {
|
|
add(1 [[^+]] 2, 3);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "expression";
|
|
HI.Kind = index::SymbolKind::Unknown;
|
|
HI.Type = "int";
|
|
HI.Value = "3";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "lhs";
|
|
HI.CalleeArgInfo->Type = "int";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::Value, false};
|
|
}},
|
|
{
|
|
R"cpp(
|
|
void foobar(const float &arg);
|
|
int main() {
|
|
foobar([[^0]]);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "literal";
|
|
HI.Kind = index::SymbolKind::Unknown;
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "arg";
|
|
HI.CalleeArgInfo->Type = "const float &";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::Value, true};
|
|
}},
|
|
{// Extra info for method call.
|
|
R"cpp(
|
|
class C {
|
|
public:
|
|
void fun(int arg_a = 3, int arg_b = 4) {}
|
|
};
|
|
void code() {
|
|
int a = 1, b = 2;
|
|
C c;
|
|
c.fun([[^a]], b);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "a";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "int a = 1";
|
|
HI.LocalScope = "code::";
|
|
HI.Value = "1";
|
|
HI.Type = "int";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "arg_a";
|
|
HI.CalleeArgInfo->Type = "int";
|
|
HI.CalleeArgInfo->Default = "3";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::Value, false};
|
|
}},
|
|
{
|
|
R"cpp(
|
|
struct Foo {
|
|
Foo(const int &);
|
|
};
|
|
void foo(Foo);
|
|
void bar() {
|
|
const int x = 0;
|
|
foo([[^x]]);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "x";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "const int x = 0";
|
|
HI.LocalScope = "bar::";
|
|
HI.Value = "0";
|
|
HI.Type = "const int";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Type = "Foo";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::ConstRef, true};
|
|
}},
|
|
{// Dont crash on invalid decl
|
|
R"cpp(
|
|
// error-ok
|
|
struct Foo {
|
|
Bar [[x^x]];
|
|
};)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "xx";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "int xx";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Type = "int";
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
{R"cpp(
|
|
// error-ok
|
|
struct Foo {
|
|
Bar xx;
|
|
int [[y^y]];
|
|
};)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "yy";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "int yy";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Type = "int";
|
|
HI.AccessSpecifier = "public";
|
|
}},
|
|
{// No crash on InitListExpr.
|
|
R"cpp(
|
|
struct Foo {
|
|
int a[10];
|
|
};
|
|
constexpr Foo k2 = {
|
|
^[[{]]1} // FIXME: why the hover range is 1 character?
|
|
};
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "expression";
|
|
HI.Kind = index::SymbolKind::Unknown;
|
|
HI.Type = "int[10]";
|
|
HI.Value = "{1}";
|
|
}},
|
|
{// Var template decl
|
|
R"cpp(
|
|
using m_int = int;
|
|
|
|
template <int Size> m_int ^[[arr]][Size];
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "arr";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Type = {"m_int[Size]", "int[Size]"};
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "template <int Size> m_int arr[Size]";
|
|
HI.TemplateParameters = {{{"int"}, {"Size"}, std::nullopt}};
|
|
}},
|
|
{// Var template decl specialization
|
|
R"cpp(
|
|
using m_int = int;
|
|
|
|
template <int Size> m_int arr[Size];
|
|
|
|
template <> m_int ^[[arr]]<4>[4];
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "arr<4>";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Type = {"m_int[4]", "int[4]"};
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "m_int arr[4]";
|
|
}},
|
|
{// Canonical type
|
|
R"cpp(
|
|
template<typename T>
|
|
struct TestHover {
|
|
using Type = T;
|
|
};
|
|
|
|
void code() {
|
|
TestHover<int>::Type ^[[a]];
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "a";
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "code::";
|
|
HI.Definition = "TestHover<int>::Type a";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Type = {"TestHover<int>::Type", "int"};
|
|
}},
|
|
{// Canonical template type
|
|
R"cpp(
|
|
template<typename T>
|
|
void ^[[foo]](T arg) {}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "template <typename T> void foo(T arg)";
|
|
HI.Type = "void (T)";
|
|
HI.ReturnType = "void";
|
|
HI.Parameters = {{{"T"}, std::string("arg"), std::nullopt}};
|
|
HI.TemplateParameters = {
|
|
{{"typename"}, std::string("T"), std::nullopt}};
|
|
}},
|
|
{// TypeAlias Template
|
|
R"cpp(
|
|
template<typename T>
|
|
using ^[[alias]] = T;
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "alias";
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "template <typename T> using alias = T";
|
|
HI.Type = "T";
|
|
HI.TemplateParameters = {
|
|
{{"typename"}, std::string("T"), std::nullopt}};
|
|
}},
|
|
{// TypeAlias Template
|
|
R"cpp(
|
|
template<typename T>
|
|
using A = T;
|
|
|
|
template<typename T>
|
|
using ^[[AA]] = A<T>;
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "AA";
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "template <typename T> using AA = A<T>";
|
|
HI.Type = {"A<T>", "type-parameter-0-0"}; // FIXME: should be 'T'
|
|
HI.TemplateParameters = {
|
|
{{"typename"}, std::string("T"), std::nullopt}};
|
|
}},
|
|
{// Constant array
|
|
R"cpp(
|
|
using m_int = int;
|
|
|
|
m_int ^[[arr]][10];
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "arr";
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Definition = "m_int arr[10]";
|
|
HI.Type = {"m_int[10]", "int[10]"};
|
|
}},
|
|
{// Incomplete array
|
|
R"cpp(
|
|
using m_int = int;
|
|
|
|
extern m_int ^[[arr]][];
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "arr";
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Definition = "extern m_int arr[]";
|
|
HI.Type = {"m_int[]", "int[]"};
|
|
}},
|
|
{// Dependent size array
|
|
R"cpp(
|
|
using m_int = int;
|
|
|
|
template<int Size>
|
|
struct Test {
|
|
m_int ^[[arr]][Size];
|
|
};
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "arr";
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Test<Size>::";
|
|
HI.AccessSpecifier = "public";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.Definition = "m_int arr[Size]";
|
|
HI.Type = {"m_int[Size]", "int[Size]"};
|
|
}},
|
|
{// Bitfield offset, size and padding
|
|
R"cpp(
|
|
struct Foo {
|
|
char x;
|
|
char [[^y]] : 1;
|
|
int z;
|
|
};
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Name = "y";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.Definition = "char y : 1";
|
|
HI.Type = "char";
|
|
HI.Offset = 8;
|
|
HI.Size = 1;
|
|
HI.Padding = 23;
|
|
HI.AccessSpecifier = "public";
|
|
}}};
|
|
for (const auto &Case : Cases) {
|
|
SCOPED_TRACE(Case.Code);
|
|
|
|
Annotations T(Case.Code);
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
TU.ExtraArgs.push_back("-std=c++17");
|
|
// Types might be different depending on the target triplet, we chose a
|
|
// fixed one to make sure tests passes on different platform.
|
|
TU.ExtraArgs.push_back("--target=x86_64-pc-linux-gnu");
|
|
auto AST = TU.build();
|
|
Config Cfg;
|
|
Cfg.Hover.ShowAKA = true;
|
|
WithContextValue WithCfg(Config::Key, std::move(Cfg));
|
|
|
|
auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
ASSERT_TRUE(H);
|
|
HoverInfo Expected;
|
|
Expected.SymRange = T.range();
|
|
Case.ExpectedBuilder(Expected);
|
|
|
|
EXPECT_EQ(H->NamespaceScope, Expected.NamespaceScope);
|
|
EXPECT_EQ(H->LocalScope, Expected.LocalScope);
|
|
EXPECT_EQ(H->Name, Expected.Name);
|
|
EXPECT_EQ(H->Kind, Expected.Kind);
|
|
EXPECT_EQ(H->Documentation, Expected.Documentation);
|
|
EXPECT_EQ(H->Definition, Expected.Definition);
|
|
EXPECT_EQ(H->Type, Expected.Type);
|
|
EXPECT_EQ(H->ReturnType, Expected.ReturnType);
|
|
EXPECT_EQ(H->Parameters, Expected.Parameters);
|
|
EXPECT_EQ(H->TemplateParameters, Expected.TemplateParameters);
|
|
EXPECT_EQ(H->SymRange, Expected.SymRange);
|
|
EXPECT_EQ(H->Value, Expected.Value);
|
|
EXPECT_EQ(H->Size, Expected.Size);
|
|
EXPECT_EQ(H->Offset, Expected.Offset);
|
|
EXPECT_EQ(H->AccessSpecifier, Expected.AccessSpecifier);
|
|
EXPECT_EQ(H->CalleeArgInfo, Expected.CalleeArgInfo);
|
|
EXPECT_EQ(H->CallPassType, Expected.CallPassType);
|
|
}
|
|
}
|
|
|
|
TEST(Hover, DefinitionLanuage) {
|
|
struct {
|
|
const char *const Code;
|
|
const std::string ClangLanguageFlag;
|
|
const char *const ExpectedDefinitionLanguage;
|
|
} Cases[] = {{R"cpp(
|
|
void [[some^Global]]() {}
|
|
)cpp",
|
|
"", "cpp"},
|
|
{R"cpp(
|
|
void [[some^Global]]() {}
|
|
)cpp",
|
|
"-xobjective-c++", "objective-cpp"},
|
|
{R"cpp(
|
|
void [[some^Global]]() {}
|
|
)cpp",
|
|
"-xobjective-c", "objective-c"}};
|
|
for (const auto &Case : Cases) {
|
|
SCOPED_TRACE(Case.Code);
|
|
|
|
Annotations T(Case.Code);
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
if (!Case.ClangLanguageFlag.empty())
|
|
TU.ExtraArgs.push_back(Case.ClangLanguageFlag);
|
|
auto AST = TU.build();
|
|
|
|
auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
ASSERT_TRUE(H);
|
|
|
|
EXPECT_STREQ(H->DefinitionLanguage, Case.ExpectedDefinitionLanguage);
|
|
}
|
|
}
|
|
|
|
TEST(Hover, CallPassType) {
|
|
const llvm::StringRef CodePrefix = R"cpp(
|
|
class Base {};
|
|
class Derived : public Base {};
|
|
class CustomClass {
|
|
public:
|
|
CustomClass() {}
|
|
CustomClass(const Base &x) {}
|
|
CustomClass(int &x) {}
|
|
CustomClass(float x) {}
|
|
CustomClass(int x, int y) {}
|
|
};
|
|
|
|
void int_by_ref(int &x) {}
|
|
void int_by_const_ref(const int &x) {}
|
|
void int_by_value(int x) {}
|
|
void base_by_ref(Base &x) {}
|
|
void base_by_const_ref(const Base &x) {}
|
|
void base_by_value(Base x) {}
|
|
void float_by_value(float x) {}
|
|
void custom_by_value(CustomClass x) {}
|
|
|
|
void fun() {
|
|
int int_x;
|
|
int &int_ref = int_x;
|
|
const int &int_const_ref = int_x;
|
|
Base base;
|
|
const Base &base_const_ref = base;
|
|
Derived derived;
|
|
float float_x;
|
|
)cpp";
|
|
const llvm::StringRef CodeSuffix = "}";
|
|
|
|
struct {
|
|
const char *const Code;
|
|
HoverInfo::PassType::PassMode PassBy;
|
|
bool Converted;
|
|
} Tests[] = {
|
|
// Integer tests
|
|
{"int_by_value([[^int_x]]);", PassMode::Value, false},
|
|
{"int_by_value([[^123]]);", PassMode::Value, false},
|
|
{"int_by_ref([[^int_x]]);", PassMode::Ref, false},
|
|
{"int_by_const_ref([[^int_x]]);", PassMode::ConstRef, false},
|
|
{"int_by_const_ref([[^123]]);", PassMode::ConstRef, false},
|
|
{"int_by_value([[^int_ref]]);", PassMode::Value, false},
|
|
{"int_by_const_ref([[^int_ref]]);", PassMode::ConstRef, false},
|
|
{"int_by_const_ref([[^int_ref]]);", PassMode::ConstRef, false},
|
|
{"int_by_const_ref([[^int_const_ref]]);", PassMode::ConstRef, false},
|
|
// Custom class tests
|
|
{"base_by_ref([[^base]]);", PassMode::Ref, false},
|
|
{"base_by_const_ref([[^base]]);", PassMode::ConstRef, false},
|
|
{"base_by_const_ref([[^base_const_ref]]);", PassMode::ConstRef, false},
|
|
{"base_by_value([[^base]]);", PassMode::Value, false},
|
|
{"base_by_value([[^base_const_ref]]);", PassMode::Value, false},
|
|
{"base_by_ref([[^derived]]);", PassMode::Ref, false},
|
|
{"base_by_const_ref([[^derived]]);", PassMode::ConstRef, false},
|
|
{"base_by_value([[^derived]]);", PassMode::Value, false},
|
|
// Custom class constructor tests
|
|
{"CustomClass c1([[^base]]);", PassMode::ConstRef, false},
|
|
{"auto c2 = new CustomClass([[^base]]);", PassMode::ConstRef, false},
|
|
{"CustomClass c3([[^int_x]]);", PassMode::Ref, false},
|
|
{"CustomClass c3(int_x, [[^int_x]]);", PassMode::Value, false},
|
|
// Converted tests
|
|
{"float_by_value([[^int_x]]);", PassMode::Value, true},
|
|
{"float_by_value([[^int_ref]]);", PassMode::Value, true},
|
|
{"float_by_value([[^int_const_ref]]);", PassMode::Value, true},
|
|
{"float_by_value([[^123.0f]]);", PassMode::Value, false},
|
|
{"float_by_value([[^123]]);", PassMode::Value, true},
|
|
{"custom_by_value([[^int_x]]);", PassMode::Ref, true},
|
|
{"custom_by_value([[^float_x]]);", PassMode::Value, true},
|
|
{"custom_by_value([[^base]]);", PassMode::ConstRef, true},
|
|
};
|
|
for (const auto &Test : Tests) {
|
|
SCOPED_TRACE(Test.Code);
|
|
|
|
const auto Code = (CodePrefix + Test.Code + CodeSuffix).str();
|
|
Annotations T(Code);
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
TU.ExtraArgs.push_back("-std=c++17");
|
|
auto AST = TU.build();
|
|
auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
ASSERT_TRUE(H);
|
|
EXPECT_EQ(H->CallPassType->PassBy, Test.PassBy);
|
|
EXPECT_EQ(H->CallPassType->Converted, Test.Converted);
|
|
}
|
|
}
|
|
|
|
TEST(Hover, NoHover) {
|
|
llvm::StringRef Tests[] = {
|
|
"^int main() {}",
|
|
"void foo() {^}",
|
|
// FIXME: "decltype(auto)" should be a single hover
|
|
"decltype(au^to) x = 0;",
|
|
// FIXME: not supported yet
|
|
R"cpp(// Lambda auto parameter
|
|
auto lamb = [](a^uto){};
|
|
)cpp",
|
|
R"cpp(// non-named decls don't get hover. Don't crash!
|
|
^static_assert(1, "");
|
|
)cpp",
|
|
R"cpp(// non-evaluatable expr
|
|
template <typename T> void foo() {
|
|
(void)[[size^of]](T);
|
|
})cpp",
|
|
R"cpp(// should not crash on invalid semantic form of init-list-expr.
|
|
/*error-ok*/
|
|
struct Foo {
|
|
int xyz = 0;
|
|
};
|
|
class Bar {};
|
|
constexpr Foo s = ^{
|
|
.xyz = Bar(),
|
|
};
|
|
)cpp",
|
|
// literals
|
|
"auto x = t^rue;",
|
|
"auto x = ^(int){42};",
|
|
"auto x = ^42.;",
|
|
"auto x = ^42.0i;",
|
|
"auto x = ^42;",
|
|
"auto x = ^nullptr;",
|
|
};
|
|
|
|
for (const auto &Test : Tests) {
|
|
SCOPED_TRACE(Test);
|
|
|
|
Annotations T(Test);
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
TU.ExtraArgs.push_back("-std=c++17");
|
|
auto AST = TU.build();
|
|
auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
ASSERT_FALSE(H);
|
|
}
|
|
}
|
|
|
|
TEST(Hover, All) {
|
|
struct {
|
|
const char *const Code;
|
|
const std::function<void(HoverInfo &)> ExpectedBuilder;
|
|
} Cases[] = {
|
|
{"auto x = [['^A']]; // character literal",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "expression";
|
|
HI.Type = "char";
|
|
HI.Value = "65 (0x41)";
|
|
}},
|
|
{"auto s = ^[[\"Hello, world!\"]]; // string literal",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "string-literal";
|
|
HI.Size = 112;
|
|
HI.Type = "const char[14]";
|
|
}},
|
|
{
|
|
R"cpp(// Local variable
|
|
int main() {
|
|
int bonjour;
|
|
^[[bonjour]] = 2;
|
|
int test1 = bonjour;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "bonjour";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "main::";
|
|
HI.Type = "int";
|
|
HI.Definition = "int bonjour";
|
|
}},
|
|
{
|
|
R"cpp(// Local variable in method
|
|
struct s {
|
|
void method() {
|
|
int bonjour;
|
|
^[[bonjour]] = 2;
|
|
}
|
|
};
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "bonjour";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "s::method::";
|
|
HI.Type = "int";
|
|
HI.Definition = "int bonjour";
|
|
}},
|
|
{
|
|
R"cpp(// Struct
|
|
namespace ns1 {
|
|
struct MyClass {};
|
|
} // namespace ns1
|
|
int main() {
|
|
ns1::[[My^Class]]* Params;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "MyClass";
|
|
HI.Kind = index::SymbolKind::Struct;
|
|
HI.NamespaceScope = "ns1::";
|
|
HI.Definition = "struct MyClass {}";
|
|
}},
|
|
{
|
|
R"cpp(// Class
|
|
namespace ns1 {
|
|
class MyClass {};
|
|
} // namespace ns1
|
|
int main() {
|
|
ns1::[[My^Class]]* Params;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "MyClass";
|
|
HI.Kind = index::SymbolKind::Class;
|
|
HI.NamespaceScope = "ns1::";
|
|
HI.Definition = "class MyClass {}";
|
|
}},
|
|
{
|
|
R"cpp(// Union
|
|
namespace ns1 {
|
|
union MyUnion { int x; int y; };
|
|
} // namespace ns1
|
|
int main() {
|
|
ns1::[[My^Union]] Params;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "MyUnion";
|
|
HI.Kind = index::SymbolKind::Union;
|
|
HI.NamespaceScope = "ns1::";
|
|
HI.Definition = "union MyUnion {}";
|
|
}},
|
|
{
|
|
R"cpp(// Function definition via pointer
|
|
void foo(int) {}
|
|
int main() {
|
|
auto *X = &^[[foo]];
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.NamespaceScope = "";
|
|
HI.Type = "void (int)";
|
|
HI.Definition = "void foo(int)";
|
|
HI.Documentation = "Function definition via pointer";
|
|
HI.ReturnType = "void";
|
|
HI.Parameters = {
|
|
{{"int"}, std::nullopt, std::nullopt},
|
|
};
|
|
}},
|
|
{
|
|
R"cpp(// Function declaration via call
|
|
int foo(int);
|
|
int main() {
|
|
return ^[[foo]](42);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.NamespaceScope = "";
|
|
HI.Type = "int (int)";
|
|
HI.Definition = "int foo(int)";
|
|
HI.Documentation = "Function declaration via call";
|
|
HI.ReturnType = "int";
|
|
HI.Parameters = {
|
|
{{"int"}, std::nullopt, std::nullopt},
|
|
};
|
|
}},
|
|
{
|
|
R"cpp(// Field
|
|
struct Foo { int x; };
|
|
int main() {
|
|
Foo bar;
|
|
(void)bar.^[[x]];
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "x";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Type = "int";
|
|
HI.Definition = "int x";
|
|
}},
|
|
{
|
|
R"cpp(// Field with initialization
|
|
struct Foo { int x = 5; };
|
|
int main() {
|
|
Foo bar;
|
|
(void)bar.^[[x]];
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "x";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Type = "int";
|
|
HI.Definition = "int x = 5";
|
|
}},
|
|
{
|
|
R"cpp(// Static field
|
|
struct Foo { static int x; };
|
|
int main() {
|
|
(void)Foo::^[[x]];
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "x";
|
|
HI.Kind = index::SymbolKind::StaticProperty;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Type = "int";
|
|
HI.Definition = "static int x";
|
|
}},
|
|
{
|
|
R"cpp(// Field, member initializer
|
|
struct Foo {
|
|
int x;
|
|
Foo() : ^[[x]](0) {}
|
|
};
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "x";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Type = "int";
|
|
HI.Definition = "int x";
|
|
}},
|
|
{
|
|
R"cpp(// Field, GNU old-style field designator
|
|
struct Foo { int x; };
|
|
int main() {
|
|
Foo bar = { ^[[x]] : 1 };
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "x";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Type = "int";
|
|
HI.Definition = "int x";
|
|
// FIXME: Initializer for x is a DesignatedInitListExpr, hence it is
|
|
// of struct type and omitted.
|
|
}},
|
|
{
|
|
R"cpp(// Field, field designator
|
|
struct Foo { int x; int y; };
|
|
int main() {
|
|
Foo bar = { .^[[x]] = 2, .y = 2 };
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "x";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Type = "int";
|
|
HI.Definition = "int x";
|
|
}},
|
|
{
|
|
R"cpp(// Method call
|
|
struct Foo { int x(); };
|
|
int main() {
|
|
Foo bar;
|
|
bar.^[[x]]();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "x";
|
|
HI.Kind = index::SymbolKind::InstanceMethod;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Type = "int ()";
|
|
HI.Definition = "int x()";
|
|
HI.ReturnType = "int";
|
|
HI.Parameters = std::vector<HoverInfo::Param>{};
|
|
}},
|
|
{
|
|
R"cpp(// Static method call
|
|
struct Foo { static int x(); };
|
|
int main() {
|
|
Foo::^[[x]]();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "x";
|
|
HI.Kind = index::SymbolKind::StaticMethod;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Foo::";
|
|
HI.Type = "int ()";
|
|
HI.Definition = "static int x()";
|
|
HI.ReturnType = "int";
|
|
HI.Parameters = std::vector<HoverInfo::Param>{};
|
|
}},
|
|
{
|
|
R"cpp(// Typedef
|
|
typedef int Foo;
|
|
int main() {
|
|
^[[Foo]] bar;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "Foo";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "typedef int Foo";
|
|
HI.Type = "int";
|
|
HI.Documentation = "Typedef";
|
|
}},
|
|
{
|
|
R"cpp(// Typedef with embedded definition
|
|
typedef struct Bar {} Foo;
|
|
int main() {
|
|
^[[Foo]] bar;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "Foo";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "typedef struct Bar Foo";
|
|
HI.Type = "struct Bar";
|
|
HI.Documentation = "Typedef with embedded definition";
|
|
}},
|
|
{
|
|
R"cpp(// Namespace
|
|
namespace ns {
|
|
struct Foo { static void bar(); };
|
|
} // namespace ns
|
|
int main() { ^[[ns]]::Foo::bar(); }
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "ns";
|
|
HI.Kind = index::SymbolKind::Namespace;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "namespace ns {}";
|
|
}},
|
|
{
|
|
R"cpp(// Anonymous namespace
|
|
namespace ns {
|
|
namespace {
|
|
int foo;
|
|
} // anonymous namespace
|
|
} // namespace ns
|
|
int main() { ns::[[f^oo]]++; }
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "ns::";
|
|
HI.Type = "int";
|
|
HI.Definition = "int foo";
|
|
}},
|
|
{
|
|
R"cpp(// Function definition via using declaration
|
|
namespace ns {
|
|
void foo();
|
|
}
|
|
int main() {
|
|
using ns::foo;
|
|
^[[foo]]();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.NamespaceScope = "ns::";
|
|
HI.Type = "void ()";
|
|
HI.Definition = "void foo()";
|
|
HI.Documentation = "";
|
|
HI.ReturnType = "void";
|
|
HI.Parameters = std::vector<HoverInfo::Param>{};
|
|
}},
|
|
{
|
|
R"cpp( // using declaration and two possible function declarations
|
|
namespace ns { void foo(int); void foo(char); }
|
|
using ns::foo;
|
|
template <typename T> void bar() { [[f^oo]](T{}); }
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Using;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "using ns::foo";
|
|
}},
|
|
{
|
|
R"cpp(// Macro
|
|
#define MACRO 0
|
|
int main() { return ^[[MACRO]]; }
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "MACRO";
|
|
HI.Value = "0";
|
|
HI.Type = "int";
|
|
HI.Kind = index::SymbolKind::Macro;
|
|
HI.Definition = "#define MACRO 0\n\n"
|
|
"// Expands to\n"
|
|
"0";
|
|
}},
|
|
{
|
|
R"cpp(// Macro
|
|
#define MACRO 0
|
|
#define MACRO2 ^[[MACRO]]
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "MACRO";
|
|
HI.Kind = index::SymbolKind::Macro;
|
|
HI.Definition = "#define MACRO 0";
|
|
// NOTE MACRO doesn't have expansion since it technically isn't
|
|
// expanded here
|
|
}},
|
|
{
|
|
R"cpp(// Macro
|
|
#define MACRO {\
|
|
return 0;\
|
|
}
|
|
int main() ^[[MACRO]]
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "MACRO";
|
|
HI.Kind = index::SymbolKind::Macro;
|
|
HI.Definition =
|
|
R"cpp(#define MACRO \
|
|
{ return 0; }
|
|
|
|
// Expands to
|
|
{ return 0; })cpp";
|
|
}},
|
|
{
|
|
R"cpp(// Forward class declaration
|
|
class Foo;
|
|
class Foo {};
|
|
[[F^oo]]* foo();
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "Foo";
|
|
HI.Kind = index::SymbolKind::Class;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "class Foo {}";
|
|
HI.Documentation = "Forward class declaration";
|
|
}},
|
|
{
|
|
R"cpp(// Function declaration
|
|
void foo();
|
|
void g() { [[f^oo]](); }
|
|
void foo() {}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.NamespaceScope = "";
|
|
HI.Type = "void ()";
|
|
HI.Definition = "void foo()";
|
|
HI.Documentation = "Function declaration";
|
|
HI.ReturnType = "void";
|
|
HI.Parameters = std::vector<HoverInfo::Param>{};
|
|
}},
|
|
{
|
|
R"cpp(// Enum declaration
|
|
enum Hello {
|
|
ONE, TWO, THREE,
|
|
};
|
|
void foo() {
|
|
[[Hel^lo]] hello = ONE;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "Hello";
|
|
HI.Kind = index::SymbolKind::Enum;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "enum Hello {}";
|
|
HI.Documentation = "Enum declaration";
|
|
}},
|
|
{
|
|
R"cpp(// Enumerator
|
|
enum Hello {
|
|
ONE, TWO, THREE,
|
|
};
|
|
void foo() {
|
|
Hello hello = [[O^NE]];
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "ONE";
|
|
HI.Kind = index::SymbolKind::EnumConstant;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Hello::";
|
|
HI.Type = "enum Hello";
|
|
HI.Definition = "ONE";
|
|
HI.Value = "0";
|
|
}},
|
|
{
|
|
R"cpp(// C++20's using enum
|
|
enum class Hello {
|
|
ONE, TWO, THREE,
|
|
};
|
|
void foo() {
|
|
using enum Hello;
|
|
Hello hello = [[O^NE]];
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "ONE";
|
|
HI.Kind = index::SymbolKind::EnumConstant;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "Hello::";
|
|
HI.Type = "enum Hello";
|
|
HI.Definition = "ONE";
|
|
HI.Value = "0";
|
|
}},
|
|
{
|
|
R"cpp(// Enumerator in anonymous enum
|
|
enum {
|
|
ONE, TWO, THREE,
|
|
};
|
|
void foo() {
|
|
int hello = [[O^NE]];
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "ONE";
|
|
HI.Kind = index::SymbolKind::EnumConstant;
|
|
HI.NamespaceScope = "";
|
|
// FIXME: This should be `(anon enum)::`
|
|
HI.LocalScope = "";
|
|
HI.Type = "enum (unnamed)";
|
|
HI.Definition = "ONE";
|
|
HI.Value = "0";
|
|
}},
|
|
{
|
|
R"cpp(// Global variable
|
|
static int hey = 10;
|
|
void foo() {
|
|
[[he^y]]++;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "hey";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Type = "int";
|
|
HI.Definition = "static int hey = 10";
|
|
HI.Documentation = "Global variable";
|
|
// FIXME: Value shouldn't be set in this case
|
|
HI.Value = "10 (0xa)";
|
|
}},
|
|
{
|
|
R"cpp(// Global variable in namespace
|
|
namespace ns1 {
|
|
static long long hey = -36637162602497;
|
|
}
|
|
void foo() {
|
|
ns1::[[he^y]]++;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "hey";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "ns1::";
|
|
HI.Type = "long long";
|
|
HI.Definition = "static long long hey = -36637162602497";
|
|
HI.Value = "-36637162602497 (0xffffdeadbeefffff)"; // needs 64 bits
|
|
}},
|
|
{
|
|
R"cpp(// Field in anonymous struct
|
|
static struct {
|
|
int hello;
|
|
} s;
|
|
void foo() {
|
|
s.[[he^llo]]++;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "hello";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "(anonymous struct)::";
|
|
HI.Type = "int";
|
|
HI.Definition = "int hello";
|
|
}},
|
|
{
|
|
R"cpp(// Templated function
|
|
template <typename T>
|
|
T foo() {
|
|
return 17;
|
|
}
|
|
void g() { auto x = [[f^oo]]<int>(); }
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.NamespaceScope = "";
|
|
HI.Type = "int ()";
|
|
HI.Definition = "template <> int foo<int>()";
|
|
HI.Documentation = "Templated function";
|
|
HI.ReturnType = "int";
|
|
HI.Parameters = std::vector<HoverInfo::Param>{};
|
|
// FIXME: We should populate template parameters with arguments in
|
|
// case of instantiations.
|
|
}},
|
|
{
|
|
R"cpp(// Anonymous union
|
|
struct outer {
|
|
union {
|
|
int abc, def;
|
|
} v;
|
|
};
|
|
void g() { struct outer o; o.v.[[d^ef]]++; }
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "def";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "outer::(anonymous union)::";
|
|
HI.Type = "int";
|
|
HI.Definition = "int def";
|
|
}},
|
|
{
|
|
R"cpp(// documentation from index
|
|
int nextSymbolIsAForwardDeclFromIndexWithNoLocalDocs;
|
|
void indexSymbol();
|
|
void g() { [[ind^exSymbol]](); }
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "indexSymbol";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.NamespaceScope = "";
|
|
HI.Type = "void ()";
|
|
HI.Definition = "void indexSymbol()";
|
|
HI.ReturnType = "void";
|
|
HI.Parameters = std::vector<HoverInfo::Param>{};
|
|
HI.Documentation = "comment from index";
|
|
}},
|
|
{
|
|
R"cpp(// Simple initialization with auto
|
|
void foo() {
|
|
^[[auto]] i = 1;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int";
|
|
}},
|
|
{
|
|
R"cpp(// Simple initialization with const auto
|
|
void foo() {
|
|
const ^[[auto]] i = 1;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int";
|
|
}},
|
|
{
|
|
R"cpp(// Simple initialization with const auto&
|
|
void foo() {
|
|
const ^[[auto]]& i = 1;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int";
|
|
}},
|
|
{
|
|
R"cpp(// Simple initialization with auto&
|
|
void foo() {
|
|
int x;
|
|
^[[auto]]& i = x;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int";
|
|
}},
|
|
{
|
|
R"cpp(// Simple initialization with auto*
|
|
void foo() {
|
|
int a = 1;
|
|
^[[auto]]* i = &a;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int";
|
|
}},
|
|
{
|
|
R"cpp(// Simple initialization with auto from pointer
|
|
void foo() {
|
|
int a = 1;
|
|
^[[auto]] i = &a;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int *";
|
|
}},
|
|
{
|
|
R"cpp(// Auto with initializer list.
|
|
namespace std
|
|
{
|
|
template<class _E>
|
|
class initializer_list {};
|
|
}
|
|
void foo() {
|
|
^[[auto]] i = {1,2};
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "std::initializer_list<int>";
|
|
}},
|
|
{
|
|
R"cpp(// User defined conversion to auto
|
|
struct Bar {
|
|
operator ^[[auto]]() const { return 10; }
|
|
};
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int";
|
|
}},
|
|
{
|
|
R"cpp(// Simple initialization with decltype(auto)
|
|
void foo() {
|
|
^[[decltype]](auto) i = 1;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int";
|
|
}},
|
|
{
|
|
R"cpp(// Simple initialization with const decltype(auto)
|
|
void foo() {
|
|
const int j = 0;
|
|
^[[decltype]](auto) i = j;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "const int";
|
|
}},
|
|
{
|
|
R"cpp(// Simple initialization with const& decltype(auto)
|
|
void foo() {
|
|
int k = 0;
|
|
const int& j = k;
|
|
^[[decltype]](auto) i = j;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "const int &";
|
|
}},
|
|
{
|
|
R"cpp(// Simple initialization with & decltype(auto)
|
|
void foo() {
|
|
int k = 0;
|
|
int& j = k;
|
|
^[[decltype]](auto) i = j;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int &";
|
|
}},
|
|
{
|
|
R"cpp(// simple trailing return type
|
|
^[[auto]] main() -> int {
|
|
return 0;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int";
|
|
}},
|
|
{
|
|
R"cpp(// auto function return with trailing type
|
|
struct Bar {};
|
|
^[[auto]] test() -> decltype(Bar()) {
|
|
return Bar();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "Bar";
|
|
HI.Documentation = "auto function return with trailing type";
|
|
}},
|
|
{
|
|
R"cpp(// trailing return type
|
|
struct Bar {};
|
|
auto test() -> ^[[decltype]](Bar()) {
|
|
return Bar();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "Bar";
|
|
HI.Documentation = "trailing return type";
|
|
}},
|
|
{
|
|
R"cpp(// auto in function return
|
|
struct Bar {};
|
|
^[[auto]] test() {
|
|
return Bar();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "Bar";
|
|
HI.Documentation = "auto in function return";
|
|
}},
|
|
{
|
|
R"cpp(// auto& in function return
|
|
struct Bar {};
|
|
^[[auto]]& test() {
|
|
static Bar x;
|
|
return x;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "Bar";
|
|
HI.Documentation = "auto& in function return";
|
|
}},
|
|
{
|
|
R"cpp(// auto* in function return
|
|
struct Bar {};
|
|
^[[auto]]* test() {
|
|
Bar* bar;
|
|
return bar;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "Bar";
|
|
HI.Documentation = "auto* in function return";
|
|
}},
|
|
{
|
|
R"cpp(// const auto& in function return
|
|
struct Bar {};
|
|
const ^[[auto]]& test() {
|
|
static Bar x;
|
|
return x;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "Bar";
|
|
HI.Documentation = "const auto& in function return";
|
|
}},
|
|
{
|
|
R"cpp(// decltype(auto) in function return
|
|
struct Bar {};
|
|
^[[decltype]](auto) test() {
|
|
return Bar();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "Bar";
|
|
HI.Documentation = "decltype(auto) in function return";
|
|
}},
|
|
{
|
|
R"cpp(// decltype(auto) reference in function return
|
|
^[[decltype]](auto) test() {
|
|
static int a;
|
|
return (a);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int &";
|
|
}},
|
|
{
|
|
R"cpp(// decltype lvalue reference
|
|
void foo() {
|
|
int I = 0;
|
|
^[[decltype]](I) J = I;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int";
|
|
}},
|
|
{
|
|
R"cpp(// decltype lvalue reference
|
|
void foo() {
|
|
int I= 0;
|
|
int &K = I;
|
|
^[[decltype]](K) J = I;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int &";
|
|
}},
|
|
{
|
|
R"cpp(// decltype lvalue reference parenthesis
|
|
void foo() {
|
|
int I = 0;
|
|
^[[decltype]]((I)) J = I;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int &";
|
|
}},
|
|
{
|
|
R"cpp(// decltype rvalue reference
|
|
void foo() {
|
|
int I = 0;
|
|
^[[decltype]](static_cast<int&&>(I)) J = static_cast<int&&>(I);
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int &&";
|
|
}},
|
|
{
|
|
R"cpp(// decltype rvalue reference function call
|
|
int && bar();
|
|
void foo() {
|
|
int I = 0;
|
|
^[[decltype]](bar()) J = bar();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int &&";
|
|
}},
|
|
{
|
|
R"cpp(// decltype of function with trailing return type.
|
|
struct Bar {};
|
|
auto test() -> decltype(Bar()) {
|
|
return Bar();
|
|
}
|
|
void foo() {
|
|
^[[decltype]](test()) i = test();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "Bar";
|
|
HI.Documentation =
|
|
"decltype of function with trailing return type.";
|
|
}},
|
|
{
|
|
R"cpp(// decltype of var with decltype.
|
|
void foo() {
|
|
int I = 0;
|
|
decltype(I) J = I;
|
|
^[[decltype]](J) K = J;
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int";
|
|
}},
|
|
{
|
|
R"cpp(// decltype of dependent type
|
|
template <typename T>
|
|
struct X {
|
|
using Y = ^[[decltype]](T::Z);
|
|
};
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "<dependent type>";
|
|
}},
|
|
{
|
|
R"cpp(// More complicated structured types.
|
|
int bar();
|
|
^[[auto]] (*foo)() = bar;
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int";
|
|
}},
|
|
{
|
|
R"cpp(// Should not crash when evaluating the initializer.
|
|
struct Test {};
|
|
void test() { Test && [[te^st]] = {}; }
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "test";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "test::";
|
|
HI.Type = "Test &&";
|
|
HI.Definition = "Test &&test = {}";
|
|
}},
|
|
{
|
|
R"cpp(// auto on alias
|
|
typedef int int_type;
|
|
^[[auto]] x = int_type();
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "int_type // aka: int";
|
|
}},
|
|
{
|
|
R"cpp(// auto on alias
|
|
struct cls {};
|
|
typedef cls cls_type;
|
|
^[[auto]] y = cls_type();
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "cls_type // aka: cls";
|
|
HI.Documentation = "auto on alias";
|
|
}},
|
|
{
|
|
R"cpp(// auto on alias
|
|
template <class>
|
|
struct templ {};
|
|
^[[auto]] z = templ<int>();
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "templ<int>";
|
|
HI.Documentation = "auto on alias";
|
|
}},
|
|
{
|
|
R"cpp(// Undeduced auto declaration
|
|
template<typename T>
|
|
void foo() {
|
|
^[[auto]] x = T();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "/* not deduced */";
|
|
}},
|
|
{
|
|
R"cpp(// Undeduced auto return type
|
|
template<typename T>
|
|
^[[auto]] foo() {
|
|
return T();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "/* not deduced */";
|
|
}},
|
|
{
|
|
R"cpp(// Template auto parameter
|
|
template<[[a^uto]] T>
|
|
void func() {
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
// FIXME: not sure this is what we want, but this
|
|
// is what we currently get with getDeducedType
|
|
HI.Name = "auto";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "/* not deduced */";
|
|
}},
|
|
{
|
|
R"cpp(// Undeduced decltype(auto) return type
|
|
template<typename T>
|
|
^[[decltype]](auto) foo() {
|
|
return T();
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "decltype";
|
|
HI.Kind = index::SymbolKind::TypeAlias;
|
|
HI.Definition = "/* not deduced */";
|
|
}},
|
|
{
|
|
R"cpp(// should not crash.
|
|
template <class T> struct cls {
|
|
int method();
|
|
};
|
|
|
|
auto test = cls<int>().[[m^ethod]]();
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Definition = "int method()";
|
|
HI.Kind = index::SymbolKind::InstanceMethod;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "cls<int>::";
|
|
HI.Name = "method";
|
|
HI.Parameters.emplace();
|
|
HI.ReturnType = "int";
|
|
HI.Type = "int ()";
|
|
}},
|
|
{
|
|
R"cpp(// type of nested templates.
|
|
template <class T> struct cls {};
|
|
cls<cls<cls<int>>> [[fo^o]];
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Definition = "cls<cls<cls<int>>> foo";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "foo";
|
|
HI.Type = "cls<cls<cls<int>>>";
|
|
}},
|
|
{
|
|
R"cpp(// type of nested templates.
|
|
template <class T> struct cls {};
|
|
[[cl^s]]<cls<cls<int>>> foo;
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Definition = "template <> struct cls<cls<cls<int>>> {}";
|
|
HI.Kind = index::SymbolKind::Struct;
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "cls<cls<cls<int>>>";
|
|
HI.Documentation = "type of nested templates.";
|
|
}},
|
|
{
|
|
R"cpp(// type with decltype
|
|
int a;
|
|
decltype(a) [[b^]] = a;)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Definition = "decltype(a) b = a";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "b";
|
|
HI.Type = "int";
|
|
}},
|
|
{
|
|
R"cpp(// type with decltype
|
|
int a;
|
|
decltype(a) c;
|
|
decltype(c) [[b^]] = a;)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Definition = "decltype(c) b = a";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "b";
|
|
HI.Type = "int";
|
|
}},
|
|
{
|
|
R"cpp(// type with decltype
|
|
int a;
|
|
const decltype(a) [[b^]] = a;)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Definition = "const decltype(a) b = a";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "b";
|
|
HI.Type = "int";
|
|
}},
|
|
{
|
|
R"cpp(// type with decltype
|
|
int a;
|
|
auto [[f^oo]](decltype(a) x) -> decltype(a) { return 0; })cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Definition = "auto foo(decltype(a) x) -> decltype(a)";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.NamespaceScope = "";
|
|
HI.Name = "foo";
|
|
// FIXME: Handle composite types with decltype with a printing
|
|
// policy.
|
|
HI.Type = {"auto (decltype(a)) -> decltype(a)",
|
|
"auto (int) -> int"};
|
|
HI.ReturnType = "int";
|
|
HI.Parameters = {{{"int"}, std::string("x"), std::nullopt}};
|
|
}},
|
|
{
|
|
R"cpp(// sizeof expr
|
|
void foo() {
|
|
(void)[[size^of]](char);
|
|
})cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "expression";
|
|
HI.Type = "unsigned long";
|
|
HI.Value = "1";
|
|
}},
|
|
{
|
|
R"cpp(// alignof expr
|
|
void foo() {
|
|
(void)[[align^of]](char);
|
|
})cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "expression";
|
|
HI.Type = "unsigned long";
|
|
HI.Value = "1";
|
|
}},
|
|
{
|
|
R"cpp(
|
|
template <typename T = int>
|
|
void foo(const T& = T()) {
|
|
[[f^oo]]<>(3);
|
|
})cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "foo";
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.Type = "void (const int &)";
|
|
HI.ReturnType = "void";
|
|
HI.Parameters = {
|
|
{{"const int &"}, std::nullopt, std::string("T()")}};
|
|
HI.Definition = "template <> void foo<int>(const int &)";
|
|
HI.NamespaceScope = "";
|
|
}},
|
|
{
|
|
R"cpp(// should not crash
|
|
@interface ObjC {
|
|
char [[da^ta]];
|
|
}@end
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "data";
|
|
HI.Type = "char";
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.LocalScope = "ObjC::";
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "char data";
|
|
}},
|
|
{
|
|
R"cpp(
|
|
@interface MYObject
|
|
@end
|
|
@interface Interface
|
|
@property(retain) [[MYOb^ject]] *x;
|
|
@end
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "MYObject";
|
|
HI.Kind = index::SymbolKind::Class;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "@interface MYObject\n@end";
|
|
}},
|
|
{
|
|
R"cpp(
|
|
@interface MYObject
|
|
@end
|
|
@interface Interface
|
|
- (void)doWith:([[MYOb^ject]] *)object;
|
|
@end
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "MYObject";
|
|
HI.Kind = index::SymbolKind::Class;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "@interface MYObject\n@end";
|
|
}},
|
|
{
|
|
R"cpp(// this expr
|
|
// comment
|
|
namespace ns {
|
|
class Foo {
|
|
Foo* bar() {
|
|
return [[t^his]];
|
|
}
|
|
};
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "this";
|
|
HI.Definition = "ns::Foo *";
|
|
}},
|
|
{
|
|
R"cpp(// this expr for template class
|
|
namespace ns {
|
|
template <typename T>
|
|
class Foo {
|
|
Foo* bar() const {
|
|
return [[t^his]];
|
|
}
|
|
};
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "this";
|
|
HI.Definition = "const Foo<T> *";
|
|
}},
|
|
{
|
|
R"cpp(// this expr for specialization class
|
|
namespace ns {
|
|
template <typename T> class Foo {};
|
|
template <>
|
|
struct Foo<int> {
|
|
Foo* bar() {
|
|
return [[thi^s]];
|
|
}
|
|
};
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "this";
|
|
HI.Definition = "Foo<int> *";
|
|
}},
|
|
{
|
|
R"cpp(// this expr for partial specialization struct
|
|
namespace ns {
|
|
template <typename T, typename F> struct Foo {};
|
|
template <typename F>
|
|
struct Foo<int, F> {
|
|
Foo* bar() const {
|
|
return [[thi^s]];
|
|
}
|
|
};
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "this";
|
|
HI.Definition = "const Foo<int, F> *";
|
|
}},
|
|
{
|
|
R"cpp(
|
|
@interface MYObject
|
|
@end
|
|
@interface MYObject (Private)
|
|
@property(nonatomic, assign) int privateField;
|
|
@end
|
|
|
|
int someFunction() {
|
|
MYObject *obj = [MYObject sharedInstance];
|
|
return obj.[[private^Field]];
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "privateField";
|
|
HI.Kind = index::SymbolKind::InstanceProperty;
|
|
HI.LocalScope = "MYObject(Private)::";
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "@property(nonatomic, assign, unsafe_unretained, "
|
|
"readwrite) int privateField;";
|
|
}},
|
|
{
|
|
R"cpp(
|
|
@protocol MYProtocol
|
|
@property(nonatomic, assign) int prop1;
|
|
@end
|
|
|
|
int someFunction() {
|
|
id<MYProtocol> obj = 0;
|
|
return obj.[[pro^p1]];
|
|
}
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "prop1";
|
|
HI.Kind = index::SymbolKind::InstanceProperty;
|
|
HI.LocalScope = "MYProtocol::";
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "@property(nonatomic, assign, unsafe_unretained, "
|
|
"readwrite) int prop1;";
|
|
}},
|
|
{
|
|
R"cpp(
|
|
@protocol MYProtocol
|
|
@end
|
|
@interface MYObject
|
|
@end
|
|
|
|
@interface MYObject (Ext) <[[MYProt^ocol]]>
|
|
@end
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "MYProtocol";
|
|
HI.Kind = index::SymbolKind::Protocol;
|
|
HI.NamespaceScope = "";
|
|
HI.Definition = "@protocol MYProtocol\n@end";
|
|
}},
|
|
{R"objc(
|
|
@interface Foo
|
|
@end
|
|
|
|
@implementation Foo(Private)
|
|
+ (int)somePrivateMethod {
|
|
int [[res^ult]] = 2;
|
|
return result;
|
|
}
|
|
@end
|
|
)objc",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "result";
|
|
HI.Definition = "int result = 2";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Type = "int";
|
|
HI.LocalScope = "+[Foo(Private) somePrivateMethod]::";
|
|
HI.NamespaceScope = "";
|
|
HI.Value = "2";
|
|
}},
|
|
{R"objc(
|
|
@interface Foo
|
|
@end
|
|
|
|
@implementation Foo
|
|
- (int)variadicArgMethod:(id)first, ... {
|
|
int [[res^ult]] = 0;
|
|
return result;
|
|
}
|
|
@end
|
|
)objc",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "result";
|
|
HI.Definition = "int result = 0";
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Type = "int";
|
|
HI.LocalScope = "-[Foo variadicArgMethod:, ...]::";
|
|
HI.NamespaceScope = "";
|
|
HI.Value = "0";
|
|
}},
|
|
{R"cpp(
|
|
void foo(int * __attribute__(([[non^null]], noescape)) );
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "nonnull";
|
|
HI.Kind = index::SymbolKind::Unknown; // FIXME: no suitable value
|
|
HI.Definition = "__attribute__((nonnull))";
|
|
HI.Documentation = Attr::getDocumentation(attr::NonNull).str();
|
|
}},
|
|
{
|
|
R"cpp(
|
|
namespace std {
|
|
struct strong_ordering {
|
|
int n;
|
|
constexpr operator int() const { return n; }
|
|
static const strong_ordering equal, greater, less;
|
|
};
|
|
constexpr strong_ordering strong_ordering::equal = {0};
|
|
constexpr strong_ordering strong_ordering::greater = {1};
|
|
constexpr strong_ordering strong_ordering::less = {-1};
|
|
}
|
|
|
|
struct Foo
|
|
{
|
|
int x;
|
|
// Foo spaceship
|
|
auto operator<=>(const Foo&) const = default;
|
|
};
|
|
|
|
bool x = Foo(1) [[!^=]] Foo(2);
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.Type = "bool (const Foo &) const noexcept";
|
|
HI.Value = "true";
|
|
HI.Name = "operator==";
|
|
HI.Parameters = {{{"const Foo &"}, std::nullopt, std::nullopt}};
|
|
HI.ReturnType = "bool";
|
|
HI.Kind = index::SymbolKind::InstanceMethod;
|
|
HI.LocalScope = "Foo::";
|
|
HI.NamespaceScope = "";
|
|
HI.Definition =
|
|
"bool operator==(const Foo &) const noexcept = default";
|
|
HI.Documentation = "Foo spaceship";
|
|
}},
|
|
};
|
|
|
|
// Create a tiny index, so tests above can verify documentation is fetched.
|
|
Symbol IndexSym = func("indexSymbol");
|
|
IndexSym.Documentation = "comment from index";
|
|
SymbolSlab::Builder Symbols;
|
|
Symbols.insert(IndexSym);
|
|
auto Index =
|
|
MemIndex::build(std::move(Symbols).build(), RefSlab(), RelationSlab());
|
|
|
|
for (const auto &Case : Cases) {
|
|
SCOPED_TRACE(Case.Code);
|
|
|
|
Annotations T(Case.Code);
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
TU.ExtraArgs.push_back("-std=c++20");
|
|
TU.ExtraArgs.push_back("-xobjective-c++");
|
|
|
|
TU.ExtraArgs.push_back("-Wno-gnu-designator");
|
|
// Types might be different depending on the target triplet, we chose a
|
|
// fixed one to make sure tests passes on different platform.
|
|
TU.ExtraArgs.push_back("--target=x86_64-pc-linux-gnu");
|
|
auto AST = TU.build();
|
|
Config Cfg;
|
|
Cfg.Hover.ShowAKA = true;
|
|
WithContextValue WithCfg(Config::Key, std::move(Cfg));
|
|
auto H = getHover(AST, T.point(), format::getLLVMStyle(), Index.get());
|
|
ASSERT_TRUE(H);
|
|
HoverInfo Expected;
|
|
Expected.SymRange = T.range();
|
|
Case.ExpectedBuilder(Expected);
|
|
|
|
SCOPED_TRACE(H->present().asPlainText());
|
|
EXPECT_EQ(H->NamespaceScope, Expected.NamespaceScope);
|
|
EXPECT_EQ(H->LocalScope, Expected.LocalScope);
|
|
EXPECT_EQ(H->Name, Expected.Name);
|
|
EXPECT_EQ(H->Kind, Expected.Kind);
|
|
EXPECT_EQ(H->Documentation, Expected.Documentation);
|
|
EXPECT_EQ(H->Definition, Expected.Definition);
|
|
EXPECT_EQ(H->Type, Expected.Type);
|
|
EXPECT_EQ(H->ReturnType, Expected.ReturnType);
|
|
EXPECT_EQ(H->Parameters, Expected.Parameters);
|
|
EXPECT_EQ(H->TemplateParameters, Expected.TemplateParameters);
|
|
EXPECT_EQ(H->SymRange, Expected.SymRange);
|
|
EXPECT_EQ(H->Value, Expected.Value);
|
|
}
|
|
}
|
|
|
|
TEST(Hover, Providers) {
|
|
struct {
|
|
const char *Code;
|
|
const std::function<void(HoverInfo &)> ExpectedBuilder;
|
|
} Cases[] = {{R"cpp(
|
|
struct Foo {};
|
|
Foo F = Fo^o{};
|
|
)cpp",
|
|
[](HoverInfo &HI) { HI.Provider = ""; }},
|
|
{R"cpp(
|
|
#include "foo.h"
|
|
Foo F = Fo^o{};
|
|
)cpp",
|
|
[](HoverInfo &HI) { HI.Provider = "\"foo.h\""; }},
|
|
{R"cpp(
|
|
#include "all.h"
|
|
Foo F = Fo^o{};
|
|
)cpp",
|
|
[](HoverInfo &HI) { HI.Provider = "\"foo.h\""; }},
|
|
{R"cpp(
|
|
#define FOO 5
|
|
int F = ^FOO;
|
|
)cpp",
|
|
[](HoverInfo &HI) { HI.Provider = ""; }},
|
|
{R"cpp(
|
|
#include "foo.h"
|
|
int F = ^FOO;
|
|
)cpp",
|
|
[](HoverInfo &HI) { HI.Provider = "\"foo.h\""; }},
|
|
{R"cpp(
|
|
#include "all.h"
|
|
int F = ^FOO;
|
|
)cpp",
|
|
[](HoverInfo &HI) { HI.Provider = "\"foo.h\""; }},
|
|
{R"cpp(
|
|
#include "foo.h"
|
|
Foo A;
|
|
Foo B;
|
|
Foo C = A ^+ B;
|
|
)cpp",
|
|
[](HoverInfo &HI) { HI.Provider = "\"foo.h\""; }},
|
|
// Hover selects the underlying decl of the using decl
|
|
{R"cpp(
|
|
#include "foo.h"
|
|
namespace ns {
|
|
using ::Foo;
|
|
}
|
|
ns::F^oo d;
|
|
)cpp",
|
|
[](HoverInfo &HI) { HI.Provider = "\"foo.h\""; }},
|
|
{R"cpp(
|
|
namespace foo {};
|
|
using namespace fo^o;
|
|
)cpp",
|
|
[](HoverInfo &HI) { HI.Provider = ""; }},
|
|
};
|
|
|
|
for (const auto &Case : Cases) {
|
|
Annotations Code{Case.Code};
|
|
SCOPED_TRACE(Code.code());
|
|
|
|
TestTU TU;
|
|
TU.Filename = "foo.cpp";
|
|
TU.Code = Code.code();
|
|
TU.AdditionalFiles["foo.h"] = guard(R"cpp(
|
|
#define FOO 1
|
|
class Foo {};
|
|
Foo& operator+(const Foo, const Foo);
|
|
)cpp");
|
|
TU.AdditionalFiles["all.h"] = guard("#include \"foo.h\"");
|
|
|
|
auto AST = TU.build();
|
|
auto H = getHover(AST, Code.point(), format::getLLVMStyle(), nullptr);
|
|
ASSERT_TRUE(H);
|
|
HoverInfo Expected;
|
|
Case.ExpectedBuilder(Expected);
|
|
SCOPED_TRACE(H->present().asMarkdown());
|
|
EXPECT_EQ(H->Provider, Expected.Provider);
|
|
}
|
|
}
|
|
|
|
TEST(Hover, ParseProviderInfo) {
|
|
HoverInfo HIFoo;
|
|
HIFoo.Name = "foo";
|
|
HIFoo.Provider = "\"foo.h\"";
|
|
|
|
HoverInfo HIFooBar;
|
|
HIFooBar.Name = "foo";
|
|
HIFooBar.Provider = "<bar.h>";
|
|
struct Case {
|
|
HoverInfo HI;
|
|
llvm::StringRef ExpectedMarkdown;
|
|
} Cases[] = {{HIFoo, "### `foo` \nprovided by `\"foo.h\"`"},
|
|
{HIFooBar, "### `foo` \nprovided by `<bar.h>`"}};
|
|
|
|
for (const auto &Case : Cases)
|
|
EXPECT_EQ(Case.HI.present().asMarkdown(), Case.ExpectedMarkdown);
|
|
}
|
|
|
|
TEST(Hover, UsedSymbols) {
|
|
struct {
|
|
const char *Code;
|
|
const std::function<void(HoverInfo &)> ExpectedBuilder;
|
|
} Cases[] = {{R"cpp(
|
|
#include ^"bar.h"
|
|
int fstBar = bar1();
|
|
int another= bar1(0);
|
|
int sndBar = bar2();
|
|
Bar bar;
|
|
int macroBar = BAR;
|
|
)cpp",
|
|
[](HoverInfo &HI) {
|
|
HI.UsedSymbolNames = {"BAR", "Bar", "bar1", "bar2"};
|
|
}},
|
|
{R"cpp(
|
|
#in^clude <vector>
|
|
std::vector<int> vec;
|
|
)cpp",
|
|
[](HoverInfo &HI) { HI.UsedSymbolNames = {"vector"}; }}};
|
|
for (const auto &Case : Cases) {
|
|
Annotations Code{Case.Code};
|
|
SCOPED_TRACE(Code.code());
|
|
|
|
TestTU TU;
|
|
TU.Filename = "foo.cpp";
|
|
TU.Code = Code.code();
|
|
TU.AdditionalFiles["bar.h"] = guard(R"cpp(
|
|
#define BAR 5
|
|
int bar1();
|
|
int bar2();
|
|
int bar1(double);
|
|
class Bar {};
|
|
)cpp");
|
|
TU.AdditionalFiles["system/vector"] = guard(R"cpp(
|
|
namespace std {
|
|
template<typename>
|
|
class vector{};
|
|
}
|
|
)cpp");
|
|
TU.ExtraArgs.push_back("-isystem" + testPath("system"));
|
|
|
|
auto AST = TU.build();
|
|
auto H = getHover(AST, Code.point(), format::getLLVMStyle(), nullptr);
|
|
ASSERT_TRUE(H);
|
|
HoverInfo Expected;
|
|
Case.ExpectedBuilder(Expected);
|
|
SCOPED_TRACE(H->present().asMarkdown());
|
|
EXPECT_EQ(H->UsedSymbolNames, Expected.UsedSymbolNames);
|
|
}
|
|
}
|
|
|
|
TEST(Hover, DocsFromIndex) {
|
|
Annotations T(R"cpp(
|
|
template <typename T> class X {};
|
|
void foo() {
|
|
auto t = X<int>();
|
|
X^<int> w;
|
|
(void)w;
|
|
})cpp");
|
|
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
auto AST = TU.build();
|
|
Symbol IndexSym;
|
|
IndexSym.ID = getSymbolID(&findDecl(AST, "X"));
|
|
IndexSym.Documentation = "comment from index";
|
|
SymbolSlab::Builder Symbols;
|
|
Symbols.insert(IndexSym);
|
|
auto Index =
|
|
MemIndex::build(std::move(Symbols).build(), RefSlab(), RelationSlab());
|
|
|
|
for (const auto &P : T.points()) {
|
|
auto H = getHover(AST, P, format::getLLVMStyle(), Index.get());
|
|
ASSERT_TRUE(H);
|
|
EXPECT_EQ(H->Documentation, IndexSym.Documentation);
|
|
}
|
|
}
|
|
|
|
TEST(Hover, DocsFromAST) {
|
|
Annotations T(R"cpp(
|
|
// doc
|
|
template <typename T> class X {};
|
|
// doc
|
|
template <typename T> void bar() {}
|
|
// doc
|
|
template <typename T> T baz;
|
|
void foo() {
|
|
au^to t = X<int>();
|
|
X^<int>();
|
|
b^ar<int>();
|
|
au^to T = ba^z<X<int>>;
|
|
ba^z<int> = 0;
|
|
})cpp");
|
|
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
auto AST = TU.build();
|
|
for (const auto &P : T.points()) {
|
|
auto H = getHover(AST, P, format::getLLVMStyle(), nullptr);
|
|
ASSERT_TRUE(H);
|
|
EXPECT_EQ(H->Documentation, "doc");
|
|
}
|
|
}
|
|
|
|
TEST(Hover, NoCrash) {
|
|
Annotations T(R"cpp(
|
|
/* error-ok */
|
|
template<typename T> T foo(T);
|
|
|
|
// Setter variable heuristic might fail if the callexpr is broken.
|
|
struct X { int Y; void [[^setY]](float) { Y = foo(undefined); } };)cpp");
|
|
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
auto AST = TU.build();
|
|
for (const auto &P : T.points())
|
|
getHover(AST, P, format::getLLVMStyle(), nullptr);
|
|
}
|
|
|
|
TEST(Hover, NoCrashAPInt64) {
|
|
Annotations T(R"cpp(
|
|
constexpr unsigned long value = -1; // wrap around
|
|
void foo() { va^lue; }
|
|
)cpp");
|
|
auto AST = TestTU::withCode(T.code()).build();
|
|
getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
}
|
|
|
|
TEST(Hover, DocsFromMostSpecial) {
|
|
Annotations T(R"cpp(
|
|
// doc1
|
|
template <typename T> class $doc1^X {};
|
|
// doc2
|
|
template <> class $doc2^X<int> {};
|
|
// doc3
|
|
template <typename T> class $doc3^X<T*> {};
|
|
void foo() {
|
|
X$doc1^<char>();
|
|
X$doc2^<int>();
|
|
X$doc3^<int*>();
|
|
})cpp");
|
|
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
auto AST = TU.build();
|
|
for (const auto *Comment : {"doc1", "doc2", "doc3"}) {
|
|
for (const auto &P : T.points(Comment)) {
|
|
auto H = getHover(AST, P, format::getLLVMStyle(), nullptr);
|
|
ASSERT_TRUE(H);
|
|
EXPECT_EQ(H->Documentation, Comment);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(Hover, Present) {
|
|
struct {
|
|
const std::function<void(HoverInfo &)> Builder;
|
|
llvm::StringRef ExpectedRender;
|
|
} Cases[] = {
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Unknown;
|
|
HI.Name = "X";
|
|
},
|
|
R"(X)",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::NamespaceAlias;
|
|
HI.Name = "foo";
|
|
},
|
|
R"(namespace-alias foo)",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Class;
|
|
HI.Size = 80;
|
|
HI.TemplateParameters = {
|
|
{{"typename"}, std::string("T"), std::nullopt},
|
|
{{"typename"}, std::string("C"), std::string("bool")},
|
|
};
|
|
HI.Documentation = "documentation";
|
|
HI.Definition =
|
|
"template <typename T, typename C = bool> class Foo {}";
|
|
HI.Name = "foo";
|
|
HI.NamespaceScope.emplace();
|
|
},
|
|
R"(class foo
|
|
|
|
Size: 10 bytes
|
|
documentation
|
|
|
|
template <typename T, typename C = bool> class Foo {})",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Function;
|
|
HI.Name = "foo";
|
|
HI.Type = {"type", "c_type"};
|
|
HI.ReturnType = {"ret_type", "can_ret_type"};
|
|
HI.Parameters.emplace();
|
|
HoverInfo::Param P;
|
|
HI.Parameters->push_back(P);
|
|
P.Type = {"type", "can_type"};
|
|
HI.Parameters->push_back(P);
|
|
P.Name = "foo";
|
|
HI.Parameters->push_back(P);
|
|
P.Default = "default";
|
|
HI.Parameters->push_back(P);
|
|
HI.NamespaceScope = "ns::";
|
|
HI.Definition = "ret_type foo(params) {}";
|
|
},
|
|
"function foo\n"
|
|
"\n"
|
|
"→ ret_type (aka can_ret_type)\n"
|
|
"Parameters:\n"
|
|
"- \n"
|
|
"- type (aka can_type)\n"
|
|
"- type foo (aka can_type)\n"
|
|
"- type foo = default (aka can_type)\n"
|
|
"\n"
|
|
"// In namespace ns\n"
|
|
"ret_type foo(params) {}",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.LocalScope = "test::Bar::";
|
|
HI.Value = "value";
|
|
HI.Name = "foo";
|
|
HI.Type = {"type", "can_type"};
|
|
HI.Definition = "def";
|
|
HI.Size = 32;
|
|
HI.Offset = 96;
|
|
HI.Padding = 32;
|
|
},
|
|
R"(field foo
|
|
|
|
Type: type (aka can_type)
|
|
Value = value
|
|
Offset: 12 bytes
|
|
Size: 4 bytes (+4 bytes padding)
|
|
|
|
// In test::Bar
|
|
def)",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.LocalScope = "test::Bar::";
|
|
HI.Value = "value";
|
|
HI.Name = "foo";
|
|
HI.Type = {"type", "can_type"};
|
|
HI.Definition = "def";
|
|
HI.Size = 25;
|
|
HI.Offset = 35;
|
|
HI.Padding = 4;
|
|
},
|
|
R"(field foo
|
|
|
|
Type: type (aka can_type)
|
|
Value = value
|
|
Offset: 4 bytes and 3 bits
|
|
Size: 25 bits (+4 bits padding)
|
|
|
|
// In test::Bar
|
|
def)",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Field;
|
|
HI.AccessSpecifier = "public";
|
|
HI.Name = "foo";
|
|
HI.LocalScope = "test::Bar::";
|
|
HI.Definition = "def";
|
|
},
|
|
R"(field foo
|
|
|
|
// In test::Bar
|
|
public: def)",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Definition = "size_t method()";
|
|
HI.AccessSpecifier = "protected";
|
|
HI.Kind = index::SymbolKind::InstanceMethod;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "cls<int>::";
|
|
HI.Name = "method";
|
|
HI.Parameters.emplace();
|
|
HI.ReturnType = {"size_t", "unsigned long"};
|
|
HI.Type = {"size_t ()", "unsigned long ()"};
|
|
},
|
|
R"(instance-method method
|
|
|
|
→ size_t (aka unsigned long)
|
|
|
|
// In cls<int>
|
|
protected: size_t method())",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Definition = "cls(int a, int b = 5)";
|
|
HI.AccessSpecifier = "public";
|
|
HI.Kind = index::SymbolKind::Constructor;
|
|
HI.NamespaceScope = "";
|
|
HI.LocalScope = "cls";
|
|
HI.Name = "cls";
|
|
HI.Parameters.emplace();
|
|
HI.Parameters->emplace_back();
|
|
HI.Parameters->back().Type = "int";
|
|
HI.Parameters->back().Name = "a";
|
|
HI.Parameters->emplace_back();
|
|
HI.Parameters->back().Type = "int";
|
|
HI.Parameters->back().Name = "b";
|
|
HI.Parameters->back().Default = "5";
|
|
},
|
|
R"(constructor cls
|
|
|
|
Parameters:
|
|
- int a
|
|
- int b = 5
|
|
|
|
// In cls
|
|
public: cls(int a, int b = 5))",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Union;
|
|
HI.AccessSpecifier = "private";
|
|
HI.Name = "foo";
|
|
HI.NamespaceScope = "ns1::";
|
|
HI.Definition = "union foo {}";
|
|
},
|
|
R"(union foo
|
|
|
|
// In namespace ns1
|
|
private: union foo {})",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Name = "foo";
|
|
HI.Definition = "int foo = 3";
|
|
HI.LocalScope = "test::Bar::";
|
|
HI.Value = "3";
|
|
HI.Type = "int";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "arg_a";
|
|
HI.CalleeArgInfo->Type = "int";
|
|
HI.CalleeArgInfo->Default = "7";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::Value, false};
|
|
},
|
|
R"(variable foo
|
|
|
|
Type: int
|
|
Value = 3
|
|
Passed as arg_a
|
|
|
|
// In test::Bar
|
|
int foo = 3)",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Name = "foo";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Type = "int";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::Value, false};
|
|
},
|
|
R"(variable foo
|
|
|
|
Passed by value)",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Name = "foo";
|
|
HI.Definition = "int foo = 3";
|
|
HI.LocalScope = "test::Bar::";
|
|
HI.Value = "3";
|
|
HI.Type = "int";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "arg_a";
|
|
HI.CalleeArgInfo->Type = "int";
|
|
HI.CalleeArgInfo->Default = "7";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::Ref, false};
|
|
},
|
|
R"(variable foo
|
|
|
|
Type: int
|
|
Value = 3
|
|
Passed by reference as arg_a
|
|
|
|
// In test::Bar
|
|
int foo = 3)",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Name = "foo";
|
|
HI.Definition = "int foo = 3";
|
|
HI.LocalScope = "test::Bar::";
|
|
HI.Value = "3";
|
|
HI.Type = "int";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "arg_a";
|
|
HI.CalleeArgInfo->Type = {"alias_int", "int"};
|
|
HI.CalleeArgInfo->Default = "7";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::Value, true};
|
|
},
|
|
R"(variable foo
|
|
|
|
Type: int
|
|
Value = 3
|
|
Passed as arg_a (converted to alias_int)
|
|
|
|
// In test::Bar
|
|
int foo = 3)",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Macro;
|
|
HI.Name = "PLUS_ONE";
|
|
HI.Definition = "#define PLUS_ONE(X) (X+1)\n\n"
|
|
"// Expands to\n"
|
|
"(1 + 1)";
|
|
},
|
|
R"(macro PLUS_ONE
|
|
|
|
#define PLUS_ONE(X) (X+1)
|
|
|
|
// Expands to
|
|
(1 + 1))",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Name = "foo";
|
|
HI.Definition = "int foo = 3";
|
|
HI.LocalScope = "test::Bar::";
|
|
HI.Value = "3";
|
|
HI.Type = "int";
|
|
HI.CalleeArgInfo.emplace();
|
|
HI.CalleeArgInfo->Name = "arg_a";
|
|
HI.CalleeArgInfo->Type = "int";
|
|
HI.CalleeArgInfo->Default = "7";
|
|
HI.CallPassType = HoverInfo::PassType{PassMode::ConstRef, true};
|
|
},
|
|
R"(variable foo
|
|
|
|
Type: int
|
|
Value = 3
|
|
Passed by const reference as arg_a (converted to int)
|
|
|
|
// In test::Bar
|
|
int foo = 3)",
|
|
},
|
|
{
|
|
[](HoverInfo &HI) {
|
|
HI.Name = "stdio.h";
|
|
HI.Definition = "/usr/include/stdio.h";
|
|
},
|
|
R"(stdio.h
|
|
|
|
/usr/include/stdio.h)",
|
|
},
|
|
{[](HoverInfo &HI) {
|
|
HI.Name = "foo.h";
|
|
HI.UsedSymbolNames = {"Foo", "Bar", "Bar"};
|
|
},
|
|
R"(foo.h
|
|
|
|
provides Foo, Bar, Bar)"},
|
|
{[](HoverInfo &HI) {
|
|
HI.Name = "foo.h";
|
|
HI.UsedSymbolNames = {"Foo", "Bar", "Baz", "Foobar", "Qux", "Quux"};
|
|
},
|
|
R"(foo.h
|
|
|
|
provides Foo, Bar, Baz, Foobar, Qux and 1 more)"}};
|
|
|
|
for (const auto &C : Cases) {
|
|
HoverInfo HI;
|
|
C.Builder(HI);
|
|
Config Cfg;
|
|
Cfg.Hover.ShowAKA = true;
|
|
WithContextValue WithCfg(Config::Key, std::move(Cfg));
|
|
EXPECT_EQ(HI.present().asPlainText(), C.ExpectedRender);
|
|
}
|
|
}
|
|
|
|
TEST(Hover, ParseDocumentation) {
|
|
struct Case {
|
|
llvm::StringRef Documentation;
|
|
llvm::StringRef ExpectedRenderMarkdown;
|
|
llvm::StringRef ExpectedRenderPlainText;
|
|
} Cases[] = {{
|
|
" \n foo\nbar",
|
|
"foo bar",
|
|
"foo bar",
|
|
},
|
|
{
|
|
"foo\nbar \n ",
|
|
"foo bar",
|
|
"foo bar",
|
|
},
|
|
{
|
|
"foo \nbar",
|
|
"foo bar",
|
|
"foo bar",
|
|
},
|
|
{
|
|
"foo \nbar",
|
|
"foo bar",
|
|
"foo bar",
|
|
},
|
|
{
|
|
"foo\n\n\nbar",
|
|
"foo \nbar",
|
|
"foo\nbar",
|
|
},
|
|
{
|
|
"foo\n\n\n\tbar",
|
|
"foo \nbar",
|
|
"foo\nbar",
|
|
},
|
|
{
|
|
"foo\n\n\n bar",
|
|
"foo \nbar",
|
|
"foo\nbar",
|
|
},
|
|
{
|
|
"foo.\nbar",
|
|
"foo. \nbar",
|
|
"foo.\nbar",
|
|
},
|
|
{
|
|
"foo. \nbar",
|
|
"foo. \nbar",
|
|
"foo.\nbar",
|
|
},
|
|
{
|
|
"foo\n*bar",
|
|
"foo \n\\*bar",
|
|
"foo\n*bar",
|
|
},
|
|
{
|
|
"foo\nbar",
|
|
"foo bar",
|
|
"foo bar",
|
|
},
|
|
{
|
|
"Tests primality of `p`.",
|
|
"Tests primality of `p`.",
|
|
"Tests primality of `p`.",
|
|
},
|
|
{
|
|
"'`' should not occur in `Code`",
|
|
"'\\`' should not occur in `Code`",
|
|
"'`' should not occur in `Code`",
|
|
},
|
|
{
|
|
"`not\nparsed`",
|
|
"\\`not parsed\\`",
|
|
"`not parsed`",
|
|
}};
|
|
|
|
for (const auto &C : Cases) {
|
|
markup::Document Output;
|
|
parseDocumentation(C.Documentation, Output);
|
|
|
|
EXPECT_EQ(Output.asMarkdown(), C.ExpectedRenderMarkdown);
|
|
EXPECT_EQ(Output.asPlainText(), C.ExpectedRenderPlainText);
|
|
}
|
|
}
|
|
|
|
// This is a separate test as headings don't create any differences in
|
|
// plaintext mode.
|
|
TEST(Hover, PresentHeadings) {
|
|
HoverInfo HI;
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Name = "foo";
|
|
|
|
EXPECT_EQ(HI.present().asMarkdown(), "### variable `foo`");
|
|
}
|
|
|
|
// This is a separate test as rulers behave differently in markdown vs
|
|
// plaintext.
|
|
TEST(Hover, PresentRulers) {
|
|
HoverInfo HI;
|
|
HI.Kind = index::SymbolKind::Variable;
|
|
HI.Name = "foo";
|
|
HI.Value = "val";
|
|
HI.Definition = "def";
|
|
|
|
llvm::StringRef ExpectedMarkdown = //
|
|
"### variable `foo` \n"
|
|
"\n"
|
|
"---\n"
|
|
"Value = `val` \n"
|
|
"\n"
|
|
"---\n"
|
|
"```cpp\n"
|
|
"def\n"
|
|
"```";
|
|
EXPECT_EQ(HI.present().asMarkdown(), ExpectedMarkdown);
|
|
|
|
llvm::StringRef ExpectedPlaintext = R"pt(variable foo
|
|
|
|
Value = val
|
|
|
|
def)pt";
|
|
EXPECT_EQ(HI.present().asPlainText(), ExpectedPlaintext);
|
|
}
|
|
|
|
TEST(Hover, SpaceshipTemplateNoCrash) {
|
|
Annotations T(R"cpp(
|
|
namespace std {
|
|
struct strong_ordering {
|
|
int n;
|
|
constexpr operator int() const { return n; }
|
|
static const strong_ordering equal, greater, less;
|
|
};
|
|
constexpr strong_ordering strong_ordering::equal = {0};
|
|
constexpr strong_ordering strong_ordering::greater = {1};
|
|
constexpr strong_ordering strong_ordering::less = {-1};
|
|
}
|
|
|
|
template <typename T>
|
|
struct S {
|
|
// Foo bar baz
|
|
friend auto operator<=>(S, S) = default;
|
|
};
|
|
static_assert(S<void>() =^= S<void>());
|
|
)cpp");
|
|
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
TU.ExtraArgs.push_back("-std=c++20");
|
|
auto AST = TU.build();
|
|
auto HI = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
EXPECT_EQ(HI->Documentation, "Foo bar baz");
|
|
}
|
|
|
|
TEST(Hover, ForwardStructNoCrash) {
|
|
Annotations T(R"cpp(
|
|
struct Foo;
|
|
int bar;
|
|
auto baz = (Fo^o*)&bar;
|
|
)cpp");
|
|
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
auto AST = TU.build();
|
|
auto HI = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
ASSERT_TRUE(HI);
|
|
EXPECT_EQ(*HI->Value, "&bar");
|
|
}
|
|
|
|
TEST(Hover, FunctionParameterDefaulValueNotEvaluatedOnInvalidDecls) {
|
|
struct {
|
|
const char *const Code;
|
|
const std::optional<std::string> HoverValue;
|
|
} Cases[] = {
|
|
{R"cpp(
|
|
// error-ok testing behavior on invalid decl
|
|
class Foo {};
|
|
void foo(Foo p^aram = nullptr);
|
|
)cpp",
|
|
std::nullopt},
|
|
{R"cpp(
|
|
class Foo {};
|
|
void foo(Foo *p^aram = nullptr);
|
|
)cpp",
|
|
"nullptr"},
|
|
};
|
|
|
|
for (const auto &C : Cases) {
|
|
Annotations T(C.Code);
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
auto AST = TU.build();
|
|
auto HI = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
ASSERT_TRUE(HI);
|
|
ASSERT_EQ(HI->Value, C.HoverValue);
|
|
}
|
|
}
|
|
|
|
TEST(Hover, DisableShowAKA) {
|
|
Annotations T(R"cpp(
|
|
using m_int = int;
|
|
m_int ^[[a]];
|
|
)cpp");
|
|
|
|
Config Cfg;
|
|
Cfg.Hover.ShowAKA = false;
|
|
WithContextValue WithCfg(Config::Key, std::move(Cfg));
|
|
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
TU.ExtraArgs.push_back("-std=c++17");
|
|
auto AST = TU.build();
|
|
auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
|
|
ASSERT_TRUE(H);
|
|
EXPECT_EQ(H->Type, HoverInfo::PrintedType("m_int"));
|
|
}
|
|
|
|
TEST(Hover, HideBigInitializers) {
|
|
Annotations T(R"cpp(
|
|
#define A(x) x, x, x, x
|
|
#define B(x) A(A(A(A(x))))
|
|
int a^rr[] = {B(0)};
|
|
)cpp");
|
|
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
auto AST = TU.build();
|
|
auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
|
|
ASSERT_TRUE(H);
|
|
EXPECT_EQ(H->Definition, "int arr[]");
|
|
}
|
|
|
|
#if defined(__aarch64__)
|
|
// FIXME: AARCH64 sanitizer buildbots are broken after 72142fbac4.
|
|
#define PREDEFINEMACROS_TEST(x) DISABLED_##x
|
|
#else
|
|
#define PREDEFINEMACROS_TEST(x) x
|
|
#endif
|
|
|
|
TEST(Hover, PREDEFINEMACROS_TEST(GlobalVarEnumeralCastNoCrash)) {
|
|
Annotations T(R"cpp(
|
|
using uintptr_t = __UINTPTR_TYPE__;
|
|
enum Test : uintptr_t {};
|
|
unsigned global_var;
|
|
void foo() {
|
|
Test v^al = static_cast<Test>(reinterpret_cast<uintptr_t>(&global_var));
|
|
}
|
|
)cpp");
|
|
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
TU.PredefineMacros = true;
|
|
auto AST = TU.build();
|
|
auto HI = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
ASSERT_TRUE(HI);
|
|
EXPECT_EQ(*HI->Value, "&global_var");
|
|
}
|
|
|
|
TEST(Hover, PREDEFINEMACROS_TEST(GlobalVarIntCastNoCrash)) {
|
|
Annotations T(R"cpp(
|
|
using uintptr_t = __UINTPTR_TYPE__;
|
|
unsigned global_var;
|
|
void foo() {
|
|
uintptr_t a^ddress = reinterpret_cast<uintptr_t>(&global_var);
|
|
}
|
|
)cpp");
|
|
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
TU.PredefineMacros = true;
|
|
auto AST = TU.build();
|
|
auto HI = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
ASSERT_TRUE(HI);
|
|
EXPECT_EQ(*HI->Value, "&global_var");
|
|
}
|
|
|
|
TEST(Hover, Typedefs) {
|
|
Annotations T(R"cpp(
|
|
template <bool X, typename T, typename F>
|
|
struct cond { using type = T; };
|
|
template <typename T, typename F>
|
|
struct cond<false, T, F> { using type = F; };
|
|
|
|
template <bool X, typename T, typename F>
|
|
using type = typename cond<X, T, F>::type;
|
|
|
|
void foo() {
|
|
using f^oo = type<true, int, double>;
|
|
}
|
|
)cpp");
|
|
|
|
TestTU TU = TestTU::withCode(T.code());
|
|
auto AST = TU.build();
|
|
auto H = getHover(AST, T.point(), format::getLLVMStyle(), nullptr);
|
|
|
|
ASSERT_TRUE(H && H->Type);
|
|
EXPECT_EQ(H->Type->Type, "int");
|
|
EXPECT_EQ(H->Definition, "using foo = type<true, int, double>");
|
|
}
|
|
|
|
TEST(Hover, EvaluateMacros) {
|
|
llvm::StringRef PredefinedCXX = R"cpp(
|
|
#define X 42
|
|
#define SizeOf sizeof
|
|
#define AlignOf alignof
|
|
#define PLUS_TWO +2
|
|
#define TWO 2
|
|
|
|
using u64 = unsigned long long;
|
|
// calculate (a ** b) % p
|
|
constexpr u64 pow_with_mod(u64 a, u64 b, u64 p) {
|
|
u64 ret = 1;
|
|
while (b) {
|
|
if (b & 1)
|
|
ret = (ret * a) % p;
|
|
a = (a * a) % p;
|
|
b >>= 1;
|
|
}
|
|
return ret;
|
|
}
|
|
#define last_n_digit(x, y, n) \
|
|
pow_with_mod(x, y, pow_with_mod(10, n, 2147483647))
|
|
#define declare_struct(X, name, value) \
|
|
struct X { \
|
|
constexpr auto name() { return value; } \
|
|
}
|
|
#define gnu_statement_expression(value) \
|
|
({ \
|
|
declare_struct(Widget, getter, value); \
|
|
Widget().getter(); \
|
|
})
|
|
#define define_lambda_begin(lambda, ...) \
|
|
[&](__VA_ARGS__) {
|
|
#define define_lambda_end() }
|
|
|
|
#define left_bracket [
|
|
#define right_bracket ]
|
|
#define dg_left_bracket <:
|
|
#define dg_right_bracket :>
|
|
#define array_decl(type, name, size) type name left_bracket size right_bracket
|
|
)cpp";
|
|
|
|
struct {
|
|
llvm::StringRef Code;
|
|
const std::function<void(std::optional<HoverInfo>, size_t /*Id*/)>
|
|
Validator;
|
|
} Cases[] = {
|
|
{
|
|
/*Code=*/R"cpp(
|
|
X^;
|
|
)cpp",
|
|
/*Validator=*/
|
|
[](std::optional<HoverInfo> HI, size_t) {
|
|
EXPECT_EQ(HI->Value, "42 (0x2a)");
|
|
EXPECT_EQ(HI->Type, HoverInfo::PrintedType("int"));
|
|
},
|
|
},
|
|
{
|
|
/*Code=*/R"cpp(
|
|
Size^Of(int);
|
|
)cpp",
|
|
/*Validator=*/
|
|
[](std::optional<HoverInfo> HI, size_t) {
|
|
EXPECT_TRUE(HI->Value);
|
|
EXPECT_TRUE(HI->Type);
|
|
// Don't validate type or value of `sizeof` and `alignof` as we're
|
|
// getting different values or desugared types on different
|
|
// platforms. Same as below.
|
|
},
|
|
},
|
|
{
|
|
/*Code=*/R"cpp(
|
|
struct Y {
|
|
int y;
|
|
double z;
|
|
};
|
|
Alig^nOf(Y);
|
|
)cpp",
|
|
/*Validator=*/
|
|
[](std::optional<HoverInfo> HI, size_t) {
|
|
EXPECT_TRUE(HI->Value);
|
|
EXPECT_TRUE(HI->Type);
|
|
},
|
|
},
|
|
{
|
|
/*Code=*/R"cpp(
|
|
// 2**32 == 4294967296
|
|
last_n_di^git(2, 32, 6);
|
|
)cpp",
|
|
/*Validator=*/
|
|
[](std::optional<HoverInfo> HI, size_t) {
|
|
EXPECT_EQ(HI->Value, "967296 (0xec280)");
|
|
EXPECT_EQ(HI->Type, "u64");
|
|
},
|
|
},
|
|
{
|
|
/*Code=*/R"cpp(
|
|
gnu_statement_exp^ression(42);
|
|
)cpp",
|
|
/*Validator=*/
|
|
[](std::optional<HoverInfo> HI, size_t) {
|
|
EXPECT_EQ(HI->Value, "42 (0x2a)");
|
|
EXPECT_EQ(HI->Type, "int");
|
|
},
|
|
},
|
|
{
|
|
/*Code=*/R"cpp(
|
|
40 + PLU^S_TWO;
|
|
)cpp",
|
|
/*Validator=*/
|
|
[](std::optional<HoverInfo> HI, size_t) {
|
|
EXPECT_EQ(HI->Value, "2");
|
|
EXPECT_EQ(HI->Type, "int");
|
|
},
|
|
},
|
|
{
|
|
/*Code=*/R"cpp(
|
|
40 PLU^S_TWO;
|
|
)cpp",
|
|
/*Validator=*/
|
|
[](std::optional<HoverInfo> HI, size_t) {
|
|
EXPECT_FALSE(HI->Value) << HI->Value;
|
|
EXPECT_FALSE(HI->Type) << HI->Type;
|
|
},
|
|
},
|
|
{
|
|
/*Code=*/R"cpp(
|
|
40 + TW^O;
|
|
)cpp",
|
|
/*Validator=*/
|
|
[](std::optional<HoverInfo> HI, size_t) {
|
|
EXPECT_EQ(HI->Value, "2");
|
|
EXPECT_EQ(HI->Type, "int");
|
|
},
|
|
},
|
|
{
|
|
/*Code=*/R"cpp(
|
|
arra^y_decl(int, vector, 10);
|
|
vector left_b^racket 3 right_b^racket;
|
|
vector dg_le^ft_bracket 3 dg_righ^t_bracket;
|
|
)cpp",
|
|
/*Validator=*/
|
|
[](std::optional<HoverInfo> HI, size_t Id) {
|
|
switch (Id) {
|
|
case 0:
|
|
EXPECT_EQ(HI->Type, HoverInfo::PrintedType("int[10]"));
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
case 4:
|
|
EXPECT_FALSE(HI->Type) << HI->Type;
|
|
EXPECT_FALSE(HI->Value) << HI->Value;
|
|
break;
|
|
default:
|
|
ASSERT_TRUE(false) << "Unhandled id: " << Id;
|
|
}
|
|
},
|
|
},
|
|
{
|
|
/*Code=*/R"cpp(
|
|
constexpr auto value = define_lamb^da_begin(lambda, int, char)
|
|
// Check if the expansion range is right.
|
|
return ^last_n_digit(10, 3, 3)^;
|
|
define_lam^bda_end();
|
|
)cpp",
|
|
/*Validator=*/
|
|
[](std::optional<HoverInfo> HI, size_t Id) {
|
|
switch (Id) {
|
|
case 0:
|
|
EXPECT_FALSE(HI->Value);
|
|
EXPECT_EQ(HI->Type, HoverInfo::PrintedType("const (lambda)"));
|
|
break;
|
|
case 1:
|
|
EXPECT_EQ(HI->Value, "0");
|
|
EXPECT_EQ(HI->Type, HoverInfo::PrintedType("u64"));
|
|
break;
|
|
case 2:
|
|
EXPECT_FALSE(HI);
|
|
break;
|
|
case 3:
|
|
EXPECT_FALSE(HI->Type) << HI->Type;
|
|
EXPECT_FALSE(HI->Value) << HI->Value;
|
|
break;
|
|
default:
|
|
ASSERT_TRUE(false) << "Unhandled id: " << Id;
|
|
}
|
|
},
|
|
},
|
|
};
|
|
|
|
Config Cfg;
|
|
Cfg.Hover.ShowAKA = false;
|
|
WithContextValue WithCfg(Config::Key, std::move(Cfg));
|
|
for (const auto &C : Cases) {
|
|
Annotations Code(
|
|
(PredefinedCXX + "void function() {\n" + C.Code + "}\n").str());
|
|
auto TU = TestTU::withCode(Code.code());
|
|
TU.ExtraArgs.push_back("-std=c++17");
|
|
auto AST = TU.build();
|
|
for (auto [Index, Position] : llvm::enumerate(Code.points())) {
|
|
C.Validator(getHover(AST, Position, format::getLLVMStyle(), nullptr),
|
|
Index);
|
|
}
|
|
}
|
|
|
|
Annotations C(R"c(
|
|
#define alignof _Alignof
|
|
void foo() {
|
|
al^ignof(struct { int x; char y[10]; });
|
|
}
|
|
)c");
|
|
|
|
auto TU = TestTU::withCode(C.code());
|
|
TU.Filename = "TestTU.c";
|
|
TU.ExtraArgs = {
|
|
"-std=c17",
|
|
};
|
|
auto AST = TU.build();
|
|
auto H = getHover(AST, C.point(), format::getLLVMStyle(), nullptr);
|
|
|
|
ASSERT_TRUE(H);
|
|
EXPECT_TRUE(H->Value);
|
|
EXPECT_TRUE(H->Type);
|
|
}
|
|
} // namespace
|
|
} // namespace clangd
|
|
} // namespace clang
|