SolidLSP: admit Struct, Interface and Constant symbols as reference containers (#1831)

find_referencing_symbols degraded to file-level attribution for any
reference located inside a Go struct body, interface body or const
group: request_containing_symbol found no admissible container, so
request_referencing_symbols logged "Could not find containing symbol
... Returning file symbol instead" and fell back to the file symbol,
losing the answer the tool exists to give.

Root cause: the candidate-container filter admitted only Python's
container kinds {Method, Function, Class} (plus Variable via the
one-liner-exempt path). The language server is not at fault:
hierarchicalDocumentSymbolSupport is declared and the DocumentSymbol
tree is complete, but Go structs arrive as SymbolKind.Struct,
interfaces as Interface and const-group members as Constant - none of
which were admitted, so every candidate was rejected.

Fix, mirroring the existing structure of the filter:
- Struct and Interface join the multi-line container whitelist,
- Constant joins Variable on the one-liner-exempt path (const-group
  members are one-liners by nature, exactly like the module-level
  variables that path already serves).

The Python suite shows no attribution change
(test/solidlsp/python/test_symbol_retrieval.py: 80 passed). Languages
whose containers use the added kinds (e.g. Rust and C structs,
TypeScript interfaces) get the same admission; verified for Go and
Python only.

Regression coverage: new Go fixture containment_sample.go with a
const-iota group, a struct field and an interface method all
referencing a named type; four tests assert containing-symbol and
referencing-symbol attribution, and all four fail against the
pre-change ls.py (verified). Full go-marker suite: 21 passed,
1 skipped.
This commit is contained in:
xor authored and GitHub committed 2026-08-09 16:07:20 +02:00
1 parent bdc9a2de2d
commit c9f0ab80f1
4 files changed
+101 -7

No files matched your search

+17 -6
View File
@@ -2533,8 +2533,9 @@ class SolidLanguageServer(ABC):
) -> ls_types.UnifiedSymbolInformation | None:
"""
Finds the first symbol containing the position for the given file.
For Python, container symbols are considered to be those with kinds corresponding to
functions, methods, or classes (typically: Function (12), Method (6), Class (5)).
Container symbols are those with kinds corresponding to functions, methods, classes, structs
and interfaces, as well as variables and constants (which may be one-liners, e.g. members
of a Go ``const`` group).
The method operates as follows:
- Request the document symbols for the file.
@@ -2546,7 +2547,7 @@ class SolidLanguageServer(ABC):
among those above the given line.
- If no container candidates are found, return None.
:param relative_file_path: The relative path to the Python file.
:param relative_file_path: The relative path to the file.
:param line: The 0-indexed line number.
:param column: The 0-indexed column (also called character). If not passed, the lookup will be based
only on the line.
@@ -2589,8 +2590,15 @@ class SolidLanguageServer(ABC):
location["relativePath"] = relative_file_path
location["uri"] = Path(absolute_file_path).as_uri()
# Allowed container kinds, currently only for Python
container_symbol_kinds = {ls_types.SymbolKind.Method, ls_types.SymbolKind.Function, ls_types.SymbolKind.Class}
# allowed container kinds; Struct and Interface cover languages (e.g. Go, Rust, C) whose
# containers are not reported as classes
container_symbol_kinds = {
ls_types.SymbolKind.Method,
ls_types.SymbolKind.Function,
ls_types.SymbolKind.Class,
ls_types.SymbolKind.Struct,
ls_types.SymbolKind.Interface,
}
def is_position_in_range(line: int, range_d: ls_types.Range) -> bool:
start = range_d["start"]
@@ -2613,7 +2621,10 @@ class SolidLanguageServer(ABC):
for s in document_symbols.iter_symbols()
if s["kind"] in container_symbol_kinds and s["location"]["range"]["start"]["line"] != s["location"]["range"]["end"]["line"]
]
var_containers = [s for s in document_symbols.iter_symbols() if s["kind"] == ls_types.SymbolKind.Variable]
# variables and constants are admitted even as one-liners (e.g. members of a Go const group)
var_containers = [
s for s in document_symbols.iter_symbols() if s["kind"] in {ls_types.SymbolKind.Variable, ls_types.SymbolKind.Constant}
]
candidate_containers.extend(var_containers)
if not candidate_containers: