Add PBS find-references as the request-local inverse of definition identity on FrontendLanguageService. Usage sites are identifier tokens whose definition locations intersect the cursor set. Empty results stay honest when definition is empty, including virtual stdlib paths. Honor includeDeclaration in the LSP bridge. Housekeep DSC-0040 with LSN-0069.
6.2 KiB
| id | ticket | title | created | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| LSN-0069 | pbs-lsp-find-references | PBS find-references is the physical inverse of definition | 2026-09-21 |
|
PBS find-references is the physical inverse of definition
Original Problem
PBS already had compiler-backed completion, hover, signature help, and go-to-definition (LSN-0051, LSN-0068). Users still could not ask where a resolved symbol is used. Text search confuses overloads, shadowing, members, comments, and coincidental names. Rename (AGD-0046) and hierarchy (AGD-0056) need an honest usage list before they exist.
The risk was inventing a SymbolId / RefIndex, waiting for snapshot cache (AGD-0051), or listing uses of stdlib APIs whose declarations are still virtual paths.
Consolidated Decision
PBS Find References is the LSP projection of physical usage sites of the same identity definition already returns.
Durable locks from DEC-0053 (AGD-0043 Q1–Q6 = A):
- Resolve the cursor through the definition path (locals, parameters, members, imports/supplementals, current-module top-level). Identity is that physical declaration-location set. Do not text-search. Do not add
SymbolIdor a persistentRefIndex. - A site is a usage when its definition locations intersect the cursor identity. Overloads follow that intersection.
- If definition at the same offset is empty, references is empty.
- Walk regular
fileTablefiles from the currentanalyze()surface. Do not scan/virtual/stdlibor other non-regular handles. - Keep the capability on the existing aggregated
FrontendLanguageService. ReuseFrontendDefinitionLocation. Map PBS spans inprometeu-frontend-pbs. Common LSP must not importp.studio.compiler.pbs.*. No References SPI. - Honor LSP
includeDeclarationin the server: union usage sites with definition locations when the host asks and the declaration is a regular file. No read/write classification. - Advertise
referencesProvider. Partial coverage is valid. Analysis stays request-local. This discussion does not build rename, hierarchy, workspace symbols, virtual documents, or snapshot cache.
Spec 23 §8.3 now also states: a references location must identify a compiler-known physical source file; empty definition implies empty references; includeDeclaration must not invent a virtual location.
Final Implementation
| Layer | What landed |
|---|---|
| PBS editorial | PbsEditorialSupportService.references walks IDENTIFIER tokens and keeps sites whose definition locations intersect the cursor identity (FileId + start + end). |
| Generic contract | FrontendLanguageService.references defaults to List.of(). FrontendEditorialContext.projectSurface carries the PBS semantic read surface so the walk can see astByFile. |
| PBS mapping | PBSFrontendLanguageService requires a non-empty physical definition set, skips non-regular files, and maps surviving spans through the same Files.isRegularFile helper as definition. |
| LSP | referencesProvider, textDocument/references. The bridge applies includeDeclaration by unioning references with definition. Compile-only frontends stay empty. |
| VS Code | Unchanged thin client. |
Stdlib and SDK modules remain virtual (/virtual/stdlib/...). Shift+F12 on Gfx.clear is empty, matching F12. Hover is unchanged.
Local binding names are not usage sites: definition at let total is empty because the name is not yet in scope. The use return total matches. includeDeclaration adds the declaration span from definition, not a second name search.
Examples
Same-file function uses, including the declaring identifier (top-level names resolve to themselves):
fn helper() -> int { return 42; }
fn frame() -> void {
helper();
helper();
// helper // not a reference
let text = "helper"; // not a reference
}
Imported project (or otherwise physical) symbols collect uses in the importing file. Identity is the foreign declaration span.
import { Log as Logger } from @sdk:log;
Logger.info("a");
Logger.info("b");
If @sdk:log is only a virtual handle, definition is empty, so references is empty.
Builtins stay hover-only:
let n: int = 1; // hover works; references is empty
includeDeclaration=true adds the canonical declaration location on top of identifier usage sites. false returns usages only.
Pitfalls
Do not implement find-references as workspace text search in lsp-v1 or in the VS Code extension. It will lie about overloads, members, and imports, then poison rename.
Do not add a SymbolId index “for rename later”. Definition already gave the key: physical declaration locations. Rename can consume this list once it has its own decision.
Do not wait for snapshot cache. The LSP already reanalyzes on each editorial request. Snapshots (AGD-0051) can make the walk cheaper without changing the contract.
Do not treat empty stdlib references as a missing feature. Virtual paths are not regular files. Empty is the honest result until those sources live on disk.
Do not expect let total to appear as a usage of total. Inverse-of-definition only keeps identifier sites that themselves resolve. The declaration span arrives through includeDeclaration.
Do not put includeDeclaration into PBS types. It is a host flag applied in the LSP bridge.
Do not scan /virtual/stdlib to “find more uses”. That reopens virtual documents.
Do not split a References SPI “for future languages”. PBS is a real consumer of FrontendLanguageService (LSN-0067).
Do not treat this lesson as closing rename, snapshots, workspace symbols, document links, or hierarchy.
References
- Agenda:
AGD-0043(Q1–Q6 = A) - Decision:
DEC-0053 - Plan:
PLN-0132 - Spec 23 §8.3 — optional navigation; physical-file-or-empty; empty definition implies empty references
LSN-0068— definition identity this feature invertsLSN-0051— wave 1 deferred navigationLSN-0058— genericFrontendLanguageServicefor LSPLSN-0047— protocol stays inlsp-v1; compiler owns semanticsLSN-0067— no SPI without a real in-repo consumer- Still open:
AGD-0046rename,AGD-0045workspace symbols,AGD-0051snapshots,AGD-0055document links,AGD-0056hierarchy