Some checks are pending
Intrepid/Prometeu/Studio/pipeline/pr-master Build started...
JaCoCo Coverage #### Project Overview
No changes detected, that affect the code coverage.
* Line Coverage: 62.51% (18192/29102)
* Branch Coverage: 53.15% (7093/13345)
* Lines of Code: 29102
* Cyclomatic Complexity: 11715
#### Quality Gates Summary
Output truncated.
Test / Build skipped: 15, passed: 663
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.
112 lines
6.2 KiB
Markdown
112 lines
6.2 KiB
Markdown
---
|
||
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
|