diff options
Diffstat (limited to 'clang-tools-extra/clangd/unittests/HoverTests.cpp')
-rw-r--r-- | clang-tools-extra/clangd/unittests/HoverTests.cpp | 1339 |
1 files changed, 1339 insertions, 0 deletions
diff --git a/clang-tools-extra/clangd/unittests/HoverTests.cpp b/clang-tools-extra/clangd/unittests/HoverTests.cpp new file mode 100644 index 00000000000..7b5907faaa2 --- /dev/null +++ b/clang-tools-extra/clangd/unittests/HoverTests.cpp @@ -0,0 +1,1339 @@ +//===-- 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 "Annotations.h" +#include "Hover.h" +#include "TestIndex.h" +#include "TestTU.h" +#include "index/MemIndex.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace clang { +namespace clangd { +namespace { + +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 = 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 = 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 { + struct Foo { + int [[b^ar]]; + }; + }} + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = "ns1::ns2::"; + HI.LocalScope = "Foo::"; + HI.Name = "bar"; + HI.Kind = SymbolKind::Field; + HI.Definition = "int bar"; + HI.Type = "int"; + }}, + // 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 = SymbolKind::Variable; + HI.Definition = "int bar"; + HI.Type = "int"; + }}, + // Anon namespace and local scope. + {R"cpp( + namespace ns1 { namespace { + struct { + int [[b^ar]]; + } T; + }} + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = "ns1::(anonymous)::"; + HI.LocalScope = "(anonymous struct)::"; + HI.Name = "bar"; + HI.Kind = SymbolKind::Field; + HI.Definition = "int bar"; + HI.Type = "int"; + }}, + // 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 = 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"; + HI.Kind = SymbolKind::Class; + HI.Definition = "template <typename T> class vector {}"; + HI.TemplateParameters = { + {std::string("typename"), std::string("T"), llvm::None}, + }; + }}, + // Class template + {R"cpp( + template <template<typename, bool...> class C, + typename = char, + int = 0, + bool Q = false, + class... Ts> class Foo {}; + template <template<typename, bool...> class T> + [[F^oo]]<T> foo; + )cpp", + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "Foo"; + HI.Kind = SymbolKind::Class; + HI.Definition = + R"cpp(template <template <typename, bool...> class C, typename = char, int = 0, + bool Q = false, class... Ts> +class Foo {})cpp"; + HI.TemplateParameters = { + {std::string("template <typename, bool...> class"), + std::string("C"), llvm::None}, + {std::string("typename"), llvm::None, std::string("char")}, + {std::string("int"), llvm::None, std::string("0")}, + {std::string("bool"), std::string("Q"), std::string("false")}, + {std::string("class..."), std::string("Ts"), llvm::None}, + }; + }}, + // 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 = SymbolKind::Function; + HI.Definition = + R"cpp(template <template <typename, bool...> class C, typename = char, int = 0, + bool Q = false, class... Ts> +void foo())cpp"; + HI.ReturnType = "void"; + HI.Type = "void ()"; + HI.Parameters.emplace(); + HI.TemplateParameters = { + {std::string("template <typename, bool...> class"), + std::string("C"), llvm::None}, + {std::string("typename"), llvm::None, std::string("char")}, + {std::string("int"), llvm::None, std::string("0")}, + {std::string("bool"), std::string("Q"), std::string("false")}, + {std::string("class..."), std::string("Ts"), llvm::None}, + }; + }}, + // 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 = 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 = { + {std::string("int"), llvm::None, llvm::None}, + {std::string("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 = SymbolKind::Variable; + HI.Definition = "auto *c = &b"; + HI.Type = "class (lambda) **"; + HI.ReturnType = "bool"; + HI.Parameters = { + {std::string("int"), std::string("T"), llvm::None}, + {std::string("bool"), std::string("B"), llvm::None}, + }; + 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 = SymbolKind::Variable; + HI.Definition = "decltype(lamb) &bar"; + HI.Type = "decltype(lamb) &"; + HI.ReturnType = "bool"; + HI.Parameters = { + {std::string("int"), std::string("T"), llvm::None}, + {std::string("bool"), std::string("B"), llvm::None}, + }; + 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 = SymbolKind::Variable; + HI.Definition = "decltype(lamb) bar"; + HI.Type = "class (lambda)"; + HI.ReturnType = "bool"; + HI.Parameters = { + {std::string("int"), std::string("T"), llvm::None}, + {std::string("bool"), std::string("B"), llvm::None}, + }; + 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 = SymbolKind::Variable; + HI.Definition = "auto lamb = [&bar](int T, bool B) -> bool {}"; + HI.Type = "class (lambda)"; + HI.ReturnType = "bool"; + HI.Parameters = { + {std::string("int"), std::string("T"), llvm::None}, + {std::string("bool"), std::string("B"), llvm::None}, + }; + 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 = 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 = 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 = SymbolKind::Constructor; + HI.ReturnType = "X<T *>"; + HI.Definition = "X()"; + HI.Parameters.emplace(); + }}, + {"class X { [[^~]]X(); };", // FIXME: Should be [[~X]]() + [](HoverInfo &HI) { + HI.NamespaceScope = ""; + HI.Name = "~X"; + HI.LocalScope = "X::"; + HI.Kind = SymbolKind::Constructor; + HI.ReturnType = "void"; + HI.Definition = "~X()"; + HI.Parameters.emplace(); + }}, + + // auto on lambda + {R"cpp( + void foo() { + [[au^to]] lamb = []{}; + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "class (lambda)"; + HI.Kind = SymbolKind::Class; + }}, + // auto on template instantiation + {R"cpp( + template<typename T> class Foo{}; + void foo() { + [[au^to]] x = Foo<int>(); + } + )cpp", + [](HoverInfo &HI) { + HI.Name = "class Foo<int>"; + HI.Kind = SymbolKind::Class; + }}, + // 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 = "class Foo<int>"; + HI.Kind = SymbolKind::Class; + }}, + + // 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 = SymbolKind::String, + HI.Definition = "#define MACRO(x, y, z) void foo(x, y, z);"; + }}, + + // 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 = 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 = 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 = SymbolKind::Variable; + HI.Type = "int"; + HI.NamespaceScope = ""; + HI.Value = "3"; + }}, + {R"cpp( + enum Color { RED, GREEN, }; + Color x = [[GR^EEN]]; + )cpp", + [](HoverInfo &HI) { + HI.Name = "GREEN"; + HI.NamespaceScope = ""; + HI.LocalScope = "Color::"; + HI.Definition = "GREEN"; + HI.Kind = SymbolKind::EnumMember; + HI.Type = "enum Color"; + HI.Value = "1"; + }}, + // FIXME: We should use the Decl referenced, even if from an implicit + // instantiation. Then the scope would be Add<1, 2> and the value 3. + {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 = SymbolKind::Property; + HI.Type = "const int"; + HI.NamespaceScope = ""; + HI.LocalScope = "Add<a, b>::"; + }}, + {R"cpp( + const char *[[ba^r]] = "1234"; + )cpp", + [](HoverInfo &HI) { + HI.Name = "bar"; + HI.Definition = "const char *bar = \"1234\""; + HI.Kind = SymbolKind::Variable; + HI.Type = "const char *"; + HI.NamespaceScope = ""; + HI.Value = "&\"1234\"[0]"; + }}, + }; + 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"); + auto AST = TU.build(); + ASSERT_TRUE(AST.getDiagnostics().empty()); + + 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); + } +} + +TEST(Hover, All) { + struct OneTest { + StringRef Input; + StringRef ExpectedHover; + }; + + OneTest Tests[] = { + { + R"cpp(// No hover + ^int main() { + } + )cpp", + "", + }, + { + R"cpp(// Local variable + int main() { + int bonjour; + ^bonjour = 2; + int test1 = bonjour; + } + )cpp", + "text[Declared in]code[main]\n" + "codeblock(cpp) [\n" + "int bonjour\n" + "]", + }, + { + R"cpp(// Local variable in method + struct s { + void method() { + int bonjour; + ^bonjour = 2; + } + }; + )cpp", + "text[Declared in]code[s::method]\n" + "codeblock(cpp) [\n" + "int bonjour\n" + "]", + }, + { + R"cpp(// Struct + namespace ns1 { + struct MyClass {}; + } // namespace ns1 + int main() { + ns1::My^Class* Params; + } + )cpp", + "text[Declared in]code[ns1]\n" + "codeblock(cpp) [\n" + "struct MyClass {}\n" + "]", + }, + { + R"cpp(// Class + namespace ns1 { + class MyClass {}; + } // namespace ns1 + int main() { + ns1::My^Class* Params; + } + )cpp", + "text[Declared in]code[ns1]\n" + "codeblock(cpp) [\n" + "class MyClass {}\n" + "]", + }, + { + R"cpp(// Union + namespace ns1 { + union MyUnion { int x; int y; }; + } // namespace ns1 + int main() { + ns1::My^Union Params; + } + )cpp", + "text[Declared in]code[ns1]\n" + "codeblock(cpp) [\n" + "union MyUnion {}\n" + "]", + }, + { + R"cpp(// Function definition via pointer + int foo(int) {} + int main() { + auto *X = &^foo; + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "int foo(int)\n" + "]\n" + "text[Function definition via pointer]", + }, + { + R"cpp(// Function declaration via call + int foo(int); + int main() { + return ^foo(42); + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "int foo(int)\n" + "]\n" + "text[Function declaration via call]", + }, + { + R"cpp(// Field + struct Foo { int x; }; + int main() { + Foo bar; + bar.^x; + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", + }, + { + R"cpp(// Field with initialization + struct Foo { int x = 5; }; + int main() { + Foo bar; + bar.^x; + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x = 5\n" + "]", + }, + { + R"cpp(// Static field + struct Foo { static int x; }; + int main() { + Foo::^x; + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "static int x\n" + "]", + }, + { + R"cpp(// Field, member initializer + struct Foo { + int x; + Foo() : ^x(0) {} + }; + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", + }, + { + R"cpp(// Field, GNU old-style field designator + struct Foo { int x; }; + int main() { + Foo bar = { ^x : 1 }; + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", + }, + { + R"cpp(// Field, field designator + struct Foo { int x; }; + int main() { + Foo bar = { .^x = 2 }; + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x\n" + "]", + }, + { + R"cpp(// Method call + struct Foo { int x(); }; + int main() { + Foo bar; + bar.^x(); + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "int x()\n" + "]", + }, + { + R"cpp(// Static method call + struct Foo { static int x(); }; + int main() { + Foo::^x(); + } + )cpp", + "text[Declared in]code[Foo]\n" + "codeblock(cpp) [\n" + "static int x()\n" + "]", + }, + { + R"cpp(// Typedef + typedef int Foo; + int main() { + ^Foo bar; + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "typedef int Foo\n" + "]\n" + "text[Typedef]", + }, + { + R"cpp(// Typedef with embedded definition + typedef struct Bar {} Foo; + int main() { + ^Foo bar; + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "typedef struct Bar Foo\n" + "]\n" + "text[Typedef with embedded definition]", + }, + { + R"cpp(// Namespace + namespace ns { + struct Foo { static void bar(); } + } // namespace ns + int main() { ^ns::Foo::bar(); } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "namespace ns {}\n" + "]", + }, + { + R"cpp(// Anonymous namespace + namespace ns { + namespace { + int foo; + } // anonymous namespace + } // namespace ns + int main() { ns::f^oo++; } + )cpp", + "text[Declared in]code[ns::(anonymous)]\n" + "codeblock(cpp) [\n" + "int foo\n" + "]", + }, + { + R"cpp(// Macro + #define MACRO 0 + #define MACRO 1 + int main() { return ^MACRO; } + #define MACRO 2 + #undef macro + )cpp", + "codeblock(cpp) [\n" + "#define MACRO 1\n" + "]", + }, + { + R"cpp(// Macro + #define MACRO 0 + #define MACRO2 ^MACRO + )cpp", + "codeblock(cpp) [\n" + "#define MACRO 0\n" + "]", + }, + { + R"cpp(// Macro + #define MACRO {\ + return 0;\ + } + int main() ^MACRO + )cpp", + R"cpp(codeblock(cpp) [ +#define MACRO \ + { return 0; } +])cpp", + }, + { + R"cpp(// Forward class declaration + class Foo; + class Foo {}; + F^oo* foo(); + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "class Foo {}\n" + "]\n" + "text[Forward class declaration]", + }, + { + R"cpp(// Function declaration + void foo(); + void g() { f^oo(); } + void foo() {} + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "void foo()\n" + "]\n" + "text[Function declaration]", + }, + { + R"cpp(// Enum declaration + enum Hello { + ONE, TWO, THREE, + }; + void foo() { + Hel^lo hello = ONE; + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "enum Hello {}\n" + "]\n" + "text[Enum declaration]", + }, + { + R"cpp(// Enumerator + enum Hello { + ONE, TWO, THREE, + }; + void foo() { + Hello hello = O^NE; + } + )cpp", + "text[Declared in]code[Hello]\n" + "codeblock(cpp) [\n" + "ONE\n" + "]", + }, + { + R"cpp(// Enumerator in anonymous enum + enum { + ONE, TWO, THREE, + }; + void foo() { + int hello = O^NE; + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "ONE\n" + "]", + }, + { + R"cpp(// Global variable + static int hey = 10; + void foo() { + he^y++; + } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "static int hey = 10\n" + "]\n" + "text[Global variable]", + }, + { + R"cpp(// Global variable in namespace + namespace ns1 { + static int hey = 10; + } + void foo() { + ns1::he^y++; + } + )cpp", + "text[Declared in]code[ns1]\n" + "codeblock(cpp) [\n" + "static int hey = 10\n" + "]", + }, + { + R"cpp(// Field in anonymous struct + static struct { + int hello; + } s; + void foo() { + s.he^llo++; + } + )cpp", + "text[Declared in]code[(anonymous struct)]\n" + "codeblock(cpp) [\n" + "int hello\n" + "]", + }, + { + R"cpp(// Templated function + template <typename T> + T foo() { + return 17; + } + void g() { auto x = f^oo<int>(); } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "template <typename T> T foo()\n" + "]\n" + "text[Templated function]", + }, + { + R"cpp(// Anonymous union + struct outer { + union { + int abc, def; + } v; + }; + void g() { struct outer o; o.v.d^ef++; } + )cpp", + "text[Declared in]code[outer::(anonymous union)]\n" + "codeblock(cpp) [\n" + "int def\n" + "]", + }, + { + R"cpp(// documentation from index + int nextSymbolIsAForwardDeclFromIndexWithNoLocalDocs; + void indexSymbol(); + void g() { ind^exSymbol(); } + )cpp", + "text[Declared in]code[global namespace]\n" + "codeblock(cpp) [\n" + "void indexSymbol()\n" + "]\n" + "text[comment from index]", + }, + { + R"cpp(// Nothing + void foo() { + ^ + } + )cpp", + "", + }, + { + R"cpp(// Simple initialization with auto + void foo() { + ^auto i = 1; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Simple initialization with const auto + void foo() { + const ^auto i = 1; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Simple initialization with const auto& + void foo() { + const ^auto& i = 1; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Simple initialization with auto& + void foo() { + ^auto& i = 1; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Simple initialization with auto* + void foo() { + int a = 1; + ^auto* i = &a; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Auto with initializer list. + namespace std + { + template<class _E> + class initializer_list {}; + } + void foo() { + ^auto i = {1,2}; + } + )cpp", + "codeblock(cpp) [\n" + "class std::initializer_list<int>\n" + "]", + }, + { + R"cpp(// User defined conversion to auto + struct Bar { + operator ^auto() const { return 10; } + }; + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Simple initialization with decltype(auto) + void foo() { + ^decltype(auto) i = 1; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Simple initialization with const decltype(auto) + void foo() { + const int j = 0; + ^decltype(auto) i = j; + } + )cpp", + "codeblock(cpp) [\n" + "const int\n" + "]", + }, + { + R"cpp(// Simple initialization with const& decltype(auto) + void foo() { + int k = 0; + const int& j = k; + ^decltype(auto) i = j; + } + )cpp", + "codeblock(cpp) [\n" + "const int &\n" + "]", + }, + { + R"cpp(// Simple initialization with & decltype(auto) + void foo() { + int k = 0; + int& j = k; + ^decltype(auto) i = j; + } + )cpp", + "codeblock(cpp) [\n" + "int &\n" + "]", + }, + { + R"cpp(// decltype with initializer list: nothing + namespace std + { + template<class _E> + class initializer_list {}; + } + void foo() { + ^decltype(auto) i = {1,2}; + } + )cpp", + "", + }, + { + R"cpp(// simple trailing return type + ^auto main() -> int { + return 0; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// auto function return with trailing type + struct Bar {}; + ^auto test() -> decltype(Bar()) { + return Bar(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// trailing return type + struct Bar {}; + auto test() -> ^decltype(Bar()) { + return Bar(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// auto in function return + struct Bar {}; + ^auto test() { + return Bar(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// auto& in function return + struct Bar {}; + ^auto& test() { + return Bar(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// auto* in function return + struct Bar {}; + ^auto* test() { + Bar* bar; + return bar; + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// const auto& in function return + struct Bar {}; + const ^auto& test() { + return Bar(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// decltype(auto) in function return + struct Bar {}; + ^decltype(auto) test() { + return Bar(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// decltype(auto) reference in function return + struct Bar {}; + ^decltype(auto) test() { + int a; + return (a); + } + )cpp", + "codeblock(cpp) [\n" + "int &\n" + "]", + }, + { + R"cpp(// decltype lvalue reference + void foo() { + int I = 0; + ^decltype(I) J = I; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// decltype lvalue reference + void foo() { + int I= 0; + int &K = I; + ^decltype(K) J = I; + } + )cpp", + "codeblock(cpp) [\n" + "int &\n" + "]", + }, + { + R"cpp(// decltype lvalue reference parenthesis + void foo() { + int I = 0; + ^decltype((I)) J = I; + } + )cpp", + "codeblock(cpp) [\n" + "int &\n" + "]", + }, + { + R"cpp(// decltype rvalue reference + void foo() { + int I = 0; + ^decltype(static_cast<int&&>(I)) J = static_cast<int&&>(I); + } + )cpp", + "codeblock(cpp) [\n" + "int &&\n" + "]", + }, + { + R"cpp(// decltype rvalue reference function call + int && bar(); + void foo() { + int I = 0; + ^decltype(bar()) J = bar(); + } + )cpp", + "codeblock(cpp) [\n" + "int &&\n" + "]", + }, + { + R"cpp(// decltype of function with trailing return type. + struct Bar {}; + auto test() -> decltype(Bar()) { + return Bar(); + } + void foo() { + ^decltype(test()) i = test(); + } + )cpp", + "codeblock(cpp) [\n" + "struct Bar\n" + "]", + }, + { + R"cpp(// decltype of var with decltype. + void foo() { + int I = 0; + decltype(I) J = I; + ^decltype(J) K = J; + } + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// structured binding. Not supported yet + struct Bar {}; + void foo() { + Bar a[2]; + ^auto [x,y] = a; + } + )cpp", + "", + }, + { + R"cpp(// Template auto parameter. Nothing (Not useful). + template<^auto T> + void func() { + } + void foo() { + func<1>(); + } + )cpp", + "", + }, + { + R"cpp(// More compilcated structured types. + int bar(); + ^auto (*foo)() = bar; + )cpp", + "codeblock(cpp) [\n" + "int\n" + "]", + }, + { + R"cpp(// Should not crash when evaluating the initializer. + struct Test {}; + void test() { Test && te^st = {}; } + )cpp", + "text[Declared in]code[test]\n" + "codeblock(cpp) [\n" + "struct Test &&test = {}\n" + "]", + }, + }; + + // 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 OneTest &Test : Tests) { + Annotations T(Test.Input); + TestTU TU = TestTU::withCode(T.code()); + TU.ExtraArgs.push_back("-std=c++17"); + auto AST = TU.build(); + if (auto H = + getHover(AST, T.point(), format::getLLVMStyle(), Index.get())) { + EXPECT_NE("", Test.ExpectedHover) << Test.Input; + EXPECT_EQ(H->present().renderForTests(), Test.ExpectedHover.str()) + << Test.Input; + } else + EXPECT_EQ("", Test.ExpectedHover.str()) << Test.Input; + } +} + +} // namespace +} // namespace clangd +} // namespace clang |