summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/unittests/clangd/XRefsTests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/unittests/clangd/XRefsTests.cpp')
-rw-r--r--clang-tools-extra/unittests/clangd/XRefsTests.cpp218
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
OpenPOWER on IntegriCloud