prometeu-runtime/files/LPS - prep.md
2026-03-24 13:40:27 +00:00

9.3 KiB

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.
  1. 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.
  1. Trocar Symbol.module: u32ModuleId.

  2. 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)
  1. 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.
  1. 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.
  1. 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)
  1. 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
  1. 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)
  1. 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.
  1. 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 }
  1. 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.
  1. 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