302 lines
9.3 KiB
Markdown
302 lines
9.3 KiB
Markdown
## PR-R1 — IDs padronizados (newtypes) em um único lugar
|
|
|
|
**Branch:** `pr-r1-ids-newtypes`
|
|
|
|
### Briefing
|
|
|
|
Hoje existem IDs espalhados entre crates (`FileId`, `NameId`, `NodeId`, `SymbolId`, `TypeId`) e alguns campos ainda usam `u32`/`usize` cru (ex.: `Symbol.module: u32`). Para LSP, precisamos de IDs consistentes para indexação, caches, spans e cross-crate APIs.
|
|
|
|
### Alvo
|
|
|
|
Centralizar e padronizar os seguintes IDs (newtypes):
|
|
|
|
* `FileId(u32)`
|
|
* `NodeId(u32)`
|
|
* `NameId(u32)`
|
|
* `SymbolId(u32)`
|
|
* `TypeId(u32)`
|
|
* `ModuleId(u32)`
|
|
* `ProjectId(u32)` *(ver PR-R4 para adoção total; aqui é apenas definição + plumbing mínimo se necessário)*
|
|
|
|
**Definição única** em `prometeu-analysis` (ou um crate novo `prometeu-ids`, se você preferir isolar):
|
|
|
|
* Arquivo sugerido: `crates/prometeu-analysis/src/ids.rs`
|
|
* Exportar via `pub mod ids; pub use ids::*;`
|
|
|
|
### Escopo / Mudanças
|
|
|
|
1. **Criar o módulo de IDs** com:
|
|
|
|
* `#[repr(transparent)] pub struct FileId(pub u32);` etc.
|
|
* `Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Debug`.
|
|
* Helpers:
|
|
|
|
* `impl FileId { pub const INVALID: FileId = FileId(u32::MAX); }` (opcional)
|
|
* `impl From<u32> for FileId` e `From<FileId> for u32`.
|
|
|
|
2. **Padronizar uso cross-crate**:
|
|
|
|
* `prometeu-compiler/frontends/pbs/ast`: trocar `NodeId` local para `prometeu_analysis::NodeId`.
|
|
* `prometeu-compiler/analysis/symbols`: trocar `SymbolId` local para `prometeu_analysis::SymbolId`.
|
|
* `prometeu-compiler/analysis/types`: trocar `TypeId` local para `prometeu_analysis::TypeId`.
|
|
* Onde houver `usize`/`u32` cru representando file/module/symbol/type/node: substituir.
|
|
|
|
3. **Trocar `Symbol.module: u32` → `ModuleId`**.
|
|
|
|
4. **Interner (`NameId`)**:
|
|
|
|
* Garantir que o interner existente retorna `NameId` do módulo unificado.
|
|
* Se existirem `NameId` duplicados em crates diferentes, remover e apontar para o único.
|
|
|
|
### Regras de compatibilidade (para não quebrar tudo de uma vez)
|
|
|
|
* Se algum ponto ainda depende de `usize`, oferecer funções auxiliares **temporárias**:
|
|
|
|
* `fn as_usize(self) -> usize` (somente se realmente necessário)
|
|
* Preferir converter na borda (ex.: índices de `Vec`).
|
|
|
|
### Testes de aceite
|
|
|
|
* `cargo test -q` no workspace.
|
|
* Teste unitário novo em `prometeu-analysis`:
|
|
|
|
* `ids_are_repr_transparent_and_hashable()` (checa `size_of::<FileId>() == 4` etc.).
|
|
* Teste de compilação indireto: build de `prometeu-compiler` sem warnings de tipos duplicados.
|
|
|
|
### Notas de implementação
|
|
|
|
* Evitar circular dependency: `prometeu-analysis` deve ser “baixo nível”. Se o compiler já depende dele, ok.
|
|
* Se `prometeu-analysis` não puder depender do compiler (não deve), manter IDs neutros e reutilizáveis.
|
|
|
|
---
|
|
|
|
## PR-R2 — Span unificado + FileId consistente em todo pipeline
|
|
|
|
**Branch:** `pr-r2-span-unify`
|
|
|
|
### Briefing
|
|
|
|
Hoje existem dois tipos de `Span`:
|
|
|
|
* `prometeu-analysis::Span` (com `FileId`)
|
|
* `prometeu-compiler::common::spans::Span` (com `file_id: usize`)
|
|
|
|
Para LSP, diagnostics/definition/symbols precisam de um único modelo de span para conversão consistente para `Location/Range`.
|
|
|
|
A spec aponta spans como **byte offsets**, `end` exclusivo, e file id deve ser estável. (PBS Implementation Spec / Diagnostic specs)
|
|
|
|
### Alvo
|
|
|
|
* Tornar `prometeu-analysis::Span` o **span canônico** do projeto.
|
|
* Remover/aposentar `prometeu-compiler::common::spans::Span`.
|
|
* Garantir que **todo span carregue `FileId`**, e não `usize`.
|
|
|
|
### Escopo / Mudanças
|
|
|
|
1. **Definir `Span` canônico** (se já existe, reforçar):
|
|
|
|
* `pub struct Span { pub file: FileId, pub start: u32, pub end: u32 }`
|
|
* `start/end` em bytes (u32), `end` exclusivo.
|
|
* Helpers:
|
|
|
|
* `Span::new(file, start, end)`
|
|
* `Span::len()`
|
|
* `Span::contains(byte)`
|
|
|
|
2. **Migrar compiler para usar Span canônico**:
|
|
|
|
* Parser: todos os nós AST devem carregar spans canônicos.
|
|
* Diagnostics: `Diagnostic.span` deve ser canônico.
|
|
* Resolver/Symbols: `Symbol.decl_span` deve ser canônico.
|
|
* RefIndex: deve usar `Span` canônico.
|
|
|
|
3. **Matar o `file_id: usize`**:
|
|
|
|
* Onde havia `usize`, trocar por `FileId`.
|
|
* Nas arenas indexadas por `Vec`, converter no ponto de acesso: `file.0 as usize`.
|
|
|
|
4. **Adapters temporários (se necessário)**
|
|
|
|
* Se houver muitos pontos que esperam o Span antigo, criar `type OldSpan = Span` por 1 PR (somente dentro do compiler), e remover no fim da PR.
|
|
|
|
### Testes de aceite
|
|
|
|
* `cargo test -q` no workspace.
|
|
* Teste novo:
|
|
|
|
* `span_end_is_exclusive()`
|
|
* `diagnostic_span_is_valid_for_file()` (valida `end>=start` e `end<=text.len()` em um fixture simples).
|
|
|
|
### Critérios de “done”
|
|
|
|
* Não existe mais `prometeu-compiler::common::spans::Span` (ou está `deprecated` e sem uso).
|
|
* Qualquer `Span` do pipeline é `prometeu-analysis::Span`.
|
|
|
|
---
|
|
|
|
## PR-R3 — TextIndex/LineIndex correto para LSP (UTF-16) + conversões
|
|
|
|
**Branch:** `pr-r3-text-index-utf16`
|
|
|
|
### Briefing
|
|
|
|
O LSP usa `Position.character` em **UTF-16 code units** (não bytes). Hoje o `LineIndex` calcula coluna como *byte offset* na linha. Em arquivos com Unicode (acentos), diagnostics e goto definition ficam desalinhados.
|
|
|
|
Queremos:
|
|
|
|
* Manter o core do compilador em **byte offsets** (spec).
|
|
* Converter **somente na borda** (LSP e ferramentas).
|
|
|
|
### Alvo
|
|
|
|
Criar um índice de texto (por arquivo) que suporte:
|
|
|
|
* `byte_offset -> (line, utf16_col)`
|
|
* `(line, utf16_col) -> byte_offset`
|
|
|
|
E manter:
|
|
|
|
* `Span` em bytes.
|
|
* O índice baseado no **conteúdo atual** do arquivo.
|
|
|
|
### Escopo / Mudanças
|
|
|
|
1. Introduzir `TextIndex` em `prometeu-analysis` (ou `prometeu-lsp` se você quiser limitar ao LSP; mas recomendo em `analysis` pois será útil para debug map e tooling):
|
|
|
|
* Arquivo sugerido: `crates/prometeu-analysis/src/text_index.rs`
|
|
* Estrutura:
|
|
|
|
* `line_starts: Vec<u32>` (byte offsets)
|
|
* `line_utf16_lens: Vec<u32>` (opcional cache)
|
|
|
|
2. API mínima:
|
|
|
|
* `TextIndex::new(text: &str) -> Self`
|
|
* `fn byte_to_lsp(&self, byte: u32) -> (u32 /*line*/, u32 /*utf16_col*/)`
|
|
* `fn lsp_to_byte(&self, line: u32, utf16_col: u32) -> u32`
|
|
|
|
3. Algoritmo
|
|
|
|
* `line_starts` calculado por varredura de `\n`.
|
|
* Para conversão de col:
|
|
|
|
* pegar o slice da linha (`&text[line_start..line_end]`)
|
|
* iterar `char_indices()`, acumulando:
|
|
|
|
* `byte_pos` e `utf16_count += ch.len_utf16()`
|
|
* parar quando:
|
|
|
|
* `byte_pos >= target_byte` (byte_to_lsp)
|
|
* `utf16_count >= target_utf16` (lsp_to_byte)
|
|
|
|
4. Testes fortes com Unicode
|
|
|
|
* Casos: `"aé🙂b"` (emoji e acento).
|
|
* Validar round-trip:
|
|
|
|
* `byte == lsp_to_byte(byte_to_lsp(byte))` para bytes em fronteira de char.
|
|
|
|
5. Integração
|
|
|
|
* Por enquanto, **não** mexer no LSP server.
|
|
* Apenas oferecer API em `analysis` para o LSP consumir na PR-08.
|
|
|
|
### Testes de aceite
|
|
|
|
* `cargo test -q`.
|
|
* Testes novos em `prometeu-analysis`:
|
|
|
|
* `text_index_ascii_roundtrip()`
|
|
* `text_index_unicode_roundtrip_utf16()`
|
|
|
|
---
|
|
|
|
## PR-R4 — ProjectId padronizado + modelagem de Project/Module estável
|
|
|
|
**Branch:** `pr-r4-project-id`
|
|
|
|
### Briefing
|
|
|
|
Hoje o resolver trabalha com `Project { name, version }` e o `symbols.json` contém projects e símbolos agrupados por projeto. Para LSP e para incremental analysis, queremos IDs estáveis e leves para:
|
|
|
|
* mapear `uri -> FileId -> (ProjectId, ModuleId)`
|
|
* armazenar caches por projeto
|
|
* suportar workspace com múltiplos projetos no futuro
|
|
|
|
Você pediu explicitamente incluir `ProjectId(u32)` nesta série.
|
|
|
|
### Alvo
|
|
|
|
Introduzir `ProjectId(u32)` e plugar no modelo de resolução/linking:
|
|
|
|
* Cada projeto carregado/descoberto no workspace recebe `ProjectId`.
|
|
* Mapas centrais usam `ProjectId` como chave em vez de string.
|
|
|
|
### Escopo / Mudanças
|
|
|
|
1. Definir `ProjectId(u32)` (já definido na PR-R1) e agora **adotar**.
|
|
|
|
2. Criar um registry estável (no analysis/resolver layer):
|
|
|
|
* `ProjectRegistry`:
|
|
|
|
* `by_name: HashMap<ProjectKey, ProjectId>`
|
|
* `projects: Vec<ProjectMeta>`
|
|
* `ProjectKey` pode ser:
|
|
|
|
* `{ name: SmolStr, version: Option<SmolStr> }` ou `{ name, version }`
|
|
|
|
3. Ajustar estruturas existentes para carregar `ProjectId`
|
|
|
|
* `ModuleRef` / `ModulePath` / `ResolvedModule` devem apontar para `ProjectId`.
|
|
* `symbols.json` writer/reader:
|
|
|
|
* Manter `project: "sdk"` no JSON (formato externo), mas internamente mapear para `ProjectId`.
|
|
|
|
4. Integração mínima (sem LSP ainda)
|
|
|
|
* `AnalysisDb` (ou equivalente) deve conseguir responder:
|
|
|
|
* `fn project_for_file(file: FileId) -> ProjectId`
|
|
|
|
### Estratégia para não explodir o diff
|
|
|
|
* Não reescrever o mundo:
|
|
|
|
* manter `ProjectMeta { id: ProjectId, name, version }`
|
|
* adicionar `id` aos lugares críticos (resolver, module index, symbols export)
|
|
|
|
### Testes de aceite
|
|
|
|
* `cargo test -q`.
|
|
* Teste novo:
|
|
|
|
* `project_registry_stable_ids_for_same_key()`
|
|
* `symbols_json_roundtrip_preserves_project_grouping()` (se houver infra de roundtrip)
|
|
|
|
### Critérios de “done”
|
|
|
|
* Nenhum mapa central chaveado por `String` para identificar projeto no core; usar `ProjectId`.
|
|
* Persistência (symbols.json) continua legível e compatível.
|
|
|
|
---
|
|
|
|
# Ordem recomendada de merge (para minimizar conflitos)
|
|
|
|
1. PR-R1 (IDs)
|
|
2. PR-R2 (Span)
|
|
3. PR-R3 (TextIndex)
|
|
4. PR-R4 (ProjectId)
|
|
|
|
> Depois disso, a PR-08 (LSP MVP) fica bem menor: o LSP só consome `Span` + `TextIndex` + IDs.
|
|
|
|
---
|
|
|
|
# Checklist global (pré-PR-08)
|
|
|
|
* [ ] IDs unificados e usados em todos os crates
|
|
* [ ] Span único, sempre com `FileId`, e offsets em bytes
|
|
* [ ] TextIndex com conversão UTF-16 confiável (testado)
|
|
* [ ] ProjectId adotado no resolver/modelo de projeto
|
|
* [ ] Workspace compila e `cargo test` passa
|