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
This commit is contained in:
Richard Smith
2026-07-30 21:35:08 +00:00
committed by GitHub
parent 239ad9fe8b
commit f473ca994a
5 changed files with 574 additions and 14 deletions
@@ -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<Lex::TokenIndex> 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<clang::clangd::DocumentSymbol> {
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;
}
@@ -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: }
@@ -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: }
@@ -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: }
@@ -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: }