From f473ca994a4e3f42c0a6c9f512fd8b279dedb1b1 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 30 Jul 2026 14:35:08 -0700 Subject: [PATCH] Fix a collection of small language-server bugs in document_symbol. (#7581) Don't CHECK-fail if the file contains mismatched braces and generates an unbalanced parse tree. Properly balance the start and end of symbols. This previously caused errors to be reported in clients such as VS Code. Compute a correct range for definitions. Don't assume we can find the matching `}` for an opening symbol, since some of our definitions are delimited by a `;` instead. Assisted-by: Gemini via Antigravity --- .../handle_document_symbol.cpp | 45 +++-- .../testdata/document_symbol/choice.carbon | 106 +++++++++++ .../document_symbol/fn_definition.carbon | 177 ++++++++++++++++++ .../document_symbol/incomplete.carbon | 148 +++++++++++++++ .../testdata/document_symbol/namespace.carbon | 112 +++++++++++ 5 files changed, 574 insertions(+), 14 deletions(-) create mode 100644 toolchain/language_server/testdata/document_symbol/choice.carbon create mode 100644 toolchain/language_server/testdata/document_symbol/fn_definition.carbon create mode 100644 toolchain/language_server/testdata/document_symbol/incomplete.carbon create mode 100644 toolchain/language_server/testdata/document_symbol/namespace.carbon diff --git a/toolchain/language_server/handle_document_symbol.cpp b/toolchain/language_server/handle_document_symbol.cpp index 18d647689e86..5b15484a5acf 100644 --- a/toolchain/language_server/handle_document_symbol.cpp +++ b/toolchain/language_server/handle_document_symbol.cpp @@ -63,14 +63,26 @@ class SymbolStore { auto HasOpenSymbol() const -> bool { return !open_symbols_.empty(); } // Completes a symbol, appending to parent list. - auto EndSymbol() -> void { + auto EndSymbol(const Lex::TokenizedBuffer* tokens = nullptr, + std::optional end_token = std::nullopt) + -> void { CARBON_CHECK(HasOpenSymbol()); + // Extend the range of the symbol to include the `end_token`, if one is + // provided. For example, this extends function definitions to the `}` or, + // for a terse function body, `;`. + if (tokens && end_token) { + auto [end_line, end_col] = tokens->GetEndLoc(*end_token); + open_symbols_.back().range.end = {.line = end_line.index, + .character = end_col - 1}; + } AddSymbol(open_symbols_.pop_back_val()); } // Returns final top level symbols. auto Collect() -> std::vector { - CARBON_CHECK(!HasOpenSymbol()); + while (HasOpenSymbol()) { + EndSymbol(); + } return std::move(top_level_symbols_); } @@ -93,8 +105,7 @@ static auto GetTokenRange(const Lex::TokenizedBuffer& tokens, }; } -// Finds a spanning range for the provided definition / declaration ast node. -// In the case of a definition start, will include the body as well. +// Finds a spanning range for the provided parse node. static auto GetSymbolRange(const Parse::TreeAndSubtrees& tree_and_subtrees, const Parse::NodeId& ast_node) -> clang::clangd::Range { @@ -105,14 +116,7 @@ static auto GetSymbolRange(const Parse::TreeAndSubtrees& tree_and_subtrees, auto start_token = tree_and_subtrees.tree().node_token(start_node); auto end_token = tree_and_subtrees.tree().node_token(ast_node); - if (tokens.GetKind(end_token).is_opening_symbol()) { - // DefinitionStart nodes use an opening token, so find its closing token to - // span the entire class/function body. - return GetTokenRange(tokens, start_token, - tokens.GetMatchedClosingToken(end_token)); - } else { - return GetTokenRange(tokens, start_token, end_token); - } + return GetTokenRange(tokens, start_token, end_token); } auto HandleDocumentSymbol( @@ -140,11 +144,18 @@ auto HandleDocumentSymbol( symbol_kind = clang::clangd::SymbolKind::Function; break; case Parse::NodeKind::FunctionDefinitionStart: + case Parse::NodeKind::BuiltinFunctionDefinitionStart: symbol_kind = clang::clangd::SymbolKind::Function; break; case Parse::NodeKind::Namespace: + is_leaf = true; symbol_kind = clang::clangd::SymbolKind::Namespace; break; + case Parse::NodeKind::InterfaceDecl: + case Parse::NodeKind::NamedConstraintDecl: + is_leaf = true; + symbol_kind = clang::clangd::SymbolKind::Interface; + break; case Parse::NodeKind::InterfaceDefinitionStart: case Parse::NodeKind::NamedConstraintDefinitionStart: symbol_kind = clang::clangd::SymbolKind::Interface; @@ -156,15 +167,21 @@ auto HandleDocumentSymbol( case Parse::NodeKind::ClassDefinitionStart: symbol_kind = clang::clangd::SymbolKind::Class; break; + case Parse::NodeKind::ChoiceDefinitionStart: + symbol_kind = clang::clangd::SymbolKind::Enum; + break; case Parse::NodeKind::FunctionDefinition: + case Parse::NodeKind::FunctionTerseDefinition: + case Parse::NodeKind::BuiltinFunctionDefinition: case Parse::NodeKind::NamedConstraintDefinition: case Parse::NodeKind::InterfaceDefinition: - case Parse::NodeKind::ClassDefinition: { + case Parse::NodeKind::ClassDefinition: + case Parse::NodeKind::ChoiceDefinition: { if (symbols.HasOpenSymbol()) { // Symbols definition has completed, pop it from stack and add to // parent/root. - symbols.EndSymbol(); + symbols.EndSymbol(&tokens, tree.node_token(node_id)); } continue; } diff --git a/toolchain/language_server/testdata/document_symbol/choice.carbon b/toolchain/language_server/testdata/document_symbol/choice.carbon new file mode 100644 index 000000000000..0b24c394944f --- /dev/null +++ b/toolchain/language_server/testdata/document_symbol/choice.carbon @@ -0,0 +1,106 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/language_server/testdata/document_symbol/choice.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/language_server/testdata/document_symbol/choice.carbon + +// --- choice.carbon +choice Colors { + Red, + Green, + Blue +} + +// --- STDIN +[[@LSP-CALL:initialize]] +[[@LSP-NOTIFY:textDocument/didOpen: + "textDocument": { + "uri": "file:/choice.carbon", + "languageId": "carbon", + "text": "FROM_FILE_SPLIT" + } +]] +[[@LSP-CALL:textDocument/documentSymbol: + "textDocument": {"uri": "file:/choice.carbon"} +]] +[[@LSP-CALL:shutdown]] +[[@LSP-NOTIFY:exit]] + +// CHECK:STDOUT: Content-Length: 146{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 1, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": { +// CHECK:STDOUT: "capabilities": { +// CHECK:STDOUT: "documentSymbolProvider": true, +// CHECK:STDOUT: "textDocumentSync": 2 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 504{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "method": "textDocument/publishDiagnostics", +// CHECK:STDOUT: "params": { +// CHECK:STDOUT: "diagnostics": [ +// CHECK:STDOUT: { +// CHECK:STDOUT: "message": "`Core.UInt` implicitly referenced here, but package `Core` not found", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 15, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "severity": 1, +// CHECK:STDOUT: "source": "carbon" +// CHECK:STDOUT: } +// CHECK:STDOUT: ], +// CHECK:STDOUT: "uri": "file:///choice.carbon" +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 465{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 2, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": [ +// CHECK:STDOUT: { +// CHECK:STDOUT: "kind": 10, +// CHECK:STDOUT: "name": "Colors", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 1, +// CHECK:STDOUT: "line": 4 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "selectionRange": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 13, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 7, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: ] +// CHECK:STDOUT: }Content-Length: 51{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 3, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": null +// CHECK:STDOUT: } diff --git a/toolchain/language_server/testdata/document_symbol/fn_definition.carbon b/toolchain/language_server/testdata/document_symbol/fn_definition.carbon new file mode 100644 index 000000000000..78563f86f578 --- /dev/null +++ b/toolchain/language_server/testdata/document_symbol/fn_definition.carbon @@ -0,0 +1,177 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/language_server/testdata/document_symbol/fn_definition.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/language_server/testdata/document_symbol/fn_definition.carbon + +// --- fn_definition.carbon +fn None(); +fn Braced() {} +fn Terse() => 42; +fn Builtin() = "int.make_type_32"; + +// --- STDIN +[[@LSP-CALL:initialize]] +[[@LSP-NOTIFY:textDocument/didOpen: + "textDocument": { + "uri": "file:/fn_definition.carbon", + "languageId": "carbon", + "text": "FROM_FILE_SPLIT" + } +]] +[[@LSP-CALL:textDocument/documentSymbol: + "textDocument": {"uri": "file:/fn_definition.carbon"} +]] +[[@LSP-CALL:shutdown]] +[[@LSP-NOTIFY:exit]] + +// CHECK:STDOUT: Content-Length: 146{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 1, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": { +// CHECK:STDOUT: "capabilities": { +// CHECK:STDOUT: "documentSymbolProvider": true, +// CHECK:STDOUT: "textDocumentSync": 2 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 482{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "method": "textDocument/publishDiagnostics", +// CHECK:STDOUT: "params": { +// CHECK:STDOUT: "diagnostics": [ +// CHECK:STDOUT: { +// CHECK:STDOUT: "message": "semantics TODO: `HandleTerseBodyArrow`", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 13, +// CHECK:STDOUT: "line": 2 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 11, +// CHECK:STDOUT: "line": 2 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "severity": 1, +// CHECK:STDOUT: "source": "carbon" +// CHECK:STDOUT: } +// CHECK:STDOUT: ], +// CHECK:STDOUT: "uri": "file:///fn_definition.carbon" +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 1706{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 2, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": [ +// CHECK:STDOUT: { +// CHECK:STDOUT: "kind": 12, +// CHECK:STDOUT: "name": "None", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 10, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "selectionRange": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 7, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 3, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: { +// CHECK:STDOUT: "kind": 12, +// CHECK:STDOUT: "name": "Braced", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 14, +// CHECK:STDOUT: "line": 1 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 1 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "selectionRange": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 9, +// CHECK:STDOUT: "line": 1 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 3, +// CHECK:STDOUT: "line": 1 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: { +// CHECK:STDOUT: "kind": 12, +// CHECK:STDOUT: "name": "Terse", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 17, +// CHECK:STDOUT: "line": 2 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 2 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "selectionRange": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 8, +// CHECK:STDOUT: "line": 2 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 3, +// CHECK:STDOUT: "line": 2 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: { +// CHECK:STDOUT: "kind": 12, +// CHECK:STDOUT: "name": "Builtin", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 34, +// CHECK:STDOUT: "line": 3 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 3 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "selectionRange": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 10, +// CHECK:STDOUT: "line": 3 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 3, +// CHECK:STDOUT: "line": 3 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: ] +// CHECK:STDOUT: }Content-Length: 51{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 3, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": null +// CHECK:STDOUT: } diff --git a/toolchain/language_server/testdata/document_symbol/incomplete.carbon b/toolchain/language_server/testdata/document_symbol/incomplete.carbon new file mode 100644 index 000000000000..124afee088fe --- /dev/null +++ b/toolchain/language_server/testdata/document_symbol/incomplete.carbon @@ -0,0 +1,148 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/language_server/testdata/document_symbol/incomplete.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/language_server/testdata/document_symbol/incomplete.carbon + +// --- incomplete.carbon +class Incomplete { + fn Foo() { + +// --- STDIN +[[@LSP-CALL:initialize]] +[[@LSP-NOTIFY:textDocument/didOpen: + "textDocument": { + "uri": "file:/incomplete.carbon", + "languageId": "carbon", + "text": "FROM_FILE_SPLIT" + } +]] +[[@LSP-CALL:textDocument/documentSymbol: + "textDocument": {"uri": "file:/incomplete.carbon"} +]] +[[@LSP-CALL:shutdown]] +[[@LSP-NOTIFY:exit]] + +// CHECK:STDOUT: Content-Length: 146{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 1, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": { +// CHECK:STDOUT: "capabilities": { +// CHECK:STDOUT: "documentSymbolProvider": true, +// CHECK:STDOUT: "textDocumentSync": 2 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 1552{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "method": "textDocument/publishDiagnostics", +// CHECK:STDOUT: "params": { +// CHECK:STDOUT: "diagnostics": [ +// CHECK:STDOUT: { +// CHECK:STDOUT: "message": "opening symbol without a corresponding closing symbol", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 18, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 17, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "severity": 1, +// CHECK:STDOUT: "source": "carbon" +// CHECK:STDOUT: }, +// CHECK:STDOUT: { +// CHECK:STDOUT: "message": "`class` declarations must either end with a `;` or have a `{ ... }` block for a definition", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 18, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 17, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "severity": 1, +// CHECK:STDOUT: "source": "carbon" +// CHECK:STDOUT: }, +// CHECK:STDOUT: { +// CHECK:STDOUT: "message": "opening symbol without a corresponding closing symbol", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 12, +// CHECK:STDOUT: "line": 1 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 11, +// CHECK:STDOUT: "line": 1 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "severity": 1, +// CHECK:STDOUT: "source": "carbon" +// CHECK:STDOUT: }, +// CHECK:STDOUT: { +// CHECK:STDOUT: "message": "semantics TODO: `handle invalid parse trees in `check``", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 18, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "severity": 1, +// CHECK:STDOUT: "source": "carbon" +// CHECK:STDOUT: } +// CHECK:STDOUT: ], +// CHECK:STDOUT: "uri": "file:///incomplete.carbon" +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 469{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 2, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": [ +// CHECK:STDOUT: { +// CHECK:STDOUT: "kind": 5, +// CHECK:STDOUT: "name": "Incomplete", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 12, +// CHECK:STDOUT: "line": 1 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "selectionRange": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 16, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 6, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: ] +// CHECK:STDOUT: }Content-Length: 51{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 3, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": null +// CHECK:STDOUT: } diff --git a/toolchain/language_server/testdata/document_symbol/namespace.carbon b/toolchain/language_server/testdata/document_symbol/namespace.carbon new file mode 100644 index 000000000000..48031e5ff98e --- /dev/null +++ b/toolchain/language_server/testdata/document_symbol/namespace.carbon @@ -0,0 +1,112 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/language_server/testdata/document_symbol/namespace.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/language_server/testdata/document_symbol/namespace.carbon + +// --- namespace.carbon +namespace Foo; + +fn Bar() {} + +// --- STDIN +[[@LSP-CALL:initialize]] +[[@LSP-NOTIFY:textDocument/didOpen: + "textDocument": { + "uri": "file:/namespace.carbon", + "languageId": "carbon", + "text": "FROM_FILE_SPLIT" + } +]] +[[@LSP-CALL:textDocument/documentSymbol: + "textDocument": {"uri": "file:/namespace.carbon"} +]] +[[@LSP-CALL:shutdown]] +[[@LSP-NOTIFY:exit]] + +// CHECK:STDOUT: Content-Length: 146{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 1, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": { +// CHECK:STDOUT: "capabilities": { +// CHECK:STDOUT: "documentSymbolProvider": true, +// CHECK:STDOUT: "textDocumentSync": 2 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 149{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "method": "textDocument/publishDiagnostics", +// CHECK:STDOUT: "params": { +// CHECK:STDOUT: "diagnostics": [], +// CHECK:STDOUT: "uri": "file:///namespace.carbon" +// CHECK:STDOUT: } +// CHECK:STDOUT: }Content-Length: 874{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 2, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": [ +// CHECK:STDOUT: { +// CHECK:STDOUT: "kind": 3, +// CHECK:STDOUT: "name": "Foo", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 14, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "selectionRange": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 13, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 10, +// CHECK:STDOUT: "line": 0 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: { +// CHECK:STDOUT: "kind": 12, +// CHECK:STDOUT: "name": "Bar", +// CHECK:STDOUT: "range": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 11, +// CHECK:STDOUT: "line": 2 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 0, +// CHECK:STDOUT: "line": 2 +// CHECK:STDOUT: } +// CHECK:STDOUT: }, +// CHECK:STDOUT: "selectionRange": { +// CHECK:STDOUT: "end": { +// CHECK:STDOUT: "character": 6, +// CHECK:STDOUT: "line": 2 +// CHECK:STDOUT: }, +// CHECK:STDOUT: "start": { +// CHECK:STDOUT: "character": 3, +// CHECK:STDOUT: "line": 2 +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: ] +// CHECK:STDOUT: }Content-Length: 51{{\r}} +// CHECK:STDOUT: {{\r}} +// CHECK:STDOUT: { +// CHECK:STDOUT: "id": 3, +// CHECK:STDOUT: "jsonrpc": "2.0", +// CHECK:STDOUT: "result": null +// CHECK:STDOUT: }