diff options
Diffstat (limited to 'clang-tools-extra/unittests/clangd/XRefsTests.cpp')
-rw-r--r-- | clang-tools-extra/unittests/clangd/XRefsTests.cpp | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/clang-tools-extra/unittests/clangd/XRefsTests.cpp b/clang-tools-extra/unittests/clangd/XRefsTests.cpp new file mode 100644 index 00000000000..db158e7bc41 --- /dev/null +++ b/clang-tools-extra/unittests/clangd/XRefsTests.cpp @@ -0,0 +1,218 @@ +//===-- 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<PCHContainerOperations>(), 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<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 = 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 |