use crate::analysis::symbols::{SymbolArena, NodeToSymbol}; use crate::analysis::types::{TypeArena, TypeFacts, TypeKind}; use crate::common::diagnostics::{Diagnostic, DiagnosticBundle, Severity}; use crate::common::spans::Span; use crate::frontends::pbs::ast::*; use crate::frontends::pbs::symbols::*; use prometeu_analysis::{NameId, NameInterner, SymbolId, ModuleId, TypeId, NodeId}; use std::collections::HashMap; pub trait ModuleProvider { fn get_module_symbols(&self, from_path: &str) -> Option<&ModuleSymbols>; } pub struct Resolver<'a> { interner: &'a NameInterner, module_provider: &'a dyn ModuleProvider, current_module: &'a ModuleSymbols, scopes: Vec)>>, pub imported_symbols: ModuleSymbols, diagnostics: Vec, pub type_arena: TypeArena, pub type_facts: TypeFacts, pub symbol_arena: crate::analysis::symbols::SymbolArena, pub node_to_symbol: crate::analysis::symbols::NodeToSymbol, primitives: HashMap, } impl<'a> Resolver<'a> { pub fn new( current_module: &'a ModuleSymbols, module_provider: &'a dyn ModuleProvider, interner: &'a NameInterner, ) -> Self { Self { interner, module_provider, current_module, scopes: Vec::new(), imported_symbols: ModuleSymbols::new(), diagnostics: Vec::new(), type_arena: TypeArena::new(), type_facts: TypeFacts::new(), symbol_arena: SymbolArena::new(), node_to_symbol: NodeToSymbol::new(), primitives: HashMap::new(), } } pub fn bootstrap_types(&mut self, interner: &NameInterner) { let primitive_names = ["int", "bool", "float", "string", "bounded", "void"]; for name in primitive_names { let name_id = interner.get(name).expect("primitive name not interned"); let type_id = self.type_arena.intern_type(TypeKind::Primitive { name: name_id }); self.primitives.insert(name.to_string(), type_id); } } pub fn resolve(&mut self, arena: &AstArena, root: NodeId) -> Result<(), DiagnosticBundle> { let file = match arena.kind(root) { NodeKind::File(file) => file, _ => { return Err(DiagnosticBundle::error( "E_RESOLVE_INVALID_ROOT", "Expected File node as root".to_string(), arena.span(root), )) } }; // Step 0: Populate symbol_arena with top-level symbols for global lookup for (name, list) in &self.current_module.type_symbols.symbols { for sym in list { self.symbol_arena.insert(crate::analysis::symbols::Symbol { name: *name, kind: match sym.kind { SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function, SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service, SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct, SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract, SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType, SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local, }, exported: sym.visibility == Visibility::Pub, module: ModuleId(0), decl_span: sym.span.clone(), }); } } for (name, list) in &self.current_module.value_symbols.symbols { for sym in list { self.symbol_arena.insert(crate::analysis::symbols::Symbol { name: *name, kind: match sym.kind { SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function, SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service, SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct, SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract, SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType, SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local, }, exported: sym.visibility == Visibility::Pub, module: ModuleId(0), decl_span: sym.span.clone(), }); } } // Step 1: Process imports to populate imported_symbols for imp in &file.imports { if let NodeKind::Import(imp_node) = arena.kind(*imp) { self.resolve_import(arena, *imp, imp_node); } } // Add imported symbols to symbol_arena too for (name, list) in &self.imported_symbols.type_symbols.symbols { for sym in list { self.symbol_arena.insert(crate::analysis::symbols::Symbol { name: *name, kind: match sym.kind { SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function, SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service, SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct, SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract, SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType, SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local, }, exported: sym.visibility == Visibility::Pub, module: ModuleId(0), // Should be target module decl_span: sym.span.clone(), }); } } for (name, list) in &self.imported_symbols.value_symbols.symbols { for sym in list { self.symbol_arena.insert(crate::analysis::symbols::Symbol { name: *name, kind: match sym.kind { SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function, SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service, SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct, SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract, SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType, SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local, }, exported: sym.visibility == Visibility::Pub, module: ModuleId(0), // Should be target module decl_span: sym.span.clone(), }); } } // Step 2: Resolve all top-level declarations for decl in &file.decls { self.resolve_node(arena, *decl); } if !self.diagnostics.is_empty() { return Err(DiagnosticBundle { diagnostics: self.diagnostics.clone(), }); } Ok(()) } fn resolve_import(&mut self, arena: &AstArena, imp_id: NodeId, imp: &ImportNodeArena) { let provider = self.module_provider; if let Some(target_symbols) = provider.get_module_symbols(&imp.from) { let spec = match arena.kind(imp.spec) { NodeKind::ImportSpec(spec) => spec, _ => { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_RESOLVE_INVALID_IMPORT".to_string(), message: "Invalid import spec".to_string(), span: arena.span(imp_id), related: Vec::new(), }); return; } }; for name in &spec.path { // Try to find in Type namespace if let Some(sym) = target_symbols.type_symbols.get(*name) { if sym.visibility == Visibility::Pub { let is_service = sym.kind == SymbolKind::Service; let mut cloned = sym.clone(); cloned.origin = Some(imp.from.clone()); if let Err(_) = self.imported_symbols.type_symbols.insert(cloned) { self.error_duplicate_import(*name, arena.span(imp_id)); } // If a Service type is imported, also bring its public methods from the same module into value namespace if is_service { let base = self.interner.resolve(*name); let prefix = format!("{}.", base); for list in target_symbols.value_symbols.symbols.values() { for vs in list { // Apenas métodos do service importado: nomes exportados de métodos devem ser no formato "Service.method#sigN" let vname = self.interner.resolve(vs.name); if vname.starts_with(&prefix) { if vs.visibility == Visibility::Pub { let mut vsym = vs.clone(); vsym.origin = Some(imp.from.clone()); let _ = self.imported_symbols.value_symbols.insert(vsym); } } } } } } else { self.error_visibility(sym, arena.span(imp_id)); } } // Try to find in Value namespace else if let Some(sym) = target_symbols.value_symbols.get(*name) { if sym.visibility == Visibility::Pub { let mut sym = sym.clone(); sym.origin = Some(imp.from.clone()); if let Err(_) = self.imported_symbols.value_symbols.insert(sym) { self.error_duplicate_import(*name, arena.span(imp_id)); } } else { self.error_visibility(sym, arena.span(imp_id)); } } else { self.error_undefined(*name, arena.span(imp_id)); } } } else { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_RESOLVE_INVALID_IMPORT".to_string(), message: format!("Module not found: {}", imp.from), span: arena.span(imp_id), related: Vec::new(), }); } } fn resolve_node(&mut self, arena: &AstArena, node: NodeId) { match arena.kind(node) { NodeKind::FnDecl(n) => self.resolve_fn_decl(arena, node, n), NodeKind::ServiceDecl(n) => self.resolve_service_decl(arena, node, n), NodeKind::TypeDecl(n) => self.resolve_type_decl(arena, node, n), NodeKind::Block(n) => self.resolve_block(arena, n), NodeKind::LetStmt(n) => self.resolve_let_stmt(arena, node, n), NodeKind::ExprStmt(n) => self.resolve_node(arena, n.expr), NodeKind::ReturnStmt(n) => { if let Some(expr) = n.expr { self.resolve_node(arena, expr); } } NodeKind::Call(n) => { if let NodeKind::Ident(id) = arena.kind(n.callee) { if !self.is_builtin(id.name, Namespace::Value) { let (value_sym, sym_id) = match self.lookup_with_id(id.name, Namespace::Value) { Some((sym, id)) => (Some(sym), id), None => (None, None), }; if let Some(sid) = sym_id { self.node_to_symbol.bind_node(n.callee, sid); } if value_sym.is_none() && self.lookup_identifier(id.name, Namespace::Type).is_none() { // TODO: Resolver for v0 allows unresolved call targets (e.g. constructors via prelude) } } } else { self.resolve_node(arena, n.callee); } for arg in &n.args { self.resolve_node(arena, *arg); } } NodeKind::Unary(n) => { self.resolve_node(arena, n.expr); match n.op.as_str() { "-" => { let int_type = self.primitives.get("int").copied(); if let Some(expr_type) = self.type_facts.get_node_type(n.expr) { if Some(expr_type) == int_type { if let Some(id) = int_type { self.type_facts.set_node_type(node, id); } } else { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_TYPE_MISMATCH".to_string(), message: "Unary '-' operator expects 'int'".to_string(), span: arena.span(node), related: Vec::new(), }); } } } "!" => { let bool_type = self.primitives.get("bool").copied(); if let Some(expr_type) = self.type_facts.get_node_type(n.expr) { if Some(expr_type) == bool_type { if let Some(id) = bool_type { self.type_facts.set_node_type(node, id); } } else { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_TYPE_MISMATCH".to_string(), message: "Unary '!' operator expects 'bool'".to_string(), span: arena.span(node), related: Vec::new(), }); } } } _ => {} } } NodeKind::Binary(n) => { self.resolve_node(arena, n.left); self.resolve_node(arena, n.right); let left_type = self.type_facts.get_node_type(n.left); let right_type = self.type_facts.get_node_type(n.right); let int_type = self.primitives.get("int").copied(); let bool_type = self.primitives.get("bool").copied(); match n.op.as_str() { "+" | "-" | "*" | "/" => { if let (Some(l), Some(r)) = (left_type, right_type) { if Some(l) == int_type && Some(r) == int_type { if let Some(id) = int_type { self.type_facts.set_node_type(node, id); } } else { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_TYPE_MISMATCH".to_string(), message: format!("Binary '{}' operator expects 'int' operands", n.op), span: arena.span(node), related: Vec::new(), }); } } } "==" | "<" | ">" | "<=" | ">=" | "!=" => { if let (Some(l), Some(r)) = (left_type, right_type) { if l == r { if let Some(id) = bool_type { self.type_facts.set_node_type(node, id); } } else { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_TYPE_MISMATCH".to_string(), message: format!("Binary '{}' operator expects operands of the same type", n.op), span: arena.span(node), related: Vec::new(), }); } } } _ => {} } } NodeKind::TypeName(_) | NodeKind::TypeApp(_) => { self.resolve_type_ref(arena, node); } NodeKind::ConstructorDecl(n) => self.resolve_constructor_decl(arena, node, n), NodeKind::ConstantDecl(n) => self.resolve_node(arena, n.value), NodeKind::Alloc(n) => { self.resolve_type_ref(arena, n.ty); } NodeKind::Mutate(n) => { self.resolve_node(arena, n.target); self.enter_scope(); self.define_local(n.binding, arena.span(node), SymbolKind::Local); self.resolve_node(arena, n.body); self.exit_scope(); } NodeKind::Borrow(n) => { self.resolve_node(arena, n.target); self.enter_scope(); self.define_local(n.binding, arena.span(node), SymbolKind::Local); self.resolve_node(arena, n.body); self.exit_scope(); } NodeKind::Peek(n) => { self.resolve_node(arena, n.target); self.enter_scope(); self.define_local(n.binding, arena.span(node), SymbolKind::Local); self.resolve_node(arena, n.body); self.exit_scope(); } NodeKind::Cast(n) => { self.resolve_node(arena, n.expr); self.resolve_type_ref(arena, n.ty); } NodeKind::IfExpr(n) => { self.resolve_node(arena, n.cond); self.resolve_node(arena, n.then_block); if let Some(else_block) = n.else_block { self.resolve_node(arena, else_block); } } NodeKind::WhenExpr(n) => { for arm in &n.arms { if let NodeKind::WhenArm(arm_node) = arena.kind(*arm) { self.resolve_node(arena, arm_node.cond); self.resolve_node(arena, arm_node.body); } } } NodeKind::Ident(n) => { if let Some((_sym, Some(sym_id))) = self.lookup_with_id(n.name, Namespace::Value) { self.node_to_symbol.bind_node(node, sym_id); if let Some(ty) = self.type_facts.get_symbol_type(sym_id) { self.type_facts.set_node_type(node, ty); } } else { self.resolve_identifier(n.name, arena.span(node), Namespace::Value); } } NodeKind::IntLit(_) => { if let Some(id) = self.primitives.get("int") { self.type_facts.set_node_type(node, *id); } } NodeKind::FloatLit(_) => { if let Some(id) = self.primitives.get("float") { self.type_facts.set_node_type(node, *id); } } NodeKind::BoundedLit(_) => { if let Some(id) = self.primitives.get("bounded") { self.type_facts.set_node_type(node, *id); } } NodeKind::StringLit(_) => { if let Some(id) = self.primitives.get("string") { self.type_facts.set_node_type(node, *id); } } NodeKind::MemberAccess(n) => match arena.kind(n.object) { NodeKind::Ident(id) => { let ident_span = arena.span(n.object); if !self.is_builtin(id.name, Namespace::Type) { if self.lookup_identifier(id.name, Namespace::Value).is_none() { // If not found in Value namespace, try Type namespace (for Contracts/Services) if self.lookup_identifier(id.name, Namespace::Type).is_none() { // Still not found, use resolve_identifier to report error in Value namespace self.resolve_identifier(id.name, ident_span, Namespace::Value); } } } } _ => { self.resolve_node(arena, n.object); } }, _ => {} } } fn resolve_fn_decl(&mut self, arena: &AstArena, _id: NodeId, n: &FnDeclNodeArena) { self.enter_scope(); for param in &n.params { let ty_id = self.resolve_type_ref(arena, param.ty); let sym_id = self.define_local(param.name, param.span.clone(), SymbolKind::Local); if let (Some(tid), Some(sid)) = (ty_id, sym_id) { self.type_facts.set_symbol_type(sid, tid); // The Ident node of the parameter in the declaration could also be bound // But ParamNodeArena does not have a direct NodeId in the AST that represents the parameter's Ident. // `param.name` is a NameId. } } if let Some(ret) = n.ret { self.resolve_type_ref(arena, ret); } self.resolve_node(arena, n.body); self.exit_scope(); } fn resolve_service_decl(&mut self, arena: &AstArena, id: NodeId, n: &ServiceDeclNodeArena) { if let Some(ext) = n.extends { self.resolve_identifier(ext, arena.span(id), Namespace::Type); } for member in &n.members { match arena.kind(*member) { NodeKind::ServiceFnSig(sig) => { for param in &sig.params { self.resolve_type_ref(arena, param.ty); } self.resolve_type_ref(arena, sig.ret); } NodeKind::ServiceFnDecl(decl) => { // service com corpo: resolve tipos, define parâmetros como locais e resolve corpo for param in &decl.params { self.resolve_type_ref(arena, param.ty); } self.resolve_type_ref(arena, decl.ret); self.enter_scope(); for param in &decl.params { let sym_id = self.define_local(param.name, param.span.clone(), SymbolKind::Local); if let Some(sym_id) = sym_id { if let Some(ty_id) = self.resolve_type_ref(arena, param.ty) { self.type_facts.set_symbol_type(sym_id, ty_id); } } } self.resolve_node(arena, decl.body); self.exit_scope(); } _ => {} } } } fn resolve_type_decl(&mut self, arena: &AstArena, _id: NodeId, n: &TypeDeclNodeArena) { for param in &n.params { self.resolve_type_ref(arena, param.ty); } for constructor in &n.constructors { if let NodeKind::ConstructorDecl(ctor) = arena.kind(*constructor) { self.resolve_constructor_decl(arena, *constructor, ctor); } } self.enter_scope(); for ctor_id in &n.constructors { if let NodeKind::ConstructorDecl(ctor) = arena.kind(*ctor_id) { self.define_local(ctor.name, arena.span(*ctor_id), SymbolKind::Local); } } for constant in &n.constants { self.resolve_node(arena, *constant); } self.exit_scope(); if let Some(body_node) = n.body { if let NodeKind::TypeBody(body) = arena.kind(body_node) { for member in &body.members { self.resolve_type_ref(arena, member.ty); } for method in &body.methods { if let NodeKind::ServiceFnSig(sig) = arena.kind(*method) { for param in &sig.params { self.resolve_type_ref(arena, param.ty); } self.resolve_type_ref(arena, sig.ret); } } } } } fn resolve_constructor_decl( &mut self, arena: &AstArena, _id: NodeId, n: &ConstructorDeclNodeArena, ) { self.enter_scope(); for param in &n.params { self.resolve_type_ref(arena, param.ty); self.define_local(param.name, param.span.clone(), SymbolKind::Local); } for init in &n.initializers { self.resolve_node(arena, *init); } self.resolve_node(arena, n.body); self.exit_scope(); } fn resolve_block(&mut self, arena: &AstArena, n: &BlockNodeArena) { self.enter_scope(); for stmt in &n.stmts { self.resolve_node(arena, *stmt); } self.exit_scope(); } fn resolve_let_stmt(&mut self, arena: &AstArena, id: NodeId, n: &LetStmtNodeArena) { let ty_id = if let Some(ty) = n.ty { self.resolve_type_ref(arena, ty) } else { None }; self.resolve_node(arena, n.init); let sym_id = self.define_local(n.name, arena.span(id), SymbolKind::Local); if let Some(sid) = sym_id { self.node_to_symbol.bind_node(id, sid); if let Some(tid) = ty_id { self.type_facts.set_symbol_type(sid, tid); } } } fn resolve_type_ref(&mut self, arena: &AstArena, node: NodeId) -> Option { let type_id = self.lower_type_node(arena, node); if let Some(tid) = type_id { self.type_facts.set_node_type(node, tid); } type_id } fn lower_type_node(&mut self, arena: &AstArena, node: NodeId) -> Option { match arena.kind(node) { NodeKind::TypeName(n) => { let name = self.interner.resolve(n.name); if let Some(prim) = self.primitives.get(name) { return Some(*prim); } // Resolve struct/type symbol if let Some((_sym, sym_id)) = self.lookup_with_id(n.name, Namespace::Type) { // Try to find if this symbol is a type symbol that can be converted to TypeId // For now, we only have TypeKind::Struct if let Some(sid) = sym_id { let kind = TypeKind::Struct { sym: sid }; return Some(self.type_arena.intern_type(kind)); } else { // sym_id is None, but _sym exists. This means it's a built-in or something not in analysis arena yet. // But for Struct resolution we need a SymbolId. // If it's a built-in it should have been handled by self.primitives. } } // If not found in current or imports, maybe it's a symbol from another module not yet in SymbolArena? // Actually, lookup_with_id checks imported_symbols too. self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_TYPE_UNKNOWN_TYPE".to_string(), message: format!("Unknown type: {}", name), span: arena.span(node), related: Vec::new(), }); None } NodeKind::TypeApp(n) => { let base_name = self.interner.resolve(n.base); match base_name { "optional" => { if n.args.len() != 1 { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_TYPE_INVALID_ARGS".to_string(), message: "optional expects exactly 1 argument".to_string(), span: arena.span(node), related: Vec::new(), }); return None; } let inner = self.lower_type_node(arena, n.args[0])?; Some(self.type_arena.intern_type(TypeKind::Optional { inner })) } "result" => { if n.args.len() != 2 { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_TYPE_INVALID_ARGS".to_string(), message: "result expects exactly 2 arguments".to_string(), span: arena.span(node), related: Vec::new(), }); return None; } let ok = self.lower_type_node(arena, n.args[0])?; let err = self.lower_type_node(arena, n.args[1])?; Some(self.type_arena.intern_type(TypeKind::Result { ok, err })) } "array" => { if n.args.len() < 1 || n.args.len() > 2 { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_TYPE_INVALID_ARGS".to_string(), message: "array or array[N] expects 1 or 2 arguments".to_string(), span: arena.span(node), related: Vec::new(), }); return None; } let inner = self.lower_type_node(arena, n.args[0])?; let len = if n.args.len() == 2 { match arena.kind(n.args[1]) { NodeKind::IntLit(lit) => Some(lit.value as u32), _ => { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_TYPE_INVALID_ARGS".to_string(), message: "Array length must be an integer literal".to_string(), span: arena.span(n.args[1]), related: Vec::new(), }); None } } } else { None }; Some(self.type_arena.intern_type(TypeKind::Array { inner, len })) } _ => { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_TYPE_UNKNOWN_TYPE".to_string(), message: format!("Unknown generic type: {}", base_name), span: arena.span(node), related: Vec::new(), }); None } } } _ => { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_TYPE_NOT_A_TYPE".to_string(), message: "Expected a type node".to_string(), span: arena.span(node), related: Vec::new(), }); None } } } fn resolve_identifier(&mut self, name: NameId, span: Span, namespace: Namespace) -> Option { if self.is_builtin(name, namespace) { return None; } if let Some(sym) = self.lookup_identifier(name, namespace) { Some(sym) } else { if namespace == Namespace::Type { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_TYPE_UNKNOWN_TYPE".to_string(), message: format!("Unknown type: {}", self.interner.resolve(name)), span, related: Vec::new(), }); } else { self.error_undefined(name, span); } None } } fn is_builtin(&self, name: NameId, namespace: Namespace) -> bool { let name = self.interner.resolve(name); match namespace { Namespace::Type => match name { "int" | "float" | "string" | "bool" | "void" | "optional" | "result" | "bounded" | "Color" | "ButtonState" | "Pad" | "Touch" => true, _ => false, }, Namespace::Value => match name { "none" | "some" | "ok" | "err" | "true" | "false" => true, _ => false, }, } } fn lookup_identifier(&self, name: NameId, namespace: Namespace) -> Option { self.lookup_with_id(name, namespace).map(|(sym, _)| sym) } fn lookup_with_id(&self, name: NameId, namespace: Namespace) -> Option<(Symbol, Option)> { // 1. local bindings if namespace == Namespace::Value { for scope in self.scopes.iter().rev() { if let Some(pair) = scope.get(&name) { return Some(pair.clone()); } } } let table = if namespace == Namespace::Type { &self.current_module.type_symbols } else { &self.current_module.value_symbols }; // 2 & 3. file-private and module symbols if let Some(sym) = table.get(name) { // Se encontrarmos no módulo atual, tentamos achar o SymbolId correspondente na symbol_arena let sym_id = self.symbol_arena.symbols.iter().enumerate().find(|(_, s)| { s.name == sym.name && s.kind == match sym.kind { SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function, SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service, SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct, SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract, SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType, SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local, } }).map(|(i, _)| SymbolId(i as u32)); return Some((sym.clone(), sym_id)); } // 4. imported symbols let imp_table = if namespace == Namespace::Type { &self.imported_symbols.type_symbols } else { &self.imported_symbols.value_symbols }; if let Some(sym) = imp_table.get(name) { let sym_id = self.symbol_arena.symbols.iter().enumerate().find(|(_, s)| { s.name == sym.name && s.kind == match sym.kind { SymbolKind::Function => crate::analysis::symbols::SymbolKind::Function, SymbolKind::Service => crate::analysis::symbols::SymbolKind::Service, SymbolKind::Struct => crate::analysis::symbols::SymbolKind::Struct, SymbolKind::Contract => crate::analysis::symbols::SymbolKind::Contract, SymbolKind::ErrorType => crate::analysis::symbols::SymbolKind::ErrorType, SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local, } }).map(|(i, _)| SymbolId(i as u32)); return Some((sym.clone(), sym_id)); } // 5. Fallback for constructor calls: check Type namespace if looking for a Value if namespace == Namespace::Value { if let Some(sym) = self.current_module.type_symbols.get(name) { if sym.kind == SymbolKind::Struct { return Some((sym.clone(), None)); } } if let Some(sym) = self.imported_symbols.type_symbols.get(name) { if sym.kind == SymbolKind::Struct { return Some((sym.clone(), None)); } } } None } fn define_local(&mut self, name: NameId, span: Span, kind: SymbolKind) -> Option { let scope = self.scopes.last_mut().expect("No scope to define local"); // Check for collision in Type namespace at top-level? // Actually, the spec says "A name may not exist in both namespaces". // If we want to be strict, we check current module's type symbols too. if let Some(existing) = self.current_module.type_symbols.get(name) { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_RESOLVE_NAMESPACE_COLLISION".to_string(), message: format!( "Local variable '{}' collides with a type name", self.interner.resolve(name) ), span, related: vec![("type defined here".to_string(), existing.span.clone())], }); return None; } if let Some((prev_sym, _)) = scope.get(&name) { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_RESOLVE_DUPLICATE_SYMBOL".to_string(), message: format!("Duplicate local variable '{}'", self.interner.resolve(name)), span, related: vec![("previous definition here".to_string(), prev_sym.span.clone())], }); None } else { let symbol = Symbol { name, kind, namespace: Namespace::Value, visibility: Visibility::FilePrivate, ty: None, // Will be set by TypeChecker is_host: false, span: span.clone(), origin: None, }; // Criar símbolo na symbol_arena (análise global) let sym_id = self.symbol_arena.insert(crate::analysis::symbols::Symbol { name, kind: match kind { SymbolKind::Local => crate::analysis::symbols::SymbolKind::Local, _ => crate::analysis::symbols::SymbolKind::Value, }, exported: false, module: ModuleId(0), // TODO: set actual module id when available decl_span: span, }); scope.insert(name, (symbol, Some(sym_id))); Some(sym_id) } } fn enter_scope(&mut self) { self.scopes.push(HashMap::new()); } fn exit_scope(&mut self) { self.scopes.pop(); } fn error_undefined(&mut self, name: NameId, span: Span) { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_RESOLVE_UNDEFINED".to_string(), message: format!("Undefined identifier: {}", self.interner.resolve(name)), span, related: Vec::new(), }); } fn error_duplicate_import(&mut self, name: NameId, span: Span) { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_RESOLVE_DUPLICATE_SYMBOL".to_string(), message: format!("Duplicate import: {}", self.interner.resolve(name)), span, related: Vec::new(), }); } fn error_visibility(&mut self, sym: &Symbol, span: Span) { self.diagnostics.push(Diagnostic { severity: Severity::Error, code: "E_RESOLVE_VISIBILITY".to_string(), message: format!( "DebugSymbol '{}' is not visible here", self.interner.resolve(sym.name) ), span, related: vec![("symbol defined here".to_string(), sym.span.clone())], }); } } #[cfg(test)] mod tests { use super::*; use crate::common::files::FileManager; use crate::common::spans::Span; use crate::frontends::pbs::*; use std::path::PathBuf; fn setup_test(source: &str) -> (ParsedAst, usize, NameInterner) { let mut fm = FileManager::new(); let file_id = fm.add(PathBuf::from("test.pbs"), source.to_string()); let mut parser = parser::Parser::new(source, crate::common::spans::FileId(file_id as u32), fm.interner_mut()); let parsed = parser.parse_file().expect("Parsing failed"); (parsed, file_id, fm.interner().clone()) } #[test] fn test_duplicate_symbols() { let source = " declare struct Foo() declare struct Foo() "; let (parsed, _, interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); let result = collector.collect(&parsed.arena, parsed.root); assert!(result.is_err()); let bundle = result.unwrap_err(); assert!(bundle.diagnostics.iter().any(|d| d.code == "E_RESOLVE_DUPLICATE_SYMBOL")); } #[test] fn test_namespace_collision() { let source = " declare struct Foo() fn Foo() {} "; let (parsed, _, interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); let result = collector.collect(&parsed.arena, parsed.root); assert!(result.is_err()); let bundle = result.unwrap_err(); assert!(bundle.diagnostics.iter().any(|d| d.code == "E_RESOLVE_NAMESPACE_COLLISION")); } #[test] fn test_undefined_identifier() { let source = " fn main() { let x = y; } "; let (parsed, _, interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector .collect(&parsed.arena, parsed.root) .expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct EmptyProvider; impl ModuleProvider for EmptyProvider { fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None } } let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); let result = resolver.resolve(&parsed.arena, parsed.root); assert!(result.is_err()); let bundle = result.unwrap_err(); assert!(bundle.diagnostics.iter().any(|d| d.code == "E_RESOLVE_UNDEFINED")); } #[test] fn test_local_variable_resolution() { let source = " fn main() { let x = 10; let y = x; } "; let (parsed, _, interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector .collect(&parsed.arena, parsed.root) .expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct EmptyProvider; impl ModuleProvider for EmptyProvider { fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None } } let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); let result = resolver.resolve(&parsed.arena, parsed.root); assert!(result.is_ok()); } #[test] fn test_visibility_error() { let source = " import PrivateType from \"./other.pbs\" fn main() {} "; let (parsed, _, mut interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector .collect(&parsed.arena, parsed.root) .expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct MockProvider { other: ModuleSymbols, } impl ModuleProvider for MockProvider { fn get_module_symbols(&self, path: &str) -> Option<&ModuleSymbols> { if path == "./other.pbs" { Some(&self.other) } else { None } } } let mut other_ts = SymbolTable::new(); other_ts.insert(Symbol { name: interner.intern("PrivateType"), kind: SymbolKind::Struct, namespace: Namespace::Type, visibility: Visibility::FilePrivate, ty: None, is_host: false, span: Span::new(crate::common::spans::FileId(1), 0, 0), origin: None, }).unwrap(); let mock_provider = MockProvider { other: ModuleSymbols { type_symbols: other_ts, value_symbols: SymbolTable::new() }, }; let mut resolver = Resolver::new(&ms, &mock_provider, &interner); let result = resolver.resolve(&parsed.arena, parsed.root); assert!(result.is_err()); let bundle = result.unwrap_err(); assert!(bundle.diagnostics.iter().any(|d| d.code == "E_RESOLVE_VISIBILITY")); } #[test] fn test_import_resolution() { let source = " import PubType from \"./other.pbs\" fn main() { let x: PubType = 10; } "; let (parsed, _, mut interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector .collect(&parsed.arena, parsed.root) .expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct MockProvider { other: ModuleSymbols, } impl ModuleProvider for MockProvider { fn get_module_symbols(&self, path: &str) -> Option<&ModuleSymbols> { if path == "./other.pbs" { Some(&self.other) } else { None } } } let mut other_ts = SymbolTable::new(); other_ts.insert(Symbol { name: interner.intern("PubType"), kind: SymbolKind::Struct, namespace: Namespace::Type, visibility: Visibility::Pub, ty: None, is_host: false, span: Span::new(crate::common::spans::FileId(1), 0, 0), origin: None, }).unwrap(); let mock_provider = MockProvider { other: ModuleSymbols { type_symbols: other_ts, value_symbols: SymbolTable::new() }, }; let mut resolver = Resolver::new(&ms, &mock_provider, &interner); let result = resolver.resolve(&parsed.arena, parsed.root); assert!(result.is_ok()); } #[test] fn test_invalid_import_module_not_found() { let source = " import NonExistent from \"./missing.pbs\" fn main() {} "; let (parsed, _, interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector .collect(&parsed.arena, parsed.root) .expect("Collection failed"); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct EmptyProvider; impl ModuleProvider for EmptyProvider { fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None } } let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); let result = resolver.resolve(&parsed.arena, parsed.root); assert!(result.is_err()); let bundle = result.unwrap_err(); assert!(bundle.diagnostics.iter().any(|d| d.code == "E_RESOLVE_INVALID_IMPORT")); } #[test] fn test_typing_literals_and_binary() { let source = " fn main() { let a = 1 + 2; let b = 1 == 2; let c = -1; } "; let (parsed, _, mut interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap(); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct EmptyProvider; impl ModuleProvider for EmptyProvider { fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None } } // Ensure primitives are interned for bootstrap for p in ["int", "bool", "float", "string", "bounded", "void"] { interner.intern(p); } let interner_for_bootstrap = interner.clone(); let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); resolver.bootstrap_types(&interner); resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed"); // Verify types in TypeFacts using format_type for easier assertion use crate::analysis::types::format_type; let mut found_a = false; let mut found_b = false; let mut found_c = false; for i in 0..parsed.arena.nodes.len() { let id = NodeId(i as u32); let kind = parsed.arena.kind(id); match kind { NodeKind::Binary(n) if n.op == "+" => { let ty = resolver.type_facts.get_node_type(id).expect("Binary + should have type"); assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "int"); found_a = true; } NodeKind::Binary(n) if n.op == "==" => { let ty = resolver.type_facts.get_node_type(id).expect("Binary == should have type"); assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "bool"); found_b = true; } NodeKind::Unary(n) if n.op == "-" => { let ty = resolver.type_facts.get_node_type(id).expect("Unary - should have type"); assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "int"); found_c = true; } _ => {} } } assert!(found_a, "Binary + node not found"); assert!(found_b, "Binary == node not found"); assert!(found_c, "Unary - node not found"); } #[test] fn test_hover_let_annotated_type() { let source = " fn main() { let x: int = 10; let y = x; } "; let (parsed, _, mut interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap(); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct EmptyProvider; impl ModuleProvider for EmptyProvider { fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None } } for p in ["int", "bool", "float", "string", "bounded", "void"] { interner.intern(p); } let interner_for_bootstrap = interner.clone(); let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); resolver.bootstrap_types(&interner); resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed"); use crate::analysis::types::format_type; let mut found_y_ref = false; for i in 0..parsed.arena.nodes.len() { let id = NodeId(i as u32); let kind = parsed.arena.kind(id); if let NodeKind::LetStmt(n) = kind { if interner.resolve(n.name) == "y" { let init_id = n.init; if let NodeKind::Ident(ident) = parsed.arena.kind(init_id) { if interner.resolve(ident.name) == "x" { let ty = resolver.type_facts.get_node_type(init_id).expect("Ident x should have type"); assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "int"); found_y_ref = true; } } } } } assert!(found_y_ref, "Reference to x in let y = x not found or not typed"); } #[test] fn test_hover_param_type() { let source = " fn foo(a: int) { let b = a; } "; let (parsed, _, mut interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap(); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct EmptyProvider; impl ModuleProvider for EmptyProvider { fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None } } for p in ["int", "bool", "float", "string", "bounded", "void"] { interner.intern(p); } let interner_for_bootstrap = interner.clone(); let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); resolver.bootstrap_types(&interner); resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed"); use crate::analysis::types::format_type; let mut found_a_ref = false; for i in 0..parsed.arena.nodes.len() { let id = NodeId(i as u32); let kind = parsed.arena.kind(id); if let NodeKind::LetStmt(n) = kind { if interner.resolve(n.name) == "b" { let init_id = n.init; if let NodeKind::Ident(ident) = parsed.arena.kind(init_id) { if interner.resolve(ident.name) == "a" { let ty = resolver.type_facts.get_node_type(init_id).expect("Ident a should have type"); assert_eq!(format_type(ty, &resolver.type_arena, &interner_for_bootstrap, None), "int"); found_a_ref = true; } } } } } assert!(found_a_ref, "Reference to a in let b = a not found or not typed"); } #[test] fn test_lower_type_node_complex() { let source = " declare struct MyType() fn main() { let x: optional = 1; let y: result = 2; let z: array[10] = 3; let w: MyType = 4; } "; let (parsed, _, mut interner) = setup_test(source); let mut collector = SymbolCollector::new(&interner); let (ts, vs) = collector.collect(&parsed.arena, parsed.root).unwrap(); let ms = ModuleSymbols { type_symbols: ts, value_symbols: vs }; struct EmptyProvider; impl ModuleProvider for EmptyProvider { fn get_module_symbols(&self, _path: &str) -> Option<&ModuleSymbols> { None } } for p in ["int", "bool", "float", "string", "bounded", "void"] { interner.intern(p); } let interner_for_bootstrap = interner.clone(); let mut resolver = Resolver::new(&ms, &EmptyProvider, &interner); resolver.bootstrap_types(&interner); resolver.resolve(&parsed.arena, parsed.root).expect("Resolution failed"); use crate::analysis::types::format_type; let mut found_x = false; let mut found_y = false; let mut found_z = false; let mut found_w = false; for i in 0..parsed.arena.nodes.len() { let id = NodeId(i as u32); let kind = parsed.arena.kind(id); if let NodeKind::LetStmt(n) = kind { let name = interner.resolve(n.name); let sym_id = resolver.node_to_symbol.get(id).expect(&format!("Node {:?} should be bound to a symbol", kind)); let ty_id = resolver.type_facts.get_symbol_type(sym_id).expect("Symbol should have type"); match name { "x" => { assert_eq!(format_type(ty_id, &resolver.type_arena, &interner_for_bootstrap, None), "optional"); found_x = true; } "y" => { assert_eq!(format_type(ty_id, &resolver.type_arena, &interner_for_bootstrap, None), "result"); found_y = true; } "z" => { assert_eq!(format_type(ty_id, &resolver.type_arena, &interner_for_bootstrap, None), "array[10]"); found_z = true; } "w" => { // MyType is a struct. Since we don't pass symbols to format_type in this test, it should be struct#ID let formatted = format_type(ty_id, &resolver.type_arena, &interner_for_bootstrap, None); assert!(formatted.starts_with("struct#"), "Formatted type should be struct#ID, got {}", formatted); found_w = true; } _ => {} } } } assert!(found_x); assert!(found_y); assert!(found_z); assert!(found_w); } }