forked from OSchip/llvm-project
989 lines
25 KiB
C++
989 lines
25 KiB
C++
//===-- XRefsTests.cpp ---------------------------*- C++ -*--------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "Annotations.h"
|
|
#include "ClangdUnit.h"
|
|
#include "Compiler.h"
|
|
#include "Matchers.h"
|
|
#include "SyncAPI.h"
|
|
#include "TestFS.h"
|
|
#include "TestTU.h"
|
|
#include "XRefs.h"
|
|
#include "index/FileIndex.h"
|
|
#include "index/SymbolCollector.h"
|
|
#include "clang/Index/IndexingAction.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "gmock/gmock.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
namespace clang {
|
|
namespace clangd {
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
using testing::ElementsAre;
|
|
using testing::Field;
|
|
using testing::IsEmpty;
|
|
using testing::Matcher;
|
|
using testing::UnorderedElementsAreArray;
|
|
|
|
class IgnoreDiagnostics : public DiagnosticsConsumer {
|
|
void onDiagnosticsReady(PathRef File,
|
|
std::vector<Diag> Diagnostics) override {}
|
|
};
|
|
|
|
// Extracts ranges from an annotated example, and constructs a matcher for a
|
|
// highlight set. Ranges should be named $read/$write as appropriate.
|
|
Matcher<const std::vector<DocumentHighlight> &>
|
|
HighlightsFrom(const Annotations &Test) {
|
|
std::vector<DocumentHighlight> Expected;
|
|
auto Add = [&](const Range &R, DocumentHighlightKind K) {
|
|
Expected.emplace_back();
|
|
Expected.back().range = R;
|
|
Expected.back().kind = K;
|
|
};
|
|
for (const auto &Range : Test.ranges())
|
|
Add(Range, DocumentHighlightKind::Text);
|
|
for (const auto &Range : Test.ranges("read"))
|
|
Add(Range, DocumentHighlightKind::Read);
|
|
for (const auto &Range : Test.ranges("write"))
|
|
Add(Range, DocumentHighlightKind::Write);
|
|
return UnorderedElementsAreArray(Expected);
|
|
}
|
|
|
|
TEST(HighlightsTest, All) {
|
|
const char *Tests[] = {
|
|
R"cpp(// Local variable
|
|
int main() {
|
|
int [[bonjour]];
|
|
$write[[^bonjour]] = 2;
|
|
int test1 = $read[[bonjour]];
|
|
}
|
|
)cpp",
|
|
|
|
R"cpp(// Struct
|
|
namespace ns1 {
|
|
struct [[MyClass]] {
|
|
static void foo([[MyClass]]*) {}
|
|
};
|
|
} // namespace ns1
|
|
int main() {
|
|
ns1::[[My^Class]]* Params;
|
|
}
|
|
)cpp",
|
|
|
|
R"cpp(// Function
|
|
int [[^foo]](int) {}
|
|
int main() {
|
|
[[foo]]([[foo]](42));
|
|
auto *X = &[[foo]];
|
|
}
|
|
)cpp",
|
|
};
|
|
for (const char *Test : Tests) {
|
|
Annotations T(Test);
|
|
auto AST = TestTU::withCode(T.code()).build();
|
|
EXPECT_THAT(findDocumentHighlights(AST, T.point()), HighlightsFrom(T))
|
|
<< Test;
|
|
}
|
|
}
|
|
|
|
MATCHER_P(RangeIs, R, "") { return arg.range == R; }
|
|
|
|
TEST(GoToDefinition, WithIndex) {
|
|
Annotations SymbolHeader(R"cpp(
|
|
class $forward[[Forward]];
|
|
class $foo[[Foo]] {};
|
|
|
|
void $f1[[f1]]();
|
|
|
|
inline void $f2[[f2]]() {}
|
|
)cpp");
|
|
Annotations SymbolCpp(R"cpp(
|
|
class $forward[[forward]] {};
|
|
void $f1[[f1]]() {}
|
|
)cpp");
|
|
|
|
TestTU TU;
|
|
TU.Code = SymbolCpp.code();
|
|
TU.HeaderCode = SymbolHeader.code();
|
|
auto Index = TU.index();
|
|
auto runFindDefinitionsWithIndex = [&Index](const Annotations &Main) {
|
|
auto AST = TestTU::withCode(Main.code()).build();
|
|
return clangd::findDefinitions(AST, Main.point(), Index.get());
|
|
};
|
|
|
|
Annotations Test(R"cpp(// only declaration in AST.
|
|
void [[f1]]();
|
|
int main() {
|
|
^f1();
|
|
}
|
|
)cpp");
|
|
EXPECT_THAT(runFindDefinitionsWithIndex(Test),
|
|
testing::ElementsAreArray(
|
|
{RangeIs(SymbolCpp.range("f1")), RangeIs(Test.range())}));
|
|
|
|
Test = Annotations(R"cpp(// definition in AST.
|
|
void [[f1]]() {}
|
|
int main() {
|
|
^f1();
|
|
}
|
|
)cpp");
|
|
EXPECT_THAT(runFindDefinitionsWithIndex(Test),
|
|
testing::ElementsAreArray(
|
|
{RangeIs(Test.range()), RangeIs(SymbolHeader.range("f1"))}));
|
|
|
|
Test = Annotations(R"cpp(// forward declaration in AST.
|
|
class [[Foo]];
|
|
F^oo* create();
|
|
)cpp");
|
|
EXPECT_THAT(runFindDefinitionsWithIndex(Test),
|
|
testing::ElementsAreArray(
|
|
{RangeIs(SymbolHeader.range("foo")), RangeIs(Test.range())}));
|
|
|
|
Test = Annotations(R"cpp(// defintion in AST.
|
|
class [[Forward]] {};
|
|
F^orward create();
|
|
)cpp");
|
|
EXPECT_THAT(runFindDefinitionsWithIndex(Test),
|
|
testing::ElementsAreArray({
|
|
RangeIs(Test.range()), RangeIs(SymbolHeader.range("forward")),
|
|
}));
|
|
}
|
|
|
|
TEST(GoToDefinition, All) {
|
|
const char *Tests[] = {
|
|
R"cpp(// Local variable
|
|
int main() {
|
|
int [[bonjour]];
|
|
^bonjour = 2;
|
|
int test1 = bonjour;
|
|
}
|
|
)cpp",
|
|
|
|
R"cpp(// Struct
|
|
namespace ns1 {
|
|
struct [[MyClass]] {};
|
|
} // namespace ns1
|
|
int main() {
|
|
ns1::My^Class* Params;
|
|
}
|
|
)cpp",
|
|
|
|
R"cpp(// Function definition via pointer
|
|
int [[foo]](int) {}
|
|
int main() {
|
|
auto *X = &^foo;
|
|
}
|
|
)cpp",
|
|
|
|
R"cpp(// Function declaration via call
|
|
int [[foo]](int);
|
|
int main() {
|
|
return ^foo(42);
|
|
}
|
|
)cpp",
|
|
|
|
R"cpp(// Field
|
|
struct Foo { int [[x]]; };
|
|
int main() {
|
|
Foo bar;
|
|
bar.^x;
|
|
}
|
|
)cpp",
|
|
|
|
R"cpp(// Field, member initializer
|
|
struct Foo {
|
|
int [[x]];
|
|
Foo() : ^x(0) {}
|
|
};
|
|
)cpp",
|
|
|
|
R"cpp(// Field, GNU old-style field designator
|
|
struct Foo { int [[x]]; };
|
|
int main() {
|
|
Foo bar = { ^x : 1 };
|
|
}
|
|
)cpp",
|
|
|
|
R"cpp(// Field, field designator
|
|
struct Foo { int [[x]]; };
|
|
int main() {
|
|
Foo bar = { .^x = 2 };
|
|
}
|
|
)cpp",
|
|
|
|
R"cpp(// Method call
|
|
struct Foo { int [[x]](); };
|
|
int main() {
|
|
Foo bar;
|
|
bar.^x();
|
|
}
|
|
)cpp",
|
|
|
|
R"cpp(// Typedef
|
|
typedef int [[Foo]];
|
|
int main() {
|
|
^Foo bar;
|
|
}
|
|
)cpp",
|
|
|
|
/* FIXME: clangIndex doesn't handle template type parameters
|
|
R"cpp(// Template type parameter
|
|
template <[[typename T]]>
|
|
void foo() { ^T t; }
|
|
)cpp", */
|
|
|
|
R"cpp(// Namespace
|
|
namespace [[ns]] {
|
|
struct Foo { static void bar(); }
|
|
} // namespace ns
|
|
int main() { ^ns::Foo::bar(); }
|
|
)cpp",
|
|
|
|
R"cpp(// Macro
|
|
#define MACRO 0
|
|
#define [[MACRO]] 1
|
|
int main() { return ^MACRO; }
|
|
#define MACRO 2
|
|
#undef macro
|
|
)cpp",
|
|
|
|
R"cpp(// Macro
|
|
class TTT { public: int a; };
|
|
#define [[FF]](S) if (int b = S.a) {}
|
|
void f() {
|
|
TTT t;
|
|
F^F(t);
|
|
}
|
|
)cpp",
|
|
|
|
R"cpp(// Macro argument
|
|
int [[i]];
|
|
#define ADDRESSOF(X) &X;
|
|
int *j = ADDRESSOF(^i);
|
|
)cpp",
|
|
|
|
R"cpp(// Symbol concatenated inside macro (not supported)
|
|
int *pi;
|
|
#define POINTER(X) p # X;
|
|
int i = *POINTER(^i);
|
|
)cpp",
|
|
|
|
R"cpp(// Forward class declaration
|
|
class Foo;
|
|
class [[Foo]] {};
|
|
F^oo* foo();
|
|
)cpp",
|
|
|
|
R"cpp(// Function declaration
|
|
void foo();
|
|
void g() { f^oo(); }
|
|
void [[foo]]() {}
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
#define FF(name) class name##_Test {};
|
|
[[FF]](my);
|
|
void f() { my^_Test a; }
|
|
)cpp",
|
|
|
|
R"cpp(
|
|
#define FF() class [[Test]] {};
|
|
FF();
|
|
void f() { T^est a; }
|
|
)cpp",
|
|
};
|
|
for (const char *Test : Tests) {
|
|
Annotations T(Test);
|
|
auto AST = TestTU::withCode(T.code()).build();
|
|
std::vector<Matcher<Location>> ExpectedLocations;
|
|
for (const auto &R : T.ranges())
|
|
ExpectedLocations.push_back(RangeIs(R));
|
|
EXPECT_THAT(findDefinitions(AST, T.point()),
|
|
ElementsAreArray(ExpectedLocations))
|
|
<< Test;
|
|
}
|
|
}
|
|
|
|
TEST(GoToDefinition, RelPathsInCompileCommand) {
|
|
Annotations SourceAnnotations(R"cpp(
|
|
int [[foo]];
|
|
int baz = f^oo;
|
|
)cpp");
|
|
|
|
IgnoreDiagnostics DiagConsumer;
|
|
MockCompilationDatabase CDB(/*UseRelPaths=*/true);
|
|
MockFSProvider FS;
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
|
|
|
auto FooCpp = testPath("foo.cpp");
|
|
FS.Files[FooCpp] = "";
|
|
|
|
Server.addDocument(FooCpp, SourceAnnotations.code());
|
|
runAddDocument(Server, FooCpp, SourceAnnotations.code());
|
|
auto Locations =
|
|
runFindDefinitions(Server, FooCpp, SourceAnnotations.point());
|
|
EXPECT_TRUE(bool(Locations)) << "findDefinitions returned an error";
|
|
|
|
EXPECT_THAT(*Locations, ElementsAre(Location{URIForFile{FooCpp},
|
|
SourceAnnotations.range()}));
|
|
}
|
|
|
|
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",
|
|
"Declared in function main\n\nint bonjour",
|
|
},
|
|
{
|
|
R"cpp(// Local variable in method
|
|
struct s {
|
|
void method() {
|
|
int bonjour;
|
|
^bonjour = 2;
|
|
}
|
|
};
|
|
)cpp",
|
|
"Declared in function s::method\n\nint bonjour",
|
|
},
|
|
{
|
|
R"cpp(// Struct
|
|
namespace ns1 {
|
|
struct MyClass {};
|
|
} // namespace ns1
|
|
int main() {
|
|
ns1::My^Class* Params;
|
|
}
|
|
)cpp",
|
|
"Declared in namespace ns1\n\nstruct MyClass {}",
|
|
},
|
|
{
|
|
R"cpp(// Class
|
|
namespace ns1 {
|
|
class MyClass {};
|
|
} // namespace ns1
|
|
int main() {
|
|
ns1::My^Class* Params;
|
|
}
|
|
)cpp",
|
|
"Declared in namespace ns1\n\nclass MyClass {}",
|
|
},
|
|
{
|
|
R"cpp(// Union
|
|
namespace ns1 {
|
|
union MyUnion { int x; int y; };
|
|
} // namespace ns1
|
|
int main() {
|
|
ns1::My^Union Params;
|
|
}
|
|
)cpp",
|
|
"Declared in namespace ns1\n\nunion MyUnion {}",
|
|
},
|
|
{
|
|
R"cpp(// Function definition via pointer
|
|
int foo(int) {}
|
|
int main() {
|
|
auto *X = &^foo;
|
|
}
|
|
)cpp",
|
|
"Declared in global namespace\n\nint foo(int)",
|
|
},
|
|
{
|
|
R"cpp(// Function declaration via call
|
|
int foo(int);
|
|
int main() {
|
|
return ^foo(42);
|
|
}
|
|
)cpp",
|
|
"Declared in global namespace\n\nint foo(int)",
|
|
},
|
|
{
|
|
R"cpp(// Field
|
|
struct Foo { int x; };
|
|
int main() {
|
|
Foo bar;
|
|
bar.^x;
|
|
}
|
|
)cpp",
|
|
"Declared in struct Foo\n\nint x",
|
|
},
|
|
{
|
|
R"cpp(// Field with initialization
|
|
struct Foo { int x = 5; };
|
|
int main() {
|
|
Foo bar;
|
|
bar.^x;
|
|
}
|
|
)cpp",
|
|
"Declared in struct Foo\n\nint x = 5",
|
|
},
|
|
{
|
|
R"cpp(// Static field
|
|
struct Foo { static int x; };
|
|
int main() {
|
|
Foo::^x;
|
|
}
|
|
)cpp",
|
|
"Declared in struct Foo\n\nstatic int x",
|
|
},
|
|
{
|
|
R"cpp(// Field, member initializer
|
|
struct Foo {
|
|
int x;
|
|
Foo() : ^x(0) {}
|
|
};
|
|
)cpp",
|
|
"Declared in struct Foo\n\nint x",
|
|
},
|
|
{
|
|
R"cpp(// Field, GNU old-style field designator
|
|
struct Foo { int x; };
|
|
int main() {
|
|
Foo bar = { ^x : 1 };
|
|
}
|
|
)cpp",
|
|
"Declared in struct Foo\n\nint x",
|
|
},
|
|
{
|
|
R"cpp(// Field, field designator
|
|
struct Foo { int x; };
|
|
int main() {
|
|
Foo bar = { .^x = 2 };
|
|
}
|
|
)cpp",
|
|
"Declared in struct Foo\n\nint x",
|
|
},
|
|
{
|
|
R"cpp(// Method call
|
|
struct Foo { int x(); };
|
|
int main() {
|
|
Foo bar;
|
|
bar.^x();
|
|
}
|
|
)cpp",
|
|
"Declared in struct Foo\n\nint x()",
|
|
},
|
|
{
|
|
R"cpp(// Static method call
|
|
struct Foo { static int x(); };
|
|
int main() {
|
|
Foo::^x();
|
|
}
|
|
)cpp",
|
|
"Declared in struct Foo\n\nstatic int x()",
|
|
},
|
|
{
|
|
R"cpp(// Typedef
|
|
typedef int Foo;
|
|
int main() {
|
|
^Foo bar;
|
|
}
|
|
)cpp",
|
|
"Declared in global namespace\n\ntypedef int Foo",
|
|
},
|
|
{
|
|
R"cpp(// Namespace
|
|
namespace ns {
|
|
struct Foo { static void bar(); }
|
|
} // namespace ns
|
|
int main() { ^ns::Foo::bar(); }
|
|
)cpp",
|
|
"Declared in global namespace\n\nnamespace ns {\n}",
|
|
},
|
|
{
|
|
R"cpp(// Anonymous namespace
|
|
namespace ns {
|
|
namespace {
|
|
int foo;
|
|
} // anonymous namespace
|
|
} // namespace ns
|
|
int main() { ns::f^oo++; }
|
|
)cpp",
|
|
"Declared in namespace ns::(anonymous)\n\nint foo",
|
|
},
|
|
{
|
|
R"cpp(// Macro
|
|
#define MACRO 0
|
|
#define MACRO 1
|
|
int main() { return ^MACRO; }
|
|
#define MACRO 2
|
|
#undef macro
|
|
)cpp",
|
|
"#define MACRO",
|
|
},
|
|
{
|
|
R"cpp(// Forward class declaration
|
|
class Foo;
|
|
class Foo {};
|
|
F^oo* foo();
|
|
)cpp",
|
|
"Declared in global namespace\n\nclass Foo {}",
|
|
},
|
|
{
|
|
R"cpp(// Function declaration
|
|
void foo();
|
|
void g() { f^oo(); }
|
|
void foo() {}
|
|
)cpp",
|
|
"Declared in global namespace\n\nvoid foo()",
|
|
},
|
|
{
|
|
R"cpp(// Enum declaration
|
|
enum Hello {
|
|
ONE, TWO, THREE,
|
|
};
|
|
void foo() {
|
|
Hel^lo hello = ONE;
|
|
}
|
|
)cpp",
|
|
"Declared in global namespace\n\nenum Hello {\n}",
|
|
},
|
|
{
|
|
R"cpp(// Enumerator
|
|
enum Hello {
|
|
ONE, TWO, THREE,
|
|
};
|
|
void foo() {
|
|
Hello hello = O^NE;
|
|
}
|
|
)cpp",
|
|
"Declared in enum Hello\n\nONE",
|
|
},
|
|
{
|
|
R"cpp(// Enumerator in anonymous enum
|
|
enum {
|
|
ONE, TWO, THREE,
|
|
};
|
|
void foo() {
|
|
int hello = O^NE;
|
|
}
|
|
)cpp",
|
|
"Declared in enum (anonymous)\n\nONE",
|
|
},
|
|
{
|
|
R"cpp(// Global variable
|
|
static int hey = 10;
|
|
void foo() {
|
|
he^y++;
|
|
}
|
|
)cpp",
|
|
"Declared in global namespace\n\nstatic int hey = 10",
|
|
},
|
|
{
|
|
R"cpp(// Global variable in namespace
|
|
namespace ns1 {
|
|
static int hey = 10;
|
|
}
|
|
void foo() {
|
|
ns1::he^y++;
|
|
}
|
|
)cpp",
|
|
"Declared in namespace ns1\n\nstatic int hey = 10",
|
|
},
|
|
{
|
|
R"cpp(// Field in anonymous struct
|
|
static struct {
|
|
int hello;
|
|
} s;
|
|
void foo() {
|
|
s.he^llo++;
|
|
}
|
|
)cpp",
|
|
"Declared in struct (anonymous)\n\nint hello",
|
|
},
|
|
{
|
|
R"cpp(// Templated function
|
|
template <typename T>
|
|
T foo() {
|
|
return 17;
|
|
}
|
|
void g() { auto x = f^oo<int>(); }
|
|
)cpp",
|
|
"Declared in global namespace\n\ntemplate <typename T> T foo()",
|
|
},
|
|
{
|
|
R"cpp(// Anonymous union
|
|
struct outer {
|
|
union {
|
|
int abc, def;
|
|
} v;
|
|
};
|
|
void g() { struct outer o; o.v.d^ef++; }
|
|
)cpp",
|
|
"Declared in union outer::(anonymous)\n\nint def",
|
|
},
|
|
{
|
|
R"cpp(// Nothing
|
|
void foo() {
|
|
^
|
|
}
|
|
)cpp",
|
|
"",
|
|
},
|
|
{
|
|
R"cpp(// Simple initialization with auto
|
|
void foo() {
|
|
^auto i = 1;
|
|
}
|
|
)cpp",
|
|
"int",
|
|
},
|
|
{
|
|
R"cpp(// Simple initialization with const auto
|
|
void foo() {
|
|
const ^auto i = 1;
|
|
}
|
|
)cpp",
|
|
"int",
|
|
},
|
|
{
|
|
R"cpp(// Simple initialization with const auto&
|
|
void foo() {
|
|
const ^auto& i = 1;
|
|
}
|
|
)cpp",
|
|
"int",
|
|
},
|
|
{
|
|
R"cpp(// Simple initialization with auto&
|
|
void foo() {
|
|
^auto& i = 1;
|
|
}
|
|
)cpp",
|
|
"int",
|
|
},
|
|
{
|
|
R"cpp(// Auto with initializer list.
|
|
namespace std
|
|
{
|
|
template<class _E>
|
|
class initializer_list {};
|
|
}
|
|
void foo() {
|
|
^auto i = {1,2};
|
|
}
|
|
)cpp",
|
|
"class std::initializer_list<int>",
|
|
},
|
|
{
|
|
R"cpp(// User defined conversion to auto
|
|
struct Bar {
|
|
operator ^auto() const { return 10; }
|
|
};
|
|
)cpp",
|
|
"int",
|
|
},
|
|
{
|
|
R"cpp(// Simple initialization with decltype(auto)
|
|
void foo() {
|
|
^decltype(auto) i = 1;
|
|
}
|
|
)cpp",
|
|
"int",
|
|
},
|
|
{
|
|
R"cpp(// Simple initialization with const decltype(auto)
|
|
void foo() {
|
|
const int j = 0;
|
|
^decltype(auto) i = j;
|
|
}
|
|
)cpp",
|
|
"const int",
|
|
},
|
|
{
|
|
R"cpp(// Simple initialization with const& decltype(auto)
|
|
void foo() {
|
|
int k = 0;
|
|
const int& j = k;
|
|
^decltype(auto) i = j;
|
|
}
|
|
)cpp",
|
|
"const int &",
|
|
},
|
|
{
|
|
R"cpp(// Simple initialization with & decltype(auto)
|
|
void foo() {
|
|
int k = 0;
|
|
int& j = k;
|
|
^decltype(auto) i = j;
|
|
}
|
|
)cpp",
|
|
"int &",
|
|
},
|
|
{
|
|
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(// auto function return with trailing type
|
|
struct Bar {};
|
|
^auto test() -> decltype(Bar()) {
|
|
return Bar();
|
|
}
|
|
)cpp",
|
|
"struct Bar",
|
|
},
|
|
{
|
|
R"cpp(// trailing return type
|
|
struct Bar {};
|
|
auto test() -> ^decltype(Bar()) {
|
|
return Bar();
|
|
}
|
|
)cpp",
|
|
"struct Bar",
|
|
},
|
|
{
|
|
R"cpp(// auto in function return
|
|
struct Bar {};
|
|
^auto test() {
|
|
return Bar();
|
|
}
|
|
)cpp",
|
|
"struct Bar",
|
|
},
|
|
{
|
|
R"cpp(// auto& in function return
|
|
struct Bar {};
|
|
^auto& test() {
|
|
return Bar();
|
|
}
|
|
)cpp",
|
|
"struct Bar",
|
|
},
|
|
{
|
|
R"cpp(// const auto& in function return
|
|
struct Bar {};
|
|
const ^auto& test() {
|
|
return Bar();
|
|
}
|
|
)cpp",
|
|
"struct Bar",
|
|
},
|
|
{
|
|
R"cpp(// decltype(auto) in function return
|
|
struct Bar {};
|
|
^decltype(auto) test() {
|
|
return Bar();
|
|
}
|
|
)cpp",
|
|
"struct Bar",
|
|
},
|
|
{
|
|
R"cpp(// decltype(auto) reference in function return
|
|
struct Bar {};
|
|
^decltype(auto) test() {
|
|
int a;
|
|
return (a);
|
|
}
|
|
)cpp",
|
|
"int &",
|
|
},
|
|
{
|
|
R"cpp(// decltype lvalue reference
|
|
void foo() {
|
|
int I = 0;
|
|
^decltype(I) J = I;
|
|
}
|
|
)cpp",
|
|
"int",
|
|
},
|
|
{
|
|
R"cpp(// decltype lvalue reference
|
|
void foo() {
|
|
int I= 0;
|
|
int &K = I;
|
|
^decltype(K) J = I;
|
|
}
|
|
)cpp",
|
|
"int &",
|
|
},
|
|
{
|
|
R"cpp(// decltype lvalue reference parenthesis
|
|
void foo() {
|
|
int I = 0;
|
|
^decltype((I)) J = I;
|
|
}
|
|
)cpp",
|
|
"int &",
|
|
},
|
|
{
|
|
R"cpp(// decltype rvalue reference
|
|
void foo() {
|
|
int I = 0;
|
|
^decltype(static_cast<int&&>(I)) J = static_cast<int&&>(I);
|
|
}
|
|
)cpp",
|
|
"int &&",
|
|
},
|
|
{
|
|
R"cpp(// decltype rvalue reference function call
|
|
int && bar();
|
|
void foo() {
|
|
int I = 0;
|
|
^decltype(bar()) J = bar();
|
|
}
|
|
)cpp",
|
|
"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",
|
|
"struct Bar",
|
|
},
|
|
{
|
|
R"cpp(// decltype of var with decltype.
|
|
void foo() {
|
|
int I = 0;
|
|
decltype(I) J = I;
|
|
^decltype(J) K = J;
|
|
}
|
|
)cpp",
|
|
"int",
|
|
},
|
|
{
|
|
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",
|
|
"",
|
|
},
|
|
};
|
|
|
|
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())) {
|
|
EXPECT_NE("", Test.ExpectedHover) << Test.Input;
|
|
EXPECT_EQ(H->contents.value, Test.ExpectedHover.str()) << Test.Input;
|
|
} else
|
|
EXPECT_EQ("", Test.ExpectedHover.str()) << Test.Input;
|
|
}
|
|
}
|
|
|
|
TEST(GoToInclude, All) {
|
|
MockFSProvider FS;
|
|
IgnoreDiagnostics DiagConsumer;
|
|
MockCompilationDatabase CDB;
|
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
|
|
|
auto FooCpp = testPath("foo.cpp");
|
|
const char *SourceContents = R"cpp(
|
|
#include ^"$2^foo.h$3^"
|
|
#include "$4^invalid.h"
|
|
int b = a;
|
|
// test
|
|
int foo;
|
|
#in$5^clude "$6^foo.h"$7^
|
|
)cpp";
|
|
Annotations SourceAnnotations(SourceContents);
|
|
FS.Files[FooCpp] = SourceAnnotations.code();
|
|
auto FooH = testPath("foo.h");
|
|
auto FooHUri = URIForFile{FooH};
|
|
|
|
const char *HeaderContents = R"cpp([[]]#pragma once
|
|
int a;
|
|
)cpp";
|
|
Annotations HeaderAnnotations(HeaderContents);
|
|
FS.Files[FooH] = HeaderAnnotations.code();
|
|
|
|
Server.addDocument(FooH, HeaderAnnotations.code());
|
|
Server.addDocument(FooCpp, SourceAnnotations.code());
|
|
|
|
// Test include in preamble.
|
|
auto Locations =
|
|
runFindDefinitions(Server, FooCpp, SourceAnnotations.point());
|
|
ASSERT_TRUE(bool(Locations)) << "findDefinitions returned an error";
|
|
EXPECT_THAT(*Locations,
|
|
ElementsAre(Location{FooHUri, HeaderAnnotations.range()}));
|
|
|
|
// Test include in preamble, last char.
|
|
Locations = runFindDefinitions(Server, FooCpp, SourceAnnotations.point("2"));
|
|
ASSERT_TRUE(bool(Locations)) << "findDefinitions returned an error";
|
|
EXPECT_THAT(*Locations,
|
|
ElementsAre(Location{FooHUri, HeaderAnnotations.range()}));
|
|
|
|
Locations = runFindDefinitions(Server, FooCpp, SourceAnnotations.point("3"));
|
|
ASSERT_TRUE(bool(Locations)) << "findDefinitions returned an error";
|
|
EXPECT_THAT(*Locations,
|
|
ElementsAre(Location{FooHUri, HeaderAnnotations.range()}));
|
|
|
|
// Test include outside of preamble.
|
|
Locations = runFindDefinitions(Server, FooCpp, SourceAnnotations.point("6"));
|
|
ASSERT_TRUE(bool(Locations)) << "findDefinitions returned an error";
|
|
EXPECT_THAT(*Locations,
|
|
ElementsAre(Location{FooHUri, HeaderAnnotations.range()}));
|
|
|
|
// Test a few positions that do not result in Locations.
|
|
Locations = runFindDefinitions(Server, FooCpp, SourceAnnotations.point("4"));
|
|
ASSERT_TRUE(bool(Locations)) << "findDefinitions returned an error";
|
|
EXPECT_THAT(*Locations, IsEmpty());
|
|
|
|
Locations = runFindDefinitions(Server, FooCpp, SourceAnnotations.point("5"));
|
|
ASSERT_TRUE(bool(Locations)) << "findDefinitions returned an error";
|
|
EXPECT_THAT(*Locations, IsEmpty());
|
|
|
|
Locations = runFindDefinitions(Server, FooCpp, SourceAnnotations.point("7"));
|
|
ASSERT_TRUE(bool(Locations)) << "findDefinitions returned an error";
|
|
EXPECT_THAT(*Locations, IsEmpty());
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace clangd
|
|
} // namespace clang
|