//===-- 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 "Matchers.h" #include "XRefs.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/PCHContainerOperations.h" #include "clang/Frontend/Utils.h" #include "llvm/Support/Path.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace clang { namespace clangd { using namespace llvm; void PrintTo(const DocumentHighlight &V, std::ostream *O) { llvm::raw_os_ostream OS(*O); OS << V.range; if (V.kind == DocumentHighlightKind::Read) OS << "(r)"; if (V.kind == DocumentHighlightKind::Write) OS << "(w)"; } namespace { using testing::ElementsAre; using testing::Field; using testing::Matcher; using testing::UnorderedElementsAreArray; // FIXME: this is duplicated with FileIndexTests. Share it. ParsedAST build(StringRef Code) { auto CI = createInvocationFromCommandLine({"clang", "-xc++", "Foo.cpp"}); auto Buf = MemoryBuffer::getMemBuffer(Code); auto AST = ParsedAST::Build( Context::empty(), std::move(CI), nullptr, std::move(Buf), std::make_shared(), vfs::getRealFileSystem()); assert(AST.hasValue()); return std::move(*AST); } // Extracts ranges from an annotated example, and constructs a matcher for a // highlight set. Ranges should be named $read/$write as appropriate. Matcher &> HighlightsFrom(const Annotations &Test) { std::vector 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 = build(T.code()); EXPECT_THAT(findDocumentHighlights(Context::empty(), AST, T.point()), HighlightsFrom(T)) << Test; } } MATCHER_P(RangeIs, R, "") { return arg.range == R; } 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", }; for (const char *Test : Tests) { Annotations T(Test); auto AST = build(T.code()); EXPECT_THAT(findDefinitions(Context::empty(), AST, T.point()), ElementsAre(RangeIs(T.range()))) << Test; } } } // namespace } // namespace clangd } // namespace clang