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 {
|
pub fn new(project: ProjectAlias, module: ModulePath, item: ItemName) -> Self {
|
||||||
Self { project, module, item }
|
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.
|
/// Export kind — generic, FE-agnostic.
|
||||||
@ -270,4 +297,28 @@ mod tests {
|
|||||||
assert!(ItemName::new("_Test").is_err());
|
assert!(ItemName::new("_Test").is_err());
|
||||||
assert!(ItemName::new("Te-st").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 {
|
for (alias, project_id) in &step.deps {
|
||||||
if let Some(compiled) = dep_modules.get(project_id) {
|
if let Some(compiled) = dep_modules.get(project_id) {
|
||||||
for (key, _meta) in &compiled.exports {
|
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 = [
|
let synthetic_paths = [
|
||||||
format!("{}/{}", alias, key.module_path),
|
format!("{}/{}", alias, key.module_path),
|
||||||
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::lowering::core_to_vm;
|
||||||
use crate::common::spans::FileId;
|
use crate::common::spans::FileId;
|
||||||
use frontend_api::traits::{Frontend as CanonFrontend, FrontendUnit};
|
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 prometeu_analysis::NameInterner;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@ -59,8 +59,36 @@ impl CanonFrontend for PbsFrontendAdapter {
|
|||||||
return FrontendUnit { diagnostics: diags, imports: vec![], exports: vec![], lowered_ir: LoweredIr::default() };
|
return FrontendUnit { diagnostics: diags, imports: vec![], exports: vec![], lowered_ir: LoweredIr::default() };
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Collect canonical imports from resolver.imported_symbols if/when available
|
// Collect canonical imports from AST imports: `from` must be "@alias:module" and items single identifiers
|
||||||
let imports = Vec::new();
|
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)
|
// Prepare canonical exports from symbol tables (names only, kind best-effort)
|
||||||
let mut exports: Vec<ExportItem> = Vec::new();
|
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))
|
// Usar o binding real do import para este Service (ex.: Log -> (sdk, log))
|
||||||
let obj_name_str = obj_name.to_string();
|
let obj_name_str = obj_name.to_string();
|
||||||
if let Some((dep_alias, module_path)) = self.import_bindings.get(&obj_name_str).cloned() {
|
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
|
// Determine the canonical module origin used when we synthesized dependency symbols
|
||||||
// We support both styles: "alias/module" and "@alias:module"
|
// Only supported style: "@alias:module"
|
||||||
let synthetic_paths = [
|
let canonical_origin = format!("@{}:{}", dep_alias, module_path);
|
||||||
format!("{}/{}", dep_alias, module_path),
|
|
||||||
format!("@{}:{}", dep_alias, module_path),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Find candidates among imported value symbols matching:
|
// Find candidates among imported value symbols matching:
|
||||||
// - name in the new canonical form: "Service.member#sigN" (prefix match on qualified base)
|
// - 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));
|
let matches_legacy = sname.starts_with(&format!("{}#sig", member_name));
|
||||||
if matches_qualified || matches_legacy {
|
if matches_qualified || matches_legacy {
|
||||||
if let Some(orig) = &s.origin {
|
if let Some(orig) = &s.origin {
|
||||||
if synthetic_paths.iter().any(|p| p == orig) {
|
if *orig == canonical_origin {
|
||||||
candidates.push(s);
|
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)
|
## PR-03.03 — Canonical import syntax → `ImportRef` (no dual styles)
|
||||||
|
|
||||||
### Title
|
### Title
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user