--- id: LSN-0069 ticket: pbs-lsp-find-references title: PBS find-references is the physical inverse of definition created: 2026-09-21 tags: [studio, lsp, vscode, compiler-pbs, editor, references] --- # 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): 1. 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 `SymbolId` or a persistent `RefIndex`. 2. A site is a usage when its definition locations intersect the cursor identity. Overloads follow that intersection. 3. If definition at the same offset is empty, references is empty. 4. Walk regular `fileTable` files from the current `analyze()` surface. Do not scan `/virtual/stdlib` or other non-regular handles. 5. Keep the capability on the existing aggregated `FrontendLanguageService`. Reuse `FrontendDefinitionLocation`. Map PBS spans in `prometeu-frontend-pbs`. Common LSP must not import `p.studio.compiler.pbs.*`. No References SPI. 6. Honor LSP `includeDeclaration` in the server: union usage sites with definition locations when the host asks and the declaration is a regular file. No read/write classification. 7. 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): ```pbs 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. ```pbs 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: ```pbs 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 inverts - `LSN-0051` — wave 1 deferred navigation - `LSN-0058` — generic `FrontendLanguageService` for LSP - `LSN-0047` — protocol stays in `lsp-v1`; compiler owns semantics - `LSN-0067` — no SPI without a real in-repo consumer - Still open: `AGD-0046` rename, `AGD-0045` workspace symbols, `AGD-0051` snapshots, `AGD-0055` document links, `AGD-0056` hierarchy