From 89163807b49564857f7a3ace8a37eaf867f34e4f Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Wed, 4 Feb 2026 19:36:51 +0000 Subject: [PATCH] pr 04.h --- .../src/analysis/symbols/mod.rs | 10 ++ .../src/frontends/pbs/resolve.rs | 146 +++++++++++++----- test-cartridges/canonical/golden/program.pbc | Bin 3727 -> 3727 bytes 3 files changed, 120 insertions(+), 36 deletions(-) diff --git a/crates/prometeu-compiler/src/analysis/symbols/mod.rs b/crates/prometeu-compiler/src/analysis/symbols/mod.rs index 21d646c6..b8c81f75 100644 --- a/crates/prometeu-compiler/src/analysis/symbols/mod.rs +++ b/crates/prometeu-compiler/src/analysis/symbols/mod.rs @@ -103,6 +103,16 @@ impl DefIndex { pub fn get(&self, key: DefKey) -> Option { self.symbols.get(&key).copied() } + + /// Lookup by name/namespace ignoring module. Returns the first match with its module id. + pub fn get_by_name_any_module(&self, name: NameId, namespace: Namespace) -> Option<(u32, SymbolId)> { + for (k, v) in &self.symbols { + if k.name == name && k.namespace == namespace { + return Some((k.module, *v)); + } + } + None + } } impl RefIndex { diff --git a/crates/prometeu-compiler/src/frontends/pbs/resolve.rs b/crates/prometeu-compiler/src/frontends/pbs/resolve.rs index 2bd80433..62760984 100644 --- a/crates/prometeu-compiler/src/frontends/pbs/resolve.rs +++ b/crates/prometeu-compiler/src/frontends/pbs/resolve.rs @@ -7,6 +7,7 @@ pub fn build_def_index( arena: &AstArena, module: u32, _interner: &NameInterner, + imports: Option<(&SymbolArena, &DefIndex)>, ) -> (SymbolArena, DefIndex, RefIndex, NodeToSymbol, Vec) { let mut symbols = SymbolArena::new(); let mut index = DefIndex::new(); @@ -69,7 +70,7 @@ pub fn build_def_index( // Passo 2: Resolver referências (Identifiers) for &root_id in &arena.roots { - walk_node(root_id, arena, module, &index, &mut ref_index, &mut node_to_symbol, &mut diagnostics); + walk_node(root_id, arena, module, &index, imports, &mut ref_index, &mut node_to_symbol, &mut diagnostics); } (symbols, index, ref_index, node_to_symbol, diagnostics) @@ -80,6 +81,7 @@ fn walk_node( arena: &AstArena, module: u32, index: &DefIndex, + imports: Option<(&SymbolArena, &DefIndex)>, ref_index: &mut RefIndex, node_to_symbol: &mut NodeToSymbol, diagnostics: &mut Vec, @@ -90,29 +92,29 @@ fn walk_node( match kind { NodeKind::File(file) => { for &decl in &file.decls { - walk_node(decl, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(decl, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } } NodeKind::FnDecl(fn_decl) => { - walk_node(fn_decl.body, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(fn_decl.body, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } NodeKind::Block(block) => { for &stmt in &block.stmts { - walk_node(stmt, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(stmt, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } if let Some(tail) = block.tail { - walk_node(tail, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(tail, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } } NodeKind::LetStmt(let_stmt) => { - walk_node(let_stmt.init, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(let_stmt.init, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } NodeKind::ExprStmt(expr_stmt) => { - walk_node(expr_stmt.expr, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(expr_stmt.expr, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } NodeKind::ReturnStmt(ret) => { if let Some(expr) = ret.expr { - walk_node(expr, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(expr, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } } NodeKind::Ident(ident) => { @@ -125,6 +127,27 @@ fn walk_node( if let Some(symbol_id) = index.get(key) { ref_index.record_ref(symbol_id, span); node_to_symbol.bind_node(node_id, symbol_id); + } else if let Some((import_arena, import_index)) = imports { + if let Some((_, symbol_id)) = import_index.get_by_name_any_module(ident.name, Namespace::Value) { + let symbol = import_arena.get(symbol_id); + if !symbol.exported && symbol.module != module { + diagnostics.push(Diagnostic { + level: DiagnosticLevel::Error, + code: Some("E_RESOLVE_VISIBILITY".to_string()), + message: format!("Symbol is not exported from module {}", symbol.module), + span: Some(span), + }); + } + ref_index.record_ref(symbol_id, span); + node_to_symbol.bind_node(node_id, symbol_id); + } else { + diagnostics.push(Diagnostic { + level: DiagnosticLevel::Error, + code: Some("E_RESOLVE_UNDEFINED_IDENTIFIER".to_string()), + message: format!("Undefined identifier"), + span: Some(span), + }); + } } else { diagnostics.push(Diagnostic { level: DiagnosticLevel::Error, @@ -135,68 +158,68 @@ fn walk_node( } } NodeKind::Call(call) => { - walk_node(call.callee, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(call.callee, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); for &arg in &call.args { - walk_node(arg, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(arg, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } } NodeKind::Unary(unary) => { - walk_node(unary.expr, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(unary.expr, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } NodeKind::Binary(binary) => { - walk_node(binary.left, arena, module, index, ref_index, node_to_symbol, diagnostics); - walk_node(binary.right, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(binary.left, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); + walk_node(binary.right, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } NodeKind::Cast(cast) => { - walk_node(cast.expr, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(cast.expr, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } NodeKind::IfExpr(if_expr) => { - walk_node(if_expr.cond, arena, module, index, ref_index, node_to_symbol, diagnostics); - walk_node(if_expr.then_block, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(if_expr.cond, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); + walk_node(if_expr.then_block, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); if let Some(else_block) = if_expr.else_block { - walk_node(else_block, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(else_block, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } } NodeKind::WhenExpr(when_expr) => { for &arm in &when_expr.arms { - walk_node(arm, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(arm, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } } NodeKind::WhenArm(when_arm) => { - walk_node(when_arm.cond, arena, module, index, ref_index, node_to_symbol, diagnostics); - walk_node(when_arm.body, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(when_arm.cond, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); + walk_node(when_arm.body, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } NodeKind::Alloc(alloc) => { - walk_node(alloc.ty, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(alloc.ty, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } NodeKind::Mutate(mutate) => { - walk_node(mutate.target, arena, module, index, ref_index, node_to_symbol, diagnostics); - walk_node(mutate.body, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(mutate.target, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); + walk_node(mutate.body, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } NodeKind::Borrow(borrow) => { - walk_node(borrow.target, arena, module, index, ref_index, node_to_symbol, diagnostics); - walk_node(borrow.body, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(borrow.target, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); + walk_node(borrow.body, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } NodeKind::Peek(peek) => { - walk_node(peek.target, arena, module, index, ref_index, node_to_symbol, diagnostics); - walk_node(peek.body, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(peek.target, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); + walk_node(peek.body, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } NodeKind::MemberAccess(member) => { - walk_node(member.object, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(member.object, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } NodeKind::TypeDecl(type_decl) => { if let Some(body) = type_decl.body { - walk_node(body, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(body, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } } NodeKind::TypeBody(body) => { for &method in &body.methods { - walk_node(method, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(method, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } } NodeKind::ServiceDecl(service) => { for &member in &service.members { - walk_node(member, arena, module, index, ref_index, node_to_symbol, diagnostics); + walk_node(member, arena, module, index, imports, ref_index, node_to_symbol, diagnostics); } } // Nós literais ou que não contém outros nós para percorrer (neste nível) @@ -252,7 +275,7 @@ mod tests { arena.roots.push(file_id); - let (symbols, index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner); + let (symbols, index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner, None); assert!(diagnostics.is_empty()); assert_eq!(symbols.symbols.len(), 2); @@ -304,7 +327,7 @@ mod tests { arena.roots.push(file_id); - let (_symbols, _index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner); + let (_symbols, _index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner, None); assert_eq!(diagnostics.len(), 1); assert_eq!(diagnostics[0].code, Some("E_RESOLVE_DUPLICATE_SYMBOL".to_string())); @@ -334,7 +357,7 @@ mod tests { arena.roots.push(file_id); - let (symbols, _index, _ref_index, node_to_symbol, _diagnostics) = build_def_index(&arena, 1, &interner); + let (symbols, _index, _ref_index, node_to_symbol, _diagnostics) = build_def_index(&arena, 1, &interner, None); let symbol_id = node_to_symbol.get(decl_id).expect("Node should be bound to a symbol"); let symbol = symbols.get(symbol_id); @@ -367,7 +390,7 @@ mod tests { arena.roots.push(file_id); - let (_symbols, _index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner); + let (_symbols, _index, _ref_index, _node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner, None); assert_eq!(diagnostics.len(), 1); assert_eq!(diagnostics[0].code, Some("E_RESOLVE_UNDEFINED_IDENTIFIER".to_string())); @@ -413,7 +436,7 @@ mod tests { arena.roots.push(file_id); - let (symbols, index, ref_index, node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner); + let (symbols, index, ref_index, node_to_symbol, diagnostics) = build_def_index(&arena, 1, &interner, None); assert!(diagnostics.is_empty(), "Diagnostics should be empty: {:?}", diagnostics); @@ -425,4 +448,55 @@ mod tests { let bound_id = node_to_symbol.get(ident_id).expect("ident should be bound to symbol"); assert_eq!(bound_id, target_sym_id); } + + #[test] + fn test_resolve_visibility_violation() { + let mut interner = NameInterner::new(); + + // Módulo 1: define função privada + let mut arena1 = AstArena::default(); + let target_name = interner.intern("private_func"); + let body1 = arena1.push(NodeKind::Block(BlockNodeArena { stmts: vec![], tail: None }), Span::new(0, 0, 0)); + let decl1 = arena1.push(NodeKind::FnDecl(FnDeclNodeArena { + vis: None, // Privado + name: target_name, + params: vec![], + ret: None, + else_fallback: None, + body: body1, + }), Span::new(0, 0, 10)); + let file1 = arena1.push(NodeKind::File(FileNodeArena { + imports: vec![], + decls: vec![decl1], + }), Span::new(0, 0, 100)); + arena1.roots.push(file1); + + let (symbols1, index1, _, _, _) = build_def_index(&arena1, 1, &interner, None); + + // Módulo 2: tenta usar função privada do Módulo 1 + let mut arena2 = AstArena::default(); + let ident_id = arena2.push(NodeKind::Ident(IdentNodeArena { name: target_name }), Span::new(0, 50, 62)); + let expr_stmt = arena2.push(NodeKind::ExprStmt(ExprStmtNodeArena { expr: ident_id }), Span::new(0, 50, 62)); + let body2 = arena2.push(NodeKind::Block(BlockNodeArena { stmts: vec![expr_stmt], tail: None }), Span::new(0, 40, 70)); + let caller = arena2.push(NodeKind::FnDecl(FnDeclNodeArena { + vis: Some("pub".to_string()), + name: interner.intern("caller"), + params: vec![], + ret: None, + else_fallback: None, + body: body2, + }), Span::new(0, 30, 80)); + let file2 = arena2.push(NodeKind::File(FileNodeArena { + imports: vec![], + decls: vec![caller], + }), Span::new(0, 0, 100)); + arena2.roots.push(file2); + + let (_symbols2, _index2, _ref_index2, _node_to_symbol2, diagnostics) = + build_def_index(&arena2, 2, &interner, Some((&symbols1, &index1))); + + assert_eq!(diagnostics.len(), 1); + assert_eq!(diagnostics[0].code, Some("E_RESOLVE_VISIBILITY".to_string())); + assert_eq!(diagnostics[0].span, Some(Span::new(0, 50, 62))); + } } diff --git a/test-cartridges/canonical/golden/program.pbc b/test-cartridges/canonical/golden/program.pbc index b06b91caaacaf4416129e782584fa51dc847ec21..666448e9c82fd9759bc1514ce6c916eaa9193fac 100644 GIT binary patch delta 25 ecmeB|?U&tadA