pr 03.03
This commit is contained in:
parent
df8c0db6eb
commit
2ba8507284
@ -125,6 +125,33 @@ impl ImportRef {
|
||||
pub fn new(project: ProjectAlias, module: ModulePath, item: ItemName) -> Self {
|
||||
Self { project, module, item }
|
||||
}
|
||||
|
||||
/// Parse a PBS-style import `from` string and an `item` into an `ImportRef`.
|
||||
///
|
||||
/// Expected `from_str` format: "@<alias>:<module_path>", e.g., "@sdk:input/testing".
|
||||
/// `item_str` must be a single symbol name like "Test" or "ServiceName".
|
||||
pub fn parse_pbs_import<S1: AsRef<str>, S2: AsRef<str>>(from_str: S1, item_str: S2) -> Result<Self, CanonError> {
|
||||
let (project, module) = parse_pbs_from_string(from_str)?;
|
||||
let item = ItemName::new(item_str)?;
|
||||
Ok(ImportRef { project, module, item })
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the PBS `from` string in the canonical format `@<alias>:<module_path>`.
|
||||
/// Returns `(ProjectAlias, ModulePath)` or a `CanonError` if invalid.
|
||||
pub fn parse_pbs_from_string<S: AsRef<str>>(from_str: S) -> Result<(ProjectAlias, ModulePath), CanonError> {
|
||||
let s = from_str.as_ref().trim();
|
||||
// Must start with '@' and contain a ':' separating alias and module path
|
||||
let rest = s.strip_prefix('@').ok_or(CanonError::InvalidStart("ImportFrom"))?;
|
||||
let mut parts = rest.splitn(2, ':');
|
||||
let alias = parts.next().unwrap_or("");
|
||||
let module = parts.next().unwrap_or("");
|
||||
if alias.is_empty() || module.is_empty() {
|
||||
return Err(CanonError::Empty("ImportFrom"));
|
||||
}
|
||||
let project = ProjectAlias::new(alias)?;
|
||||
let module = ModulePath::parse(module)?;
|
||||
Ok((project, module))
|
||||
}
|
||||
|
||||
/// Export kind — generic, FE-agnostic.
|
||||
@ -270,4 +297,28 @@ mod tests {
|
||||
assert!(ItemName::new("_Test").is_err());
|
||||
assert!(ItemName::new("Te-st").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_pbs_from_string_valid_and_normalization() {
|
||||
let (proj, module) = parse_pbs_from_string("@sdk:input/testing").unwrap();
|
||||
assert_eq!(proj.as_str(), "sdk");
|
||||
assert_eq!(module.as_str(), "input/testing");
|
||||
|
||||
// Normalization of module path
|
||||
let (_, module2) = parse_pbs_from_string("@sdk:/Input/./Testing/").unwrap();
|
||||
assert_eq!(module2.as_str(), "input/testing");
|
||||
|
||||
// Windows-style separators normalize
|
||||
let (_, module3) = parse_pbs_from_string("@abc:foo\\bar").unwrap();
|
||||
assert_eq!(module3.as_str(), "foo/bar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_pbs_from_string_invalid_forms() {
|
||||
assert!(parse_pbs_from_string("").is_err());
|
||||
assert!(parse_pbs_from_string("sdk:input/testing").is_err()); // missing '@'
|
||||
assert!(parse_pbs_from_string("@Sdk:input/testing").is_err()); // invalid alias
|
||||
assert!(parse_pbs_from_string("@sdk:").is_err()); // empty module
|
||||
assert!(parse_pbs_from_string("@sdk:..").is_err()); // parent segments forbidden
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,6 +141,8 @@ pub fn compile_project(
|
||||
for (alias, project_id) in &step.deps {
|
||||
if let Some(compiled) = dep_modules.get(project_id) {
|
||||
for (key, _meta) in &compiled.exports {
|
||||
// Track both legacy and canonical forms ONLY for collision detection between deps and local modules.
|
||||
// Import resolution uses only '@alias:module' elsewhere.
|
||||
let synthetic_paths = [
|
||||
format!("{}/{}", alias, key.module_path),
|
||||
format!("@{}:{}", alias, key.module_path),
|
||||
|
||||
@ -2,7 +2,7 @@ use crate::frontends::pbs::{parser::Parser, SymbolCollector, ModuleSymbols, Reso
|
||||
use crate::lowering::core_to_vm;
|
||||
use crate::common::spans::FileId;
|
||||
use frontend_api::traits::{Frontend as CanonFrontend, FrontendUnit};
|
||||
use frontend_api::types::{Diagnostic as CanonDiagnostic, Severity as CanonSeverity, ExportItem, ExportKind, ItemName, LoweredIr};
|
||||
use frontend_api::types::{Diagnostic as CanonDiagnostic, Severity as CanonSeverity, ExportItem, ExportKind, ItemName, LoweredIr, ImportRef, parse_pbs_from_string};
|
||||
use prometeu_analysis::NameInterner;
|
||||
use std::path::Path;
|
||||
|
||||
@ -59,8 +59,36 @@ impl CanonFrontend for PbsFrontendAdapter {
|
||||
return FrontendUnit { diagnostics: diags, imports: vec![], exports: vec![], lowered_ir: LoweredIr::default() };
|
||||
}
|
||||
|
||||
// TODO: Collect canonical imports from resolver.imported_symbols if/when available
|
||||
let imports = Vec::new();
|
||||
// Collect canonical imports from AST imports: `from` must be "@alias:module" and items single identifiers
|
||||
let mut imports: Vec<ImportRef> = Vec::new();
|
||||
if let crate::frontends::pbs::ast::NodeKind::File(file_node) = parsed.arena.kind(parsed.root) {
|
||||
for imp_id in &file_node.imports {
|
||||
if let crate::frontends::pbs::ast::NodeKind::Import(imp) = parsed.arena.kind(*imp_id) {
|
||||
// Parse project/module from `from` string
|
||||
match parse_pbs_from_string(&imp.from) {
|
||||
Ok((project, module)) => {
|
||||
// Resolve item names from spec node
|
||||
if let crate::frontends::pbs::ast::NodeKind::ImportSpec(spec) = parsed.arena.kind(imp.spec) {
|
||||
for &name_id in &spec.path {
|
||||
let name_str = interner.resolve(name_id);
|
||||
match ItemName::new(name_str) {
|
||||
Ok(item) => imports.push(ImportRef::new(project.clone(), module.clone(), item)),
|
||||
Err(e) => diags.push(CanonDiagnostic::error(format!(
|
||||
"Invalid import item '{}': {}",
|
||||
name_str, e
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => diags.push(CanonDiagnostic::error(format!(
|
||||
"Invalid import path '{}': {}",
|
||||
&imp.from, e
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare canonical exports from symbol tables (names only, kind best-effort)
|
||||
let mut exports: Vec<ExportItem> = Vec::new();
|
||||
|
||||
@ -1322,12 +1322,9 @@ impl<'a> Lowerer<'a> {
|
||||
// Usar o binding real do import para este Service (ex.: Log -> (sdk, log))
|
||||
let obj_name_str = obj_name.to_string();
|
||||
if let Some((dep_alias, module_path)) = self.import_bindings.get(&obj_name_str).cloned() {
|
||||
// Determine the synthetic module path used when we synthesized dependency symbols
|
||||
// We support both styles: "alias/module" and "@alias:module"
|
||||
let synthetic_paths = [
|
||||
format!("{}/{}", dep_alias, module_path),
|
||||
format!("@{}:{}", dep_alias, module_path),
|
||||
];
|
||||
// Determine the canonical module origin used when we synthesized dependency symbols
|
||||
// Only supported style: "@alias:module"
|
||||
let canonical_origin = format!("@{}:{}", dep_alias, module_path);
|
||||
|
||||
// Find candidates among imported value symbols matching:
|
||||
// - name in the new canonical form: "Service.member#sigN" (prefix match on qualified base)
|
||||
@ -1342,7 +1339,7 @@ impl<'a> Lowerer<'a> {
|
||||
let matches_legacy = sname.starts_with(&format!("{}#sig", member_name));
|
||||
if matches_qualified || matches_legacy {
|
||||
if let Some(orig) = &s.origin {
|
||||
if synthetic_paths.iter().any(|p| p == orig) {
|
||||
if *orig == canonical_origin {
|
||||
candidates.push(s);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,52 +6,6 @@
|
||||
|
||||
---
|
||||
|
||||
## PR-03.02 — Ban PBS leakage at the BE boundary (dependency & import hygiene)
|
||||
|
||||
### Title
|
||||
|
||||
Remove PBS imports from BE layers and enforce `frontend-api` boundary
|
||||
|
||||
### Briefing / Context
|
||||
|
||||
BE code currently imports PBS modules (symbols, typed builder, etc.) from `prometeu-compiler`. This is the leak that makes the system unmaintainable and creates accidental coupling. We must ensure BE only depends on `frontend-api` outputs.
|
||||
|
||||
### Target
|
||||
|
||||
* BE layers (`building/*`, `sources.rs`, orchestrator/linker paths) **must not import PBS modules**.
|
||||
* Any FE-specific logic is moved behind the `Frontend` implementation.
|
||||
|
||||
### Scope
|
||||
|
||||
* Replace `use crate::frontends::pbs::*` imports in BE files with `frontend-api` types.
|
||||
* Add a simple compile-time guard:
|
||||
|
||||
* Option A: a `deny`/lint via `mod` separation + no re-exports.
|
||||
* Option B: create `crates/prometeu-compiler-backend` module that does not depend on `pbs` module.
|
||||
* Identify and remove PBS-specific helper calls inside BE (e.g., `build_typed_module_symbols` from BE).
|
||||
|
||||
### Out of scope
|
||||
|
||||
* Fixing overload resolution itself (handled in later PRs).
|
||||
|
||||
### Checklist
|
||||
|
||||
* [ ] Update imports in BE files.
|
||||
* [ ] Remove PBS type references from BE data structures.
|
||||
* [ ] Ensure build compiles without BE → PBS direct dependency.
|
||||
* [ ] Add a “boundary test”: a module that `use`s backend and fails to compile if PBS is required (or a CI check script).
|
||||
|
||||
### Tests
|
||||
|
||||
* `cargo test -p prometeu-compiler`
|
||||
* `cargo test --workspace`
|
||||
|
||||
### Risk
|
||||
|
||||
Medium. Refactor touches build/orchestrator wiring.
|
||||
|
||||
---
|
||||
|
||||
## PR-03.03 — Canonical import syntax → `ImportRef` (no dual styles)
|
||||
|
||||
### Title
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user