|  | //===-- 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 |