summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam McCall <sam.mccall@gmail.com>2017-12-20 16:06:05 +0000
committerSam McCall <sam.mccall@gmail.com>2017-12-20 16:06:05 +0000
commitecb55af7afac66e639fa85982483925eb6a68ca3 (patch)
tree2c6b7e22ccfcc2a8bdfc8e50b55c38242de2bf8c
parentd1ca8b929f50d952ca6d1650a61cef2b6d2cedf3 (diff)
[clangd] Switch xrefs and documenthighlight to annotated-code unit tests. NFC
Summary: The goal here is again to make it easier to read and write the tests. I've extracted `parseTextMarker` from CodeCompleteTests into an `Annotations` class, adding features to it: - as well as points `^s` it allows ranges `[[...]]` - multiple points and ranges are supported - points and ranges may be named: `$name^` and `$name[[...]]` These features are used for the xrefs tests. This also paves the way for replacing the lit diagnostics.test with more readable unit tests, using named ranges. Alternative considered: `TestSelectionRange` in clang-refactor/TestSupport Main problems were: - delimiting the end of ranges is awkward, requiring counting - comment syntax is long and at least as cryptic for most cases - no separate syntax for point vs range, which keeps xrefs tests concise - Still need to convert to Position everywhere - Still need helpers for common case of expecting exactly one point/range (I'll probably promote the extra `PrintTo`s from some of the core Protocol types into `operator<<` in `Protocol.h` itself in a separate, prior patch...) Reviewers: ioeric Subscribers: klimek, mgorny, ilya-biryukov, cfe-commits Differential Revision: https://reviews.llvm.org/D41432 git-svn-id: https://llvm.org/svn/llvm-project/clang-tools-extra/trunk@321184 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--test/clangd/definitions.test421
-rw-r--r--test/clangd/documenthighlight.test42
-rw-r--r--test/clangd/xrefs.test67
-rw-r--r--unittests/clangd/Annotations.cpp87
-rw-r--r--unittests/clangd/Annotations.h69
-rw-r--r--unittests/clangd/CMakeLists.txt2
-rw-r--r--unittests/clangd/CodeCompleteTests.cpp55
-rw-r--r--unittests/clangd/Matchers.h1
-rw-r--r--unittests/clangd/XRefsTests.cpp218
9 files changed, 462 insertions, 500 deletions
diff --git a/test/clangd/definitions.test b/test/clangd/definitions.test
deleted file mode 100644
index 9cc26109..00000000
--- a/test/clangd/definitions.test
+++ /dev/null
@@ -1,421 +0,0 @@
-# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace %s
-# It is absolutely vital that this file has CRLF line endings.
-#
-Content-Length: 125
-
-{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
-
-Content-Length: 172
-
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"int main() {\nint a;\na;\n}\n"}}}
-
-Content-Length: 148
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":0}}}
-# Go to local variable
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 5,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 0,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 148
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":1}}}
-# Go to local variable, end of token
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 5,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 0,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 214
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":2},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n Foo bar = { x : 1 };\n}\n"}]}}
-
-Content-Length: 149
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":14}}}
-# Go to field, GNU old-style field designator
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 5,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 0,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 215
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":3},"contentChanges":[{"text":"struct Foo {\nint x;\n};\nint main() {\n Foo baz = { .x = 2 };\n}\n"}]}}
-
-Content-Length: 149
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":15}}}
-# Go to field, field designator
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 5,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 0,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 188
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":4},"contentChanges":[{"text":"int main() {\n main();\n return 0;\n}"}]}}
-
-Content-Length: 148
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":3}}}
-# Go to function declaration, function call
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 1,
-# CHECK-NEXT: "line": 3
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 0,
-# CHECK-NEXT: "line": 0
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 209
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"struct Foo {\n};\nint main() {\n Foo bar;\n return 0;\n}\n"}]}}
-
-Content-Length: 148
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":3}}}
-# Go to struct declaration, new struct instance
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 1,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 0,
-# CHECK-NEXT: "line": 0
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 232
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":5},"contentChanges":[{"text":"namespace n1 {\nstruct Foo {\n};\n}\nint main() {\n n1::Foo bar;\n return 0;\n}\n"}]}}
-
-Content-Length: 148
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":4}}}
-# Go to struct declaration, new struct instance, qualified name
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 1,
-# CHECK-NEXT: "line": 3
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 0,
-# CHECK-NEXT: "line": 0
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 216
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":6},"contentChanges":[{"text":"struct Foo {\n int x;\n};\nint main() {\n Foo bar;\n bar.x;\n}\n"}]}}
-
-Content-Length: 148
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}
-# Go to field declaration, field reference
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 7,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 2,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 221
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n void x();\n};\nint main() {\n Foo bar;\n bar.x();\n}\n"}]}}
-
-Content-Length: 148
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":5,"character":7}}}
-# Go to method declaration, method call
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 10,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 2,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 241
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"struct Foo {\n};\ntypedef Foo TypedefFoo;\nint main() {\n TypedefFoo bar;\n return 0;\n}\n"}]}}
-
-Content-Length: 149
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":10}}}
-# Go to typedef
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 22,
-# CHECK-NEXT: "line": 2
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 0,
-# CHECK-NEXT: "line": 2
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 253
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"template <typename MyTemplateParam>\nvoid foo() {\n MyTemplateParam a;\n}\nint main() {\n return 0;\n}\n"}]}}
-
-Content-Length: 149
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":13}}}
-# Go to template type parameter. Fails until clangIndex is modified to handle those.
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": []
-Content-Length: 257
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\nstatic void bar() {}\n};\n}\nint main() {\n ns::Foo::bar();\n return 0;\n}\n"}]}}
-
-Content-Length: 148
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":6,"character":4}}}
-# Go to namespace, static method call
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 1,
-# CHECK-NEXT: "line": 4
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 0,
-# CHECK-NEXT: "line": 0
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 265
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"namespace ns {\nstruct Foo {\n int field;\n Foo(int param) : field(param) {}\n};\n}\nint main() {\n return 0;\n}\n"}]}}
-
-Content-Length: 149
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":21}}}
-# Go to field, member initializer
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 11,
-# CHECK-NEXT: "line": 2
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 2,
-# CHECK-NEXT: "line": 2
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 204
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define MY_MACRO 0\nint main() {\n return MY_MACRO;\n}\n"}]}}
-
-Content-Length: 148
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":2,"character":9}}}
-# Go to macro.
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 18,
-# CHECK-NEXT: "line": 0
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 8,
-# CHECK-NEXT: "line": 0
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 217
-
-{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///main.cpp","version":7},"contentChanges":[{"text":"#define FOO 1\nint a = FOO;\n#define FOO 2\nint b = FOO;\n#undef FOO\n"}]}}
-
-Content-Length: 148
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":8}}}
-# Go to macro, re-defined later
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 13,
-# CHECK-NEXT: "line": 0
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 8,
-# CHECK-NEXT: "line": 0
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 148
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":3,"character":8}}}
-# Go to macro, undefined later
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 13,
-# CHECK-NEXT: "line": 2
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 8,
-# CHECK-NEXT: "line": 2
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 148
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":4,"character":7}}}
-# Go to macro, being undefined
-# CHECK: "id": 1,
-# CHECK-NEXT: "jsonrpc": "2.0",
-# CHECK-NEXT: "result": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 13,
-# CHECK-NEXT: "line": 2
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 8,
-# CHECK-NEXT: "line": 2
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT: }
-# CHECK-NEXT: ]
-Content-Length: 156
-
-{"jsonrpc":"2.0","id":2,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///doesnotexist.cpp"},"position":{"line":4,"character":7}}}
-# CHECK: "error": {
-# CHECK-NEXT: "code": -32602,
-# CHECK-NEXT: "message": "findDefinitions called on non-added file"
-# CHECK-NEXT: },
-# CHECK-NEXT: "id": 2,
-# CHECK-NEXT: "jsonrpc": "2.0"
-Content-Length: 48
-
-{"jsonrpc":"2.0","id":10000,"method":"shutdown"}
-Content-Length: 33
-
-{"jsonrpc":"2.0","method":"exit"}
diff --git a/test/clangd/documenthighlight.test b/test/clangd/documenthighlight.test
deleted file mode 100644
index c9263324..00000000
--- a/test/clangd/documenthighlight.test
+++ /dev/null
@@ -1,42 +0,0 @@
-# RUN: clangd -run-synchronously < %s | FileCheck %s
-# It is absolutely vital that this file has CRLF line endings.
-#
-Content-Length: 125
-
-{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
-
-Content-Length: 479
-
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"#define MACRO 1\nnamespace ns1 {\nstruct MyClass {\nint xasd;\nvoid anotherOperation() {\n}\nstatic int foo(MyClass*) {\nreturn 0;\n}\n\n};\nstruct Foo {\nint xasd;\n};\n}\nint main() {\nint bonjour;\nbonjour = 2;\nint test1 = bonjour;\nns1::Foo bar = { xasd : 1};\nbar.xasd = 3;\nns1::MyClass* Params;\nParams->anotherOperation();\n}\n"}}}
-
-Content-Length: 156
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":17,"character":2}}}
-# Verify local variable
-# CHECK: {"id":1,"jsonrpc":"2.0","result":[{"kind":1,"range":{"end":{"character":11,"line":16},"start":{"character":4,"line":16}}},{"kind":3,"range":{"end":{"character":7,"line":17},"start":{"character":0,"line":17}}},{"kind":2,"range":{"end":{"character":19,"line":18},"start":{"character":12,"line":18}}}]}
-
-Content-Length: 157
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":18,"character":17}}}
-# Verify struct highlight
-# CHECK: {"id":1,"jsonrpc":"2.0","result":[{"kind":1,"range":{"end":{"character":11,"line":16},"start":{"character":4,"line":16}}},{"kind":3,"range":{"end":{"character":7,"line":17},"start":{"character":0,"line":17}}},{"kind":2,"range":{"end":{"character":19,"line":18},"start":{"character":12,"line":18}}}]}
-
-Content-Length: 157
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":21,"character":10}}}
-# Verify method highlight
-# CHECK: {"id":1,"jsonrpc":"2.0","result":[{"kind":1,"range":{"end":{"character":14,"line":2},"start":{"character":7,"line":2}}},{"kind":1,"range":{"end":{"character":22,"line":6},"start":{"character":15,"line":6}}},{"kind":1,"range":{"end":{"character":12,"line":21},"start":{"character":5,"line":21}}}]}
-
-Content-Length: 157
-
-{"jsonrpc":"2.0","id":1,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":18,"character":14}}}
-# Verify Read-access of a symbol (kind = 2)
-# CHECK: {"id":1,"jsonrpc":"2.0","result":[{"kind":1,"range":{"end":{"character":11,"line":16},"start":{"character":4,"line":16}}},{"kind":3,"range":{"end":{"character":7,"line":17},"start":{"character":0,"line":17}}},{"kind":2,"range":{"end":{"character":19,"line":18},"start":{"character":12,"line":18}}}]}
-
-Content-Length: 48
-
-{"jsonrpc":"2.0","id":10000,"method":"shutdown"}
-
-Content-Length: 33
-
-{"jsonrpc":"2.0":"method":"exit"} \ No newline at end of file
diff --git a/test/clangd/xrefs.test b/test/clangd/xrefs.test
new file mode 100644
index 00000000..ad3c241d
--- /dev/null
+++ b/test/clangd/xrefs.test
@@ -0,0 +1,67 @@
+# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace %s
+# It is absolutely vital that this file has CRLF line endings.
+#
+Content-Length: 125
+
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+
+Content-Length: 165
+
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"int x = 0;\nint y = x;"}}}
+
+Content-Length: 148
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":8}}}
+# CHECK: "id": 1,
+# CHECK-NEXT: "jsonrpc": "2.0",
+# CHECK-NEXT: "result": [
+# CHECK-NEXT: {
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 9,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 0,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: }
+# CHECK-NEXT: },
+# CHECK-NEXT: "uri": "file:///{{([A-Z]:/)?}}main.cpp"
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+Content-Length: 155
+
+{"jsonrpc":"2.0","id":1,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///main.cpp"},"position":{"line":1,"character":8}}}
+# CHECK: "id": 1
+# CHECK-NEXT: "jsonrpc": "2.0",
+# CHECK-NEXT: "result": [
+# CHECK-NEXT: {
+# CHECK-NEXT: "kind": 1,
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 5,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 4,
+# CHECK-NEXT: "line": 0
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: },
+# CHECK-NEXT: {
+# CHECK-NEXT: "kind": 2,
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 9,
+# CHECK-NEXT: "line": 1
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 8,
+# CHECK-NEXT: "line": 1
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: }
+# CHECK-NEXT: ]
+Content-Length: 48
+
+{"jsonrpc":"2.0","id":10000,"method":"shutdown"}
diff --git a/unittests/clangd/Annotations.cpp b/unittests/clangd/Annotations.cpp
new file mode 100644
index 00000000..69532214
--- /dev/null
+++ b/unittests/clangd/Annotations.cpp
@@ -0,0 +1,87 @@
+//===--- Annotations.cpp - Annotated source code for unit tests -*- 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 "SourceCode.h"
+
+namespace clang {
+namespace clangd {
+using namespace llvm;
+
+// Crash if the assertion fails, printing the message and testcase.
+// More elegant error handling isn't needed for unit tests.
+static void require(bool Assertion, const char *Msg, llvm::StringRef Code) {
+ if (!Assertion) {
+ llvm::errs() << "Annotated testcase: " << Msg << "\n" << Code << "\n";
+ llvm_unreachable("Annotated testcase assertion failed!");
+ }
+}
+
+Annotations::Annotations(StringRef Text) {
+ auto Here = [this] { return offsetToPosition(Code, Code.size()); };
+ auto Require = [this, Text](bool Assertion, const char *Msg) {
+ require(Assertion, Msg, Text);
+ };
+ Optional<StringRef> Name;
+ SmallVector<std::pair<StringRef, Position>, 8> OpenRanges;
+
+ Code.reserve(Text.size());
+ while (!Text.empty()) {
+ if (Text.consume_front("^")) {
+ Points[Name.getValueOr("")].push_back(Here());
+ Name = None;
+ continue;
+ }
+ if (Text.consume_front("[[")) {
+ OpenRanges.emplace_back(Name.getValueOr(""), Here());
+ Name = None;
+ continue;
+ }
+ Require(!Name, "$name should be followed by ^ or [[");
+ if (Text.consume_front("]]")) {
+ Require(!OpenRanges.empty(), "unmatched ]]");
+ Ranges[OpenRanges.back().first].push_back(
+ {OpenRanges.back().second, Here()});
+ OpenRanges.pop_back();
+ continue;
+ }
+ if (Text.consume_front("$")) {
+ Name = Text.take_while(llvm::isAlnum);
+ Text = Text.drop_front(Name->size());
+ continue;
+ }
+ Code.push_back(Text.front());
+ Text = Text.drop_front();
+ }
+ Require(!Name, "unterminated $name");
+ Require(OpenRanges.empty(), "unmatched [[");
+}
+
+Position Annotations::point(llvm::StringRef Name) const {
+ auto I = Points.find(Name);
+ require(I != Points.end() && I->getValue().size() == 1,
+ "expected exactly one point", Code);
+ return I->getValue()[0];
+}
+std::vector<Position> Annotations::points(llvm::StringRef Name) const {
+ auto P = Points.lookup(Name);
+ return {P.begin(), P.end()};
+}
+Range Annotations::range(llvm::StringRef Name) const {
+ auto I = Ranges.find(Name);
+ require(I != Ranges.end() && I->getValue().size() == 1,
+ "expected exactly one range", Code);
+ return I->getValue()[0];
+}
+std::vector<Range> Annotations::ranges(llvm::StringRef Name) const {
+ auto R = Ranges.lookup(Name);
+ return {R.begin(), R.end()};
+}
+
+} // namespace clangd
+} // namespace clang
diff --git a/unittests/clangd/Annotations.h b/unittests/clangd/Annotations.h
new file mode 100644
index 00000000..b376dd3f
--- /dev/null
+++ b/unittests/clangd/Annotations.h
@@ -0,0 +1,69 @@
+//===--- Annotations.h - Annotated source code for tests --------*- C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===---------------------------------------------------------------------===//
+//
+// Annotations lets you mark points and ranges inside source code, for tests:
+//
+// Annotations Example(R"cpp(
+// int complete() { x.pri^ } // ^ indicates a point
+// void err() { [["hello" == 42]]; } // [[this is a range]]
+// $definition^class Foo{}; // points can be named: "definition"
+// $fail[[static_assert(false, "")]] // ranges can be named too: "fail"
+// )cpp");
+//
+// StringRef Code = Example.code(); // annotations stripped.
+// std::vector<Position> PP = Example.points(); // all unnamed points
+// Position P = Example.point(); // there must be exactly one
+// Range R = Example.range("fail"); // find named ranges
+//
+// Points/ranges are coordinates into `code()` which is stripped of annotations.
+//
+// Ranges may be nested (and points can be inside ranges), but there's no way
+// to define general overlapping ranges.
+//
+//===---------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_ANNOTATIONS_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_ANNOTATIONS_H
+#include "Protocol.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace clang {
+namespace clangd {
+
+class Annotations {
+public:
+ // Parses the annotations from Text. Crashes if it's malformed.
+ Annotations(llvm::StringRef Text);
+
+ // The input text with all annotations stripped.
+ // All points and ranges are relative to this stripped text.
+ llvm::StringRef code() const { return Code; }
+
+ // Returns the position of the point marked by ^ (or $name^) in the text.
+ // Crashes if there isn't exactly one.
+ Position point(llvm::StringRef Name = "") const;
+ // Returns the position of all points marked by ^ (or $name^) in the text.
+ std::vector<Position> points(llvm::StringRef Name = "") const;
+
+ // Returns the location of the range marked by [[ ]] (or $name[[ ]]).
+ // Crashes if there isn't exactly one.
+ Range range(llvm::StringRef Name = "") const;
+ // Returns the location of all ranges marked by [[ ]] (or $name[[ ]]).
+ std::vector<Range> ranges(llvm::StringRef Name = "") const;
+
+private:
+ std::string Code;
+ llvm::StringMap<llvm::SmallVector<Position, 1>> Points;
+ llvm::StringMap<llvm::SmallVector<Range, 1>> Ranges;
+};
+
+} // namespace clangd
+} // namespace clang
+#endif
diff --git a/unittests/clangd/CMakeLists.txt b/unittests/clangd/CMakeLists.txt
index 36853a17..ffe11b77 100644
--- a/unittests/clangd/CMakeLists.txt
+++ b/unittests/clangd/CMakeLists.txt
@@ -9,6 +9,7 @@ include_directories(
)
add_extra_unittest(ClangdTests
+ Annotations.cpp
ClangdTests.cpp
CodeCompleteTests.cpp
ContextTests.cpp
@@ -20,6 +21,7 @@ add_extra_unittest(ClangdTests
TraceTests.cpp
SourceCodeTests.cpp
SymbolCollectorTests.cpp
+ XRefsTests.cpp
)
target_link_libraries(ClangdTests
diff --git a/unittests/clangd/CodeCompleteTests.cpp b/unittests/clangd/CodeCompleteTests.cpp
index 312725fc..7411e1c3 100644
--- a/unittests/clangd/CodeCompleteTests.cpp
+++ b/unittests/clangd/CodeCompleteTests.cpp
@@ -7,7 +7,9 @@
//
//===----------------------------------------------------------------------===//
+#include "Annotations.h"
#include "ClangdServer.h"
+#include "CodeComplete.h"
#include "Compiler.h"
#include "Context.h"
#include "Matchers.h"
@@ -60,27 +62,6 @@ class IgnoreDiagnostics : public DiagnosticsConsumer {
PathRef File, Tagged<std::vector<DiagWithFixIts>> Diagnostics) override {}
};
-struct StringWithPos {
- std::string Text;
- clangd::Position MarkerPos;
-};
-
-/// Accepts a source file with a cursor marker ^.
-/// Returns the source file with the marker removed, and the marker position.
-StringWithPos parseTextMarker(StringRef Text) {
- std::size_t MarkerOffset = Text.find('^');
- assert(MarkerOffset != StringRef::npos && "^ wasn't found in Text.");
-
- std::string WithoutMarker;
- WithoutMarker += Text.take_front(MarkerOffset);
- WithoutMarker += Text.drop_front(MarkerOffset + 1);
- assert(StringRef(WithoutMarker).find('^') == StringRef::npos &&
- "There were multiple occurences of ^ inside Text");
-
- auto MarkerPos = offsetToPosition(WithoutMarker, MarkerOffset);
- return {std::move(WithoutMarker), MarkerPos};
-}
-
// GMock helpers for matching completion items.
MATCHER_P(Named, Name, "") { return arg.insertText == Name; }
MATCHER_P(Labeled, Label, "") { return arg.label == Label; }
@@ -112,9 +93,9 @@ CompletionList completions(StringRef Text,
ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
/*StorePreamblesInMemory=*/true);
auto File = getVirtualTestFilePath("foo.cpp");
- auto Test = parseTextMarker(Text);
- Server.addDocument(Context::empty(), File, Test.Text);
- return Server.codeComplete(Context::empty(), File, Test.MarkerPos, Opts)
+ Annotations Test(Text);
+ Server.addDocument(Context::empty(), File, Test.code());
+ return Server.codeComplete(Context::empty(), File, Test.point(), Opts)
.get()
.second.Value;
}
@@ -291,13 +272,13 @@ TEST(CompletionTest, CheckContentsOverride) {
auto File = getVirtualTestFilePath("foo.cpp");
Server.addDocument(Context::empty(), File, "ignored text!");
- auto Example = parseTextMarker("int cbc; int b = ^;");
- auto Results =
- Server
- .codeComplete(Context::empty(), File, Example.MarkerPos,
- clangd::CodeCompleteOptions(), StringRef(Example.Text))
- .get()
- .second.Value;
+ Annotations Example("int cbc; int b = ^;");
+ auto Results = Server
+ .codeComplete(Context::empty(), File, Example.point(),
+ clangd::CodeCompleteOptions(),
+ StringRef(Example.code()))
+ .get()
+ .second.Value;
EXPECT_THAT(Results.items, Contains(Named("cbc")));
}
@@ -392,9 +373,9 @@ SignatureHelp signatures(StringRef Text) {
ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(),
/*StorePreamblesInMemory=*/true);
auto File = getVirtualTestFilePath("foo.cpp");
- auto Test = parseTextMarker(Text);
- Server.addDocument(Context::empty(), File, Test.Text);
- auto R = Server.signatureHelp(Context::empty(), File, Test.MarkerPos);
+ Annotations Test(Text);
+ Server.addDocument(Context::empty(), File, Test.code());
+ auto R = Server.signatureHelp(Context::empty(), File, Test.point());
assert(R);
return R.get().Value;
}
@@ -573,13 +554,13 @@ TEST(CompletionTest, ASTIndexMultiFile) {
.wait();
auto File = getVirtualTestFilePath("bar.cpp");
- auto Test = parseTextMarker(R"cpp(
+ Annotations Test(R"cpp(
namespace ns { class XXX {}; void fooooo() {} }
void f() { ns::^ }
)cpp");
- Server.addDocument(Context::empty(), File, Test.Text).wait();
+ Server.addDocument(Context::empty(), File, Test.code()).wait();
- auto Results = Server.codeComplete(Context::empty(), File, Test.MarkerPos, {})
+ auto Results = Server.codeComplete(Context::empty(), File, Test.point(), {})
.get()
.second.Value;
// "XYZ" and "foo" are not included in the file being completed but are still
diff --git a/unittests/clangd/Matchers.h b/unittests/clangd/Matchers.h
index 073a5525..81bc110b 100644
--- a/unittests/clangd/Matchers.h
+++ b/unittests/clangd/Matchers.h
@@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_MATCHERS_H
#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_MATCHERS_H
+#include "Protocol.h"
#include "gmock/gmock.h"
namespace clang {
diff --git a/unittests/clangd/XRefsTests.cpp b/unittests/clangd/XRefsTests.cpp
new file mode 100644
index 00000000..db158e7b
--- /dev/null
+++ b/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